Skip to content
Draft
3 changes: 3 additions & 0 deletions LocalPreferences.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
# which storage mode unspecified allocations should default to.
# possible values: "private", "shared", "managed"
#default_storage = "private"

# when false, always use metal 3 even on a metal 4 platform
#force_metal3 = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about a api_version preference, determined automatically if unset, but overridable by setting "3" or "4"?

I'd also use string values; literals have given me issues in the past (if there's a typo in the user-provided TOML, stuff breaks).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted about the string values.

Metal 3 can be used alongside Metal 4, so maybe we should call it default_api_version to make it clear that all it's doing is selecting the default API?

I think maybe that this preference should only affect whether global_queue returns an MTLCommandQueue or an MTL4CommandQueue, and everything else that takes in a queue should dispatch on the correct implementation based on which type was provided?

9 changes: 8 additions & 1 deletion lib/mtl/MTL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,26 @@ include("size.jl")
include("device.jl")
include("resource.jl")
include("storage_type.jl")
include("compile-opts.jl")
include("compile_opts.jl")
include("library.jl")
include("function.jl")
include("events.jl")
include("fences.jl")
include("heap.jl")
include("buffer.jl")
include("command_queue.jl")
include("command_queue4.jl")
include("command_buf.jl")
include("command_buf4.jl")
include("compute_pipeline.jl")
include("compute_pipeline4.jl")
include("command_enc.jl")
include("command_enc4.jl")
include("command_alloc4.jl")
include("arg_table.jl")
include("command_enc/blit.jl")
include("command_enc/compute.jl")
include("command_enc/compute4.jl")
include("binary_archive.jl")
include("capture.jl")
include("texture.jl")
Expand Down
36 changes: 36 additions & 0 deletions lib/mtl/arg_table.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#
# command buffer
#

export MTL4ArgumentTable, MTL4ArgumentTableDescriptor

function MTL4ArgumentTableDescriptor()
desc = @objc [MTL4ArgumentTableDescriptor alloc]::id{MTL4ArgumentTableDescriptor}
obj = MTL4ArgumentTableDescriptor(desc)
return obj
end

function MTL4ArgumentTable(device::MTLDevice, desc::MTL4ArgumentTableDescriptor)
err = Ref{id{NSError}}(nil)
handle = @objc [device::id{MTLDevice} newArgumentTableWithDescriptor:desc::id{MTL4ArgumentTableDescriptor}
error:err::Ptr{id{NSError}}]::id{MTL4ArgumentTable}
err[] == nil || throw(NSError(err[]))
obj = MTL4ArgumentTable(handle)
# finalizer(release, obj)
return obj
end

# Buffer Arguments
function set_address!(cce::MTL4ArgumentTable, address, bindingIndex)
@objc [cce::id{MTL4ArgumentTable} setAddress:address::NSUInteger
atIndex:(bindingIndex-1)::NSUInteger]::Nothing
end

function set_address!(cce::MTL4ArgumentTable, buf::MTLBuffer, bindingIndex)
@objc [cce::id{MTL4ArgumentTable} setAddress:contents(buf)::NSUInteger
atIndex:(bindingIndex-1)::NSUInteger]::Nothing
end

function set_buffer!(cce::MTL4ArgumentTable, buf::MTLBuffer, offset, index)
@inline set_address!(cce, contents(buf)+offset, index)
end
53 changes: 53 additions & 0 deletions lib/mtl/command_alloc4.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

export MTL4CommandAllocatorDescriptor

function MTL4CommandAllocatorDescriptor()
handle = @objc [MTL4CommandAllocatorDescriptor new]::id{MTL4CommandAllocatorDescriptor}
obj = MTL4CommandAllocatorDescriptor(handle)
finalizer(release, obj)
return obj
end
function MTL4CommandAllocatorDescriptor(label)
desc = MTL4CommandAllocatorDescriptor()
desc.label = label
return desc
end



#
# command allocator
#

export MTL4CommandAllocator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How large is the Metal 4 API? Should we put it in a mtl4 top-level folder?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of the Metal 4 API lives as new methods of Metal 3 Objects so maybe a mtl4 subfolder in lib/mtl would be more appropriate?


# @objcwrapper immutable=false MTL4CommandAllocator <: NSObject

function MTL4CommandAllocator(device::MTLDevice)
handle = @objc [device::id{MTLDevice} newCommandAllocator]::id{MTL4CommandAllocator}
obj = MTL4CommandAllocator(handle)
finalizer(release, obj)
return obj
end

