-
Notifications
You must be signed in to change notification settings - Fork 47
Metal 4 #612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Metal 4 #612
Changes from all commits
820987c
4cd871e
d07df7d
4c25119
80d32ab
c0fef38
9df7891
6cf080f
0079d3a
faa176f
049e632
6fca8e2
7cf55e6
ffc5b41
2529a91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
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 |
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 |
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) |
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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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 anMTLCommandQueue
or anMTL4CommandQueue
, and everything else that takes in aqueue
should dispatch on the correct implementation based on which type was provided?