Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Sources/Alloy/Core/Extensions/Alloy/MTLContext+Device.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ public extension MTLContext {
func texture(width: Int,
height: Int,
pixelFormat: MTLPixelFormat,
usage: MTLTextureUsage = [.shaderRead]) throws -> MTLTexture {
usage: MTLTextureUsage = [.shaderRead],
storageMode: MTLStorageMode = .shared) throws -> MTLTexture {
return try self.device
.texture(width: width,
height: height,
pixelFormat: pixelFormat,
usage: usage)
usage: usage,
storageMode: storageMode)
}

func depthState(depthCompareFunction: MTLCompareFunction,
Expand Down
25 changes: 25 additions & 0 deletions Sources/Alloy/Core/Extensions/Foundation/Array+Data.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Foundation

public extension Array {

init(from data: Data) {
guard data.count % MemoryLayout<Element>.stride == 0 else {
fatalError("Cannot construct array from data as the number of bytes is not divisible by element size")
}

let numberOfElements = data.count / MemoryLayout<Element>.stride

self.init(unsafeUninitializedCapacity: numberOfElements) { buffer, initializedCount in
initializedCount = numberOfElements
_ = data.withUnsafeBytes { (p: UnsafeRawBufferPointer) in
memcpy(buffer.baseAddress, p.baseAddress, data.count)
}
}
}

var data: Data {
return self.withUnsafeBufferPointer { pointer in
return Data(buffer: pointer)
}
}
}
12 changes: 12 additions & 0 deletions Sources/Alloy/Core/Extensions/Foundation/NSColor+MTLColor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#if os(macOS)
import Foundation

public extension NSColor {
var metalColor: MTLClearColor {
return .init(red: self.redComponent,
green: self.greenComponent,
blue: self.blueComponent,
alpha: self.alphaComponent)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public extension MTLCommandBuffer {

// TODO: Support multisample rendering
func render(to texture: MTLTexture,
arrayLength: Int = 1,
loadAction: MTLRenderPassColorAttachmentDescriptor.LoadAction = .clear(.clear),
storeAction: MTLStoreAction = .store,
_ commands: (MTLRenderCommandEncoder) -> Void) {
Expand All @@ -51,6 +52,83 @@ public extension MTLCommandBuffer {
renderPassDescriptor.colorAttachments[0].setLoadAction(loadAction)
renderPassDescriptor.colorAttachments[0].storeAction = storeAction

if arrayLength > 1 {
renderPassDescriptor.renderTargetArrayLength = arrayLength
}

self.render(descriptor: renderPassDescriptor, commands)
}

@available(visionOS 1.0, iOS 13.0, macOS 10.15.4, *)
func render(to texture: MTLTexture,
arrayLength: Int = 1,
with rasterizationMap: MTLRasterizationRateMap,
loadAction: MTLRenderPassColorAttachmentDescriptor.LoadAction = .clear(.clear),
storeAction: MTLStoreAction = .store,
_ commands: (MTLRenderCommandEncoder) -> Void) {
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].setLoadAction(loadAction)
renderPassDescriptor.colorAttachments[0].storeAction = storeAction

renderPassDescriptor.rasterizationRateMap = rasterizationMap

if arrayLength > 1 {
renderPassDescriptor.renderTargetArrayLength = arrayLength
}

self.render(descriptor: renderPassDescriptor, commands)
}

func render(to texture: MTLTexture,
arrayLength: Int = 1,
loadAction: MTLRenderPassColorAttachmentDescriptor.LoadAction = .clear(.clear),
storeAction: MTLStoreAction = .store,
depthAttachment: MTLTexture,
depthLoadAction: MTLRenderPassDepthAttachmentDescriptor.LoadAction = .clear(1.0),
depthStoreAction: MTLStoreAction = .dontCare,
_ commands: (MTLRenderCommandEncoder) -> Void) {
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].setLoadAction(loadAction)
renderPassDescriptor.colorAttachments[0].storeAction = storeAction

renderPassDescriptor.depthAttachment.texture = depthAttachment
renderPassDescriptor.depthAttachment.setLoadAction(depthLoadAction)
renderPassDescriptor.depthAttachment.storeAction = depthStoreAction

if arrayLength > 1 {
renderPassDescriptor.renderTargetArrayLength = arrayLength
}

self.render(descriptor: renderPassDescriptor, commands)
}

@available(visionOS 1.0, iOS 13, macOS 10.15.4, *)
func render(to texture: MTLTexture,
arrayLength: Int = 1,
with rasterizationMap: MTLRasterizationRateMap,
loadAction: MTLRenderPassColorAttachmentDescriptor.LoadAction = .clear(.clear),
storeAction: MTLStoreAction = .store,
depthAttachment: MTLTexture,
depthLoadAction: MTLRenderPassDepthAttachmentDescriptor.LoadAction = .clear(1.0),
depthStoreAction: MTLStoreAction = .dontCare,
_ commands: (MTLRenderCommandEncoder) -> Void) {
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].setLoadAction(loadAction)
renderPassDescriptor.colorAttachments[0].storeAction = storeAction

renderPassDescriptor.depthAttachment.texture = depthAttachment
renderPassDescriptor.depthAttachment.setLoadAction(depthLoadAction)
renderPassDescriptor.depthAttachment.storeAction = depthStoreAction

renderPassDescriptor.rasterizationRateMap = rasterizationMap

if arrayLength > 1 {
renderPassDescriptor.renderTargetArrayLength = arrayLength
}

self.render(descriptor: renderPassDescriptor, commands)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import Metal

public extension MTLCommandQueue {

@available(iOS 13.0, macOS 12.0, *)
func schedule<T>(_ bufferEncodings: (MTLCommandBuffer) throws -> T) async throws -> T {
guard let commandBuffer = self.makeCommandBuffer()
else { throw MetalError.MTLCommandQueueError.commandBufferCreationFailed }

let retVal = try bufferEncodings(commandBuffer)

commandBuffer.commit()
commandBuffer.waitUntilCompleted()

return retVal
}

func scheduleAndWait<T>(_ bufferEncodings: (MTLCommandBuffer) throws -> T) throws -> T {
guard let commandBuffer = self.makeCommandBuffer()
else { throw MetalError.MTLCommandQueueError.commandBufferCreationFailed }
Expand Down
2 changes: 2 additions & 0 deletions Sources/Alloy/Core/Extensions/Metal/MTLDevice+Features.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public extension MTLDevice {
return self.supportsFeatureSet(.iOS_GPUFamily4_v1)
#elseif os(macOS)
return self.supportsFeatureSet(.macOS_GPUFamily1_v3)
#elseif os(visionOS)
return self.supportsFamily(.common2)
#endif

case let .readWriteTextures(pixelFormat):
Expand Down
20 changes: 10 additions & 10 deletions Sources/Alloy/Core/Extensions/Metal/MTLPixelFormat+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public extension MTLPixelFormat {
}

var isOrdinary8Bit: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .a8Unorm, .r8Unorm, .r8Unorm_srgb, .r8Snorm, .r8Uint, .r8Sint:
return true
Expand All @@ -53,7 +53,7 @@ public extension MTLPixelFormat {
}

var isOrdinary16Bit: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .r16Unorm, .r16Snorm, .r16Uint, .r16Sint, .r16Float,
.rg8Unorm, .rg8Unorm_srgb, .rg8Snorm, .rg8Uint, .rg8Sint:
Expand All @@ -71,7 +71,7 @@ public extension MTLPixelFormat {
}

var isPacked16Bit: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .b5g6r5Unorm, .a1bgr5Unorm, .abgr4Unorm, .bgr5A1Unorm:
return true
Expand All @@ -93,7 +93,7 @@ public extension MTLPixelFormat {
}

var isPacked32Bit: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .rgb10a2Unorm, .rgb10a2Uint, .rg11b10Float, .rgb9e5Float,
.bgr10a2Unorm, .bgr10_xr, .bgr10_xr_srgb:
Expand All @@ -111,7 +111,7 @@ public extension MTLPixelFormat {
}

var isNormal64Bit: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .rg32Uint, .rg32Sint, .rg32Float, .rgba16Unorm,
.rgba16Snorm, .rgba16Uint, .rgba16Sint, .rgba16Float,
Expand All @@ -138,7 +138,7 @@ public extension MTLPixelFormat {
}

var isSRGB: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .bgra8Unorm_srgb, .bgr10_xr_srgb, .bgra10_xr_srgb,
.r8Unorm_srgb, .rg8Unorm_srgb,
Expand All @@ -161,7 +161,7 @@ public extension MTLPixelFormat {
}

var isExtendedRange: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
switch self {
case .bgr10_xr, .bgr10_xr_srgb,
.bgra10_xr, .bgra10_xr_srgb:
Expand All @@ -174,7 +174,7 @@ public extension MTLPixelFormat {
}

var isCompressed: Bool {
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
return self.isPVRTC
|| self.isEAC
|| self.isETC
Expand All @@ -187,7 +187,7 @@ public extension MTLPixelFormat {
#endif
}

#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)

var isPVRTC: Bool {
switch self {
Expand Down Expand Up @@ -309,7 +309,7 @@ public extension MTLPixelFormat {
case .a8Unorm:
return false
case .rgb9e5Float:
#if os(iOS) && !targetEnvironment(macCatalyst)
#if (os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)
return true
#elseif os(macOS) || (os(iOS) && targetEnvironment(macCatalyst))
return false
Expand Down
12 changes: 12 additions & 0 deletions Sources/Alloy/Core/Extensions/Metal/MTLRegion+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ public extension MTLRegion {
self.size.width * self.size.height
}

var width: Int {
self.size.width
}

var height: Int {
self.size.height
}

var depth: Int {
self.size.depth
}

func clamped(to region: MTLRegion) -> MTLRegion? {
let ox = max(self.origin.x, region.origin.x)
let oy = max(self.origin.y, region.origin.y)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,26 @@ public extension MTLRenderCommandEncoder {
indexBufferOffset: offset,
instanceCount: instanceCount)
}

func drawIndexedPrimitives(type: MTLPrimitiveType,
indexBuffer: MTLIndexBuffer,
indirectBuffer: MTLBuffer) {
self.drawIndexedPrimitives(type: type,
indexType: indexBuffer.type,
indexBuffer: indexBuffer.buffer,
indexBufferOffset: 0,
indirectBuffer: indirectBuffer,
indirectBufferOffset: 0)
}

@available(macOS 10.15.4, visionOS 1, iOS 17, *)
func setDefaultAmplificationMappings(count: Int) {
var viewMappings = (0..<count).map {
MTLVertexAmplificationViewMapping(viewportArrayIndexOffset: UInt32($0),
renderTargetArrayIndexOffset: UInt32($0))
}
self.setVertexAmplificationCount(count,
viewMappings: &viewMappings)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Metal

public extension MTLRenderPassDepthAttachmentDescriptor {
/// slightly more useful type for MTLLoadAction
enum LoadAction {
case dontCare
case load
case clear(Double)
}

func setLoadAction(_ action: LoadAction) {
switch action {
case .dontCare:
self.loadAction = .dontCare
case .load:
self.loadAction = .load
case .clear(let value):
self.loadAction = .clear
self.clearDepth = value
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Metal

public extension MTLRenderPassDescriptor {
static func to(texture: MTLTexture,
loadAction: MTLRenderPassColorAttachmentDescriptor.LoadAction = .clear(.clear),
storeAction: MTLStoreAction = .store) -> MTLRenderPassDescriptor {
let newDescriptor = MTLRenderPassDescriptor()
newDescriptor.colorAttachments[0].texture = texture
newDescriptor.colorAttachments[0].setLoadAction(loadAction)
newDescriptor.colorAttachments[0].storeAction = storeAction

return newDescriptor
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public extension MTLRenderPipelineColorAttachmentDescriptor {
case none
/// Regular alpha blending.
case alpha
/// Front to back rendering using alpha blending
case reverseAlpha
/// Pre-multiplied alpha blending. (This is usually the default)
case premultipliedAlpha
/// Additive blending. (Similar to PhotoShop's linear dodge mode)
Expand Down Expand Up @@ -47,6 +49,11 @@ public extension MTLRenderPipelineColorAttachmentDescriptor {
self.destinationRGBBlendFactor = .oneMinusSourceAlpha
self.sourceAlphaBlendFactor = .sourceAlpha
self.destinationAlphaBlendFactor = .oneMinusSourceAlpha
case .reverseAlpha:
self.sourceRGBBlendFactor = .oneMinusDestinationAlpha
self.sourceAlphaBlendFactor = .oneMinusDestinationAlpha
self.destinationRGBBlendFactor = .one
self.destinationAlphaBlendFactor = .one
case .premultipliedAlpha:
self.sourceRGBBlendFactor = .one
self.destinationRGBBlendFactor = .oneMinusSourceAlpha
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Metal
public extension MTLResource {

var isAccessibleOnCPU: Bool {
#if (os(iOS) && !targetEnvironment(macCatalyst)) || os(tvOS)
#if ((os(iOS) || os(visionOS)) && !targetEnvironment(macCatalyst)) || os(tvOS)
return self.storageMode == .shared
#elseif os(macOS) || (os(iOS) && targetEnvironment(macCatalyst))
return self.storageMode == .managed || self.storageMode == .shared
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MetalPerformanceShaders
import Accelerate

public extension MTLTexture {
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
typealias XImage = UIImage
#elseif os(macOS)
typealias XImage = NSImage
Expand Down Expand Up @@ -142,7 +142,7 @@ public extension MTLTexture {

func image(colorSpace: CGColorSpace? = nil) throws -> XImage {
let cgImage = try self.cgImage(colorSpace: colorSpace)
#if os(iOS)
#if os(iOS) || os(visionOS)
return UIImage(cgImage: cgImage)
#elseif os(macOS)
return NSImage(cgImage: cgImage,
Expand Down
Loading