function MTL4CommandAllocator(dev::MTLDevice, descriptor::MTL4CommandAllocatorDescriptor)
err = Ref{id{NSError}}(nil)
handle = @objc [dev::id{MTLDevice} newCommandAllocatorWithDescriptor:descriptor::id{MTL4CommandAllocatorDescriptor}
error:err::Ptr{id{NSError}}]::id{MTL4CommandAllocator}
obj = MTL4CommandAllocator(handle)
finalizer(release, obj)
return obj
end

function MTL4CommandAllocator(dev::MTLDevice, label)
desc = MTL4CommandAllocatorDescriptor(label)
return MTL4CommandAllocator(dev, desc)
end

function allocatedSize(alloc::MTL4CommandAllocator)::UInt64
@objc [alloc::id{MTL4CommandAllocator} allocatedSize]::UInt64
end

function reset!(alloc::MTL4CommandAllocator)
@objc [alloc::id{MTL4CommandAllocator} reset]::Nothing
end
59 changes: 59 additions & 0 deletions lib/mtl/command_buf4.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#
# command buffer
#

export MTL4CommandBuffer, commit!, beginCommandBufferWithAllocator!, endCommandBuffer!

# @objcwrapper immutable=false MTL4CommandBuffer <: NSObject

function MTL4CommandBuffer(device::MTLDevice, label=nothing)
handle = @objc [device::id{MTLDevice} newCommandBuffer]::id{MTL4CommandBuffer}
buf = MTL4CommandBuffer(handle)
if !isnothing(label)
buf.label = label
end
return buf
end

function MTL4CommandBuffer(f::Base.Callable, device::MTLDevice, label=nothing; queue::MTL4CommandQueue=MTL4CommandQueue(device), allocator::MTL4CommandAllocator=MTL4CommandAllocator(device))
cmdbuf = MTL4CommandBuffer(device, label)

commit!(f, cmdbuf, queue, allocator)
end

function beginCommandBufferWithAllocator!(cmdbuf::MTL4CommandBuffer, allocator::MTL4CommandAllocator, options::Union{Nothing, MTL4CommandBufferOptions} = nothing)
if isnothing(options)
@objc [cmdbuf::id{MTL4CommandBuffer} beginCommandBufferWithAllocator:allocator::id{MTL4CommandAllocator}]::Nothing
else
@objc [cmdbuf::id{MTL4CommandBuffer} beginCommandBufferWithAllocator:allocator::id{MTL4CommandAllocator}
options:options::id{MTL4CommandBufferOptions}]::Nothing
end
end

function endCommandBuffer!(cmdbuf::MTL4CommandBuffer)
@objc [cmdbuf::id{MTL4CommandBuffer} endCommandBuffer]::Nothing
end

function commit!(cmdqueue::MTL4CommandQueue, cmdbuf::MTL4CommandBuffer)
cmdbufRef = Ref{MTL4CommandBuffer}(cmdbuf)
@objc [cmdqueue::id{MTL4CommandQueue} commit:cmdbufRef::Ref{MTL4CommandBuffer}
count:1::NSUInteger]::Nothing
end
function commit!(cmdqueue::MTL4CommandQueue, cmdbuf::MTL4CommandBuffer, options::MTL4CommitOptions)
cmdbufRef = Ref{MTL4CommandBuffer}(cmdbuf)
@objc [cmdqueue::id{MTL4CommandQueue} commit:cmdbufRef::Ref{MTL4CommandBuffer}
count:1::NSUInteger
options:options::id{MTL4CommitOptions}]::Nothing
end

function commit!(f::Base.Callable, cmdbuf::MTL4CommandBuffer, queue::MTL4CommandQueue, allocator::MTL4CommandAllocator)
beginCommandBufferWithAllocator!(cmdbuf, allocator)

try
ret = f(cmdbuf)
return ret
finally
endCommandBuffer!(cmdbuf)
commit!(queue, cmdbuf)
end
end
91 changes: 91 additions & 0 deletions lib/mtl/command_enc/compute4.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
export MTL4ComputeCommandEncoder
export set_function!, set_buffer!, set_bytes!, set_texture!, set_sampler_state!
export stages
export dispatchThreadgroups!, dispatchThreads!, endEncoding!
export use!, memoryBarrier!, append_copy!, append_fillbuffer!, append_sync!

# @objcwrapper immutable=false MTL4ComputeCommandEncoder <: MTL4CommandEncoder

function MTL4ComputeCommandEncoder(cmdbuf::MTL4CommandBuffer)
handle = @objc [cmdbuf::id{MTL4CommandBuffer} computeCommandEncoder]::id{MTL4ComputeCommandEncoder}
obj = MTL4ComputeCommandEncoder(handle)
# finalizer(release, obj)
return obj
end


function MTL4ComputeCommandEncoder(f::Base.Callable, cmdbuf::MTL4CommandBuffer, sync=false)
encoder = MTL4ComputeCommandEncoder(cmdbuf)
try
f(encoder)
finally
sync && barrierAfterStages!(encoder)
close(encoder)
end
end

# Pipeline State
function set_function!(cce::MTL4ComputeCommandEncoder, pipeline::MTLComputePipelineState)
@objc [cce::id{MTL4ComputeCommandEncoder} setComputePipelineState:pipeline::id{MTLComputePipelineState}]::Nothing
end

function set_argument_table!(cce::MTL4ComputeCommandEncoder, arg_table::MTL4ArgumentTable)
@objc [cce::id{MTL4ComputeCommandEncoder} setArgumentTable:arg_table::id{MTL4ArgumentTable}]::Nothing
end

# Dispatch Commands
function dispatchThreadgroups!(cce::MTL4ComputeCommandEncoder, gridSize::MTLSize, threadGroupSize::MTLSize)
@objc [cce::id{MTL4ComputeCommandEncoder} dispatchThreadgroups:gridSize::MTLSize
threadsPerThreadgroup:threadGroupSize::MTLSize]::Nothing
end

function dispatchThreads!(cce::MTL4ComputeCommandEncoder, threadsSize::MTLSize, threadsPerThreadgroup::MTLSize)
@objc [cce::id{MTL4ComputeCommandEncoder} dispatchThreads:threadsSize::MTLSize
threadsPerThreadgroup:threadsPerThreadgroup::MTLSize]::Nothing
end

# Copy Operations (Blit functionality integrated into compute encoder in Metal 4)
function append_copy!(cce::MTL4ComputeCommandEncoder, dst::MTLBuffer, dstOffset::Integer,
src::MTLBuffer, srcOffset::Integer, size::Integer)
@objc [cce::id{MTL4ComputeCommandEncoder} copyFromBuffer:src::id{MTLBuffer}
sourceOffset:srcOffset::NSUInteger
toBuffer:dst::id{MTLBuffer}
destinationOffset:dstOffset::NSUInteger
size:size::NSUInteger]::Nothing
end

# function append_copy!(cce::MTL4ComputeCommandEncoder, dst::MTLTexture, dstSlice::Integer, dstLevel::Integer, dstOrigin::MTLOrigin,
# src::MTLBuffer, srcOffset::Integer, srcBytesPerRow::Integer, srcBytesPerImage::Integer,
# size::MTLSize)
# @objc [cce::id{MTL4ComputeCommandEncoder} copyFromBuffer:src::id{MTLBuffer}
# sourceOffset:srcOffset::NSUInteger
# sourceBytesPerRow:srcBytesPerRow::NSUInteger
# sourceBytesPerImage:srcBytesPerImage::NSUInteger
# sourceSize:size::MTLSize
# toTexture:dst::id{MTLTexture}
# destinationSlice:dstSlice::NSUInteger
# destinationLevel:dstLevel::NSUInteger
# destinationOrigin:dstOrigin::MTLOrigin]::Nothing
# end

function stages(cce::MTL4ComputeCommandEncoder)
@objc [cce::id{MTL4ComputeCommandEncoder} stages]::MTLStages
end

# Fill Buffer
function append_fillbuffer!(cce::MTL4ComputeCommandEncoder, buffer::MTLBuffer, range::NSRange, value::UInt8)
@objc [cce::id{MTL4ComputeCommandEncoder} fillBuffer:buffer::id{MTLBuffer}
range:range::NSRange
value:value::UInt8]::Nothing
end

function append_fillbuffer!(cce::MTL4ComputeCommandEncoder, buffer::MTLBuffer, value::UInt8,
byteSize::Integer, offset::Integer=0)
range = NSRange(offset, byteSize)
append_fillbuffer!(cce, buffer, range, value)
end

# Convenience dispatch function for encoding
function append_current_function!(cce::MTL4ComputeCommandEncoder, gridSize::MTLSize, threadGroupSize::MTLSize)
dispatchThreadgroups!(cce, gridSize, threadGroupSize)
end
35 changes: 35 additions & 0 deletions lib/mtl/command_enc4.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export endEncoding!, updateFence!, waitForFence
export barrierAfterEncoderStages!, barrierAfterQueueStages!, barrierAfterStages!

# @objcwrapper immutable=true MTL4CommandEncoder <: NSObject

function updateFence!(encoder::MTL4CommandEncoder, fence::MTLFence, afterEncoderStages::MTLStages=MTLStageAll)
@objc [encoder::id{MTL4CommandEncoder} updateFence:fence::id{MTLFence}
afterEncoderStages:afterEncoderStages::MTLStages]::Nothing
end

function waitForFence(encoder::MTL4CommandEncoder, fence::MTLFence, beforeEncoderStages::MTLStages=MTLStageAll)
@objc [encoder::id{MTL4CommandEncoder} waitForFence:fence::id{MTLFence}
beforeEncoderStages:afterEncoderStages::MTLStages]::Nothing
end

function barrierAfterEncoderStages!(encoder::MTL4CommandEncoder, afterEncoderStages::MTLStages=MTLStageAll, beforeEncoderStages::MTLStages=MTLStageAll, visibilityOptions::MTL4VisibilityOptions=MTL4VisibilityOptionResourceAlias)
@objc [encoder::id{MTL4CommandEncoder} barrierAfterEncoderStages:afterEncoderStages::MTLStages
beforeEncoderStages:beforeEncoderStages::MTLStages
visibilityOptions:visibilityOptions::MTL4VisibilityOptions]::Nothing
end

function barrierAfterQueueStages!(encoder::MTL4CommandEncoder, afterQueueStages::MTLStages=MTLStageAll, beforeStages::MTLStages=MTLStageAll, visibilityOptions::MTL4VisibilityOptions=MTL4VisibilityOptionResourceAlias)
@objc [encoder::id{MTL4CommandEncoder} barrierAfterQueueStages:afterQueueStages::MTLStages
beforeStages:beforeStages::MTLStages
visibilityOptions:visibilityOptions::MTL4VisibilityOptions]::Nothing
end

function barrierAfterStages!(encoder::MTL4CommandEncoder, afterStages::MTLStages=MTLStageAll, beforeQueueStages::MTLStages=MTLStageAll, visibilityOptions::MTL4VisibilityOptions=MTL4VisibilityOptionResourceAlias)
@objc [encoder::id{MTL4CommandEncoder} barrierAfterStages:afterStages::MTLStages
beforeQueueStages:beforeQueueStages::MTLStages
visibilityOptions:visibilityOptions::MTL4VisibilityOptions]::Nothing
end

endEncoding!(ce::MTL4CommandEncoder) = @objc [ce::id{MTL4CommandEncoder} endEncoding]::Nothing
Base.close(ce::MTL4CommandEncoder) = endEncoding!(ce)
72 changes: 72 additions & 0 deletions lib/mtl/command_queue4.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export MTL4CommitOptions

# @objcwrapper immutable=false MTL4CommitOptions <: NSObject

function MTL4CommitOptions()
handle = @objc [MTL4CommitOptions new]::id{MTL4CommitOptions}
obj = MTL4CommitOptions(handle)
finalizer(release, obj)
return obj
end
function MTL4CommitOptions(f::Base.Callable)
options = MTL4CommitOptions()
addFeedbackHandler(f, options)
return options
end

function _command_buffer4_callback(f)
# convert the incoming pointer, and discard any return value
function wrapper(ptr)
try
f(ptr == nil ? nothing : MTL4CommitFeedback(ptr))
catch err
# we might be on an unmanaged thread here, so display the error
# (otherwise it may get lost, or worse, crash Julia)
@error "Command buffer callback encountered an error: " * sprint(showerror, err)
end
return
end
@objcblock(wrapper, Nothing, (id{MTL4CommitFeedback},))
end

function addFeedbackHandler(f::Base.Callable, options::MTL4CommitOptions)
block = _command_buffer4_callback(f)
@objc [options::id{MTL4CommitOptions} addFeedbackHandler:block::id{NSBlock}]::Nothing
end



export MTL4CommandQueueDescriptor

function MTL4CommandQueueDescriptor()
handle = @objc [MTL4CommandQueueDescriptor new]::id{MTL4CommandQueueDescriptor}
obj = MTL4CommandQueueDescriptor(handle)
finalizer(release, obj)
return obj
end
function MTL4CommandQueueDescriptor(label)
desc = MTL4CommandQueueDescriptor()
desc.label = label
return desc
end


export MTL4CommandQueue

# @objcwrapper immutable=false MTL4CommandQueue <: NSObject

function MTL4CommandQueue(dev::MTLDevice)
handle = @objc [dev::id{MTLDevice} newMTL4CommandQueue]::id{MTL4CommandQueue}
obj = MTL4CommandQueue(handle)
finalizer(release, obj)
return obj
end

function MTL4CommandQueue(dev::MTLDevice, descriptor::MTL4CommandQueueDescriptor)
err = Ref{id{NSError}}(nil)
handle = @objc [dev::id{MTLDevice} newMTL4CommandQueueWithDescriptor:descriptor::id{MTL4CommandQueueDescriptor}
error:err::Ptr{id{NSError}}]::id{MTL4CommandQueue}
obj = MTL4CommandQueue(handle)
finalizer(release, obj)
return obj
end
File renamed without changes.
Loading