Donner SVG 0.8.0-pre
SVG editor and embeddable C⁠+⁠+⁠20 engine.
Loading...
Searching...
No Matches
donner::gpu::metal::MetalDevice Class Referencefinal

Metal backend of the Donner GPU runtime. More...

#include "donner/gpu/metal/MetalDevice.h"

Inheritance diagram for donner::gpu::metal::MetalDevice:
[legend]

Public Types

enum class  MemoryModel : uint8_t {
  Detected ,
  ForceNonUnified
}
 Which memory model the backend builds its resources for. More...

Public Member Functions

bool usesUnifiedMemoryForTest () const
 Whether this device's resources are built for unified memory. Test accessor.
uint64_t hostWritePublishCountForTest () const
 How many buffer writes have published their range to the device copy. Test accessor.
uint64_t deviceWritePublishCountForTest () const
 How many submissions have published the device's changes back to the host copy. Test accessor, for the same reason as above but in the other direction.
 ~MetalDevice () override
 Destructor; releases all Metal objects still alive.
uint64_t completedSerial () const override
 Serial of the most recent submission whose Metal command buffer has completed on the GPU (0 if none). Updated by completion handlers, which may run on another thread.
bool waitForSerial (uint64_t serial, double timeoutSeconds)
 Blocks until completedSerial reaches serial or timeoutSeconds elapses, by polling the completion counter (the completion handler runs on a Metal-internal thread, so a poll loop with a short sleep is sufficient and keeps this backend free of extra sync primitives).
Result< std::vector< uint8_t > > readBackBuffer (const Buffer &buffer)
 Copies the full contents of buffer back to the host and returns the bytes.
std::string lastErrorForTest () const
 Message of the most recent asynchronous command-buffer execution error captured by a completion handler, or an empty string if none occurred. Test/diagnostic accessor.
std::string adapterName () const
 Name the Metal driver reports for the underlying device, for example Apple M1 Pro. Two GPUs running the same shaders can round a covered edge texel differently, so anything comparing this backend's pixels against a committed record has to know which one it is on.
Public Member Functions inherited from donner::gpu::Device
virtual ~Device ()
 Destructor; expires the device-alive token (so handles that outlive the device release nothing) and frees all remaining resources. Backends that submit asynchronously must wait for in-flight submissions in their own destructor before backend state is torn down.
 Device (const Device &)=delete
Device & operator= (const Device &)=delete
 Device (Device &&)=delete
Device & operator= (Device &&)=delete
uint64_t deviceId () const
 Process-unique identity of this device (starts at 1, never reused). Baked into every handle for cross-device validation.
Result< BuffercreateBuffer (const BufferDescriptor &descriptor)
 Creates a buffer.
Result< TexturecreateTexture (const TextureDescriptor &descriptor)
 Creates a 2D texture.
Result< TextureViewcreateTextureView (const Texture &texture, const TextureViewDescriptor &descriptor)
 Creates a view of texture covering the whole texture.
Result< SamplercreateSampler (const SamplerDescriptor &descriptor)
 Creates a sampler.
Result< BindGroupLayoutcreateBindGroupLayout (const BindGroupLayoutDescriptor &descriptor)
 Creates a bind group layout.
Result< BindGroupcreateBindGroup (const BindGroupDescriptor &descriptor)
 Creates a bind group.
Result< PipelineLayoutcreatePipelineLayout (const PipelineLayoutDescriptor &descriptor)
 Creates a pipeline layout.
Result< ShaderModulecreateShaderModule (const ShaderModuleDescriptor &descriptor)
 Creates a shader module from trusted generated source.
Result< RenderPipelinecreateRenderPipeline (const RenderPipelineDescriptor &descriptor)
 Creates a render pipeline.
Result< ComputePipelinecreateComputePipeline (const ComputePipelineDescriptor &descriptor)
 Creates a compute pipeline.
Status destroyBuffer (Buffer &&buffer)
 Destroys a buffer (see the destroy contract above).
Status destroyTexture (Texture &&texture)
 Destroys a texture (see the destroy contract above).
Status destroyTextureView (TextureView &&textureView)
 Destroys a texture view (see the destroy contract above).
Status destroySampler (Sampler &&sampler)
 Destroys a sampler (see the destroy contract above).
Status destroyBindGroupLayout (BindGroupLayout &&bindGroupLayout)
 Destroys a bind group layout (see the destroy contract above).
Status destroyBindGroup (BindGroup &&bindGroup)
 Destroys a bind group (see the destroy contract above).
Status destroyPipelineLayout (PipelineLayout &&pipelineLayout)
 Destroys a pipeline layout (see the destroy contract above).
Status destroyShaderModule (ShaderModule &&shaderModule)
 Destroys a shader module (see the destroy contract above).
Status destroyRenderPipeline (RenderPipeline &&renderPipeline)
 Destroys a render pipeline (see the destroy contract above).
Status destroyComputePipeline (ComputePipeline &&computePipeline)
 Destroys a compute pipeline (see the destroy contract above).
Result< std::unique_ptr< CommandEncoder > > createCommandEncoder ()
 Creates a command encoder recording against this device. The encoder must not outlive the device.
Result< SurfacecreateSurface (const SurfaceDescriptor &descriptor)
 Creates a surface presenting to a platform object.
Result< SurfaceCapabilitiessurfaceCapabilities (const Surface &surface) const
 What surface supports, for choosing a configuration.
Status configureSurface (const Surface &surface, const SurfaceConfiguration &configuration)
 Configures how surface presents, replacing any previous configuration.
Result< SurfaceTextureacquireCurrentTexture (const Surface &surface)
 Acquires the texture for the next frame of surface.
Result< SurfaceStatuspresentSurface (const Surface &surface)
 Presents the texture acquired from surface and invalidates it.
Status abandonCurrentTexture (const Surface &surface)
 Releases the acquired texture of surface without presenting it, for a frame the caller decided not to show.
Status destroySurface (Surface &&surface)
 Destroys a surface (see the destroy contract above).
Result< BufferMappingmapBufferAsync (const Buffer &buffer, MapMode mode, uint64_t offsetBytes, uint64_t byteCount)
 Begins mapping a range of buffer for host access, and returns the handle that names the mapping.
Result< MapWaitOutcomewaitForMapping (const BufferMapping &mapping, const MapWaitParams &params, const std::function< bool()> &shouldCancel, const MapWaitTestHooks &testHooks={})
Result< std::span< const uint8_t > > mappedBytes (const BufferMapping &mapping) const
 Returns the mapped bytes of a completed mapping.
Status unmapBuffer (BufferMapping &&mapping)
 Releases a mapping, invalidating the handle and every copy of it.
Status writeBuffer (const Buffer &buffer, uint64_t offsetBytes, std::span< const uint8_t > data)
 Writes data into buffer at offsetBytes.
Status writeTexture (const Texture &texture, std::span< const uint8_t > data, const TexelCopyBufferLayout &dataLayout, const Extent2d &writeSize)
 Writes texel rows from data into texture starting at texel (0, 0).
Result< uint64_t > submit (CommandBuffer commandBuffer)
 Submits a finished command buffer, consuming it, and returns the assigned submission serial.
void poll ()
 Processes deferred destructions: releases the backend object of every destroyed resource whose last referencing submission has completed (completedSerial), and recycles its slot.
uint64_t lastSubmittedSerial () const
 Serial assigned to the most recent submission (0 if none yet).

Static Public Member Functions

static std::unique_ptr< MetalDevice > Create (MemoryModel memoryModel=MemoryModel::Detected)
 Creates a device on the system default Metal device.

Protected Member Functions

Status onCreateBuffer (uint32_t slotIndex, const BufferDescriptor &descriptor) override
 Backend hook: a buffer passed validation and occupies slotIndex.
Status onCreateTexture (uint32_t slotIndex, const TextureDescriptor &descriptor) override
 Backend hook: a texture passed validation and occupies slotIndex.
Status onCreateTextureView (uint32_t slotIndex, uint32_t textureSlotIndex, const TextureViewDescriptor &descriptor) override
 Backend hook: a texture view passed validation and occupies slotIndex.
Status onCreateSampler (uint32_t slotIndex, const SamplerDescriptor &descriptor) override
 Backend hook: a sampler passed validation and occupies slotIndex.
Status onCreateBindGroupLayout (uint32_t slotIndex, const BindGroupLayoutDescriptor &descriptor) override
 Backend hook: a bind group layout passed validation and occupies slotIndex.
Status onCreateBindGroup (uint32_t slotIndex, const BindGroupDescriptor &descriptor) override
 Backend hook: a bind group passed validation and occupies slotIndex.
Status onCreatePipelineLayout (uint32_t slotIndex, const PipelineLayoutDescriptor &descriptor) override
 Backend hook: a pipeline layout passed validation and occupies slotIndex.
Status onCreateShaderModule (uint32_t slotIndex, const ShaderModuleDescriptor &descriptor) override
 Backend hook: a shader module passed validation and occupies slotIndex.
Status onCreateRenderPipeline (uint32_t slotIndex, const RenderPipelineDescriptor &descriptor) override
 Backend hook: a render pipeline passed validation and occupies slotIndex.
Status onCreateComputePipeline (uint32_t slotIndex, const ComputePipelineDescriptor &descriptor) override
 Backend hook: a compute pipeline passed validation and occupies slotIndex.
void onDestroyResource (std::string_view resourceName, uint32_t slotIndex) override
 Backend hook: a validated resource was destroyed.
Status onWriteBuffer (uint32_t slotIndex, uint64_t offsetBytes, std::span< const uint8_t > data) override
 Backend hook: a validated buffer write.
Status onWriteTexture (uint32_t slotIndex, std::span< const uint8_t > data, const TexelCopyBufferLayout &dataLayout, const Extent2d &writeSize) override
 Backend hook: a validated texture write.
Status onSubmit (uint64_t submissionSerial, uint32_t commandBufferSlotIndex, std::span< const Command > commands) override
 Backend hook: a validated command buffer was submitted.
Protected Member Functions inherited from donner::gpu::Device
 Device ()
 Constructor for backends; assigns the process-unique device identity.
Status validateBufferHandleForBackend (const Buffer &buffer) const
 Validates a buffer handle for backend-provided auxiliary entry points (test readback helpers and similar), running the same null/device-identity/generation checks the template-method public API performs before its hooks.
Status validateTextureHandleForBackend (const Texture &texture) const
 Validates a texture handle for backend-provided auxiliary entry points, running the same null/device-identity/generation checks the template-method public API performs.
Status validateTextureViewHandleForBackend (const TextureView &textureView) const
 Validates a texture view handle for backend-provided auxiliary entry points: the view itself plus a re-resolution of its viewed texture, so a view of a destroyed (or slot-recycled) texture fails closed exactly like it does on the normal Device paths.
Status validateBufferMappingHandleForBackend (const BufferMapping &mapping) const
 Validates a buffer-mapping handle for backend-provided auxiliary entry points, running the same null/device-identity/generation checks the template-method public API performs, so a stale handle cannot read state belonging to the slot's new occupant.
virtual Status onMapBufferAsync (uint32_t mappingSlotIndex, uint32_t bufferSlotIndex, MapMode mode, uint64_t offsetBytes, uint64_t byteCount)
 Backend hook: begin mapping a buffer range.
virtual MapSliceState onWaitMappingSlice (uint32_t mappingSlotIndex, double sliceSeconds)
 Backend hook: wait up to sliceSeconds for a pending mapping and report what it found.
virtual Result< std::span< const uint8_t > > onMappedBytes (uint32_t mappingSlotIndex) const
 Backend hook: bytes of a completed mapping.
virtual void onUnmapBuffer (uint32_t mappingSlotIndex)
 Backend hook: release a mapping.
virtual Status onCreateSurface (uint32_t slotIndex, const SurfaceDescriptor &descriptor)
 Backend hook: create a surface for a platform object.
virtual Result< SurfaceCapabilitiesonSurfaceCapabilities (uint32_t slotIndex) const
 Backend hook: what a surface supports.
virtual Status onConfigureSurface (uint32_t slotIndex, const SurfaceConfiguration &configuration)
 Backend hook: apply a configuration.
virtual Result< SurfaceStatusonAcquireCurrentTexture (uint32_t slotIndex, uint32_t textureSlotIndex)
 Backend hook: acquire the next frame's texture and report the surface's state.
virtual Result< SurfaceStatusonPresentSurface (uint32_t slotIndex)
 Backend hook: present the acquired texture.
virtual void onAbandonCurrentTexture (uint32_t slotIndex)
 Backend hook: drop the acquired texture without presenting.

Detailed Description

Metal backend of the Donner GPU runtime.

Inherits every fail-closed validation check from donner::gpu::Device; the on* hooks receive only validated input and translate it to Metal objects. Any Metal-side failure (nil object, compile error, encoder failure) fails closed with a donner::gpu::GpuError; the backend never crashes on such failures.

Scope: host-visible buffers and textures, MSL shader modules, render pipelines with a single vertex buffer layout at slot 0 and bind group 0 only, render passes with color attachments, compute pipelines and compute passes, and texture-to-buffer readback copies. Bindings follow the deterministic argument-table mapping in donner/gpu/shader/MslBindingMap.h: buffer binding b maps to Metal buffer index 1 + b, texture and sampler bindings map directly, and stage-in vertex data occupies vertex buffer index 30.

Memory model: the storage mode of host-visible resources follows what the device reports rather than an assumption about it. Where the CPU and GPU address one copy of a resource (hasUnifiedMemory), resources are MTLStorageModeShared and queue writes (memcpy / replaceRegion) and buffer readback need no staging and no publication step. Where they do not, resources are MTLStorageModeManaged and each side's changes must be published to the other explicitly: a host buffer write publishes its range, and every submission ends by publishing the device's changes back before it completes, since afterwards there is no encoder left to do it with. Neither omission is an API error, so a missing publication is not reported by validation - it shows up only as the host reading whatever its copy last held.

Threading: single-threaded use, matching donner::gpu::Device's thread affinity. The one exception is command-buffer completion handlers, which Metal invokes on an internal queue; they touch only atomics and a mutex-protected error string, observable through completedSerial, waitForSerial, and lastErrorForTest.

The header is pure C++ (Objective-C state lives behind a pimpl) so it is includable from C++ tests; the implementation is Objective-C++.

Member Enumeration Documentation

◆ MemoryModel

enum class donner::gpu::metal::MetalDevice::MemoryModel : uint8_t
strong

Which memory model the backend builds its resources for.

Enumerator
Detected 

Take the model the Metal device reports. Production always uses this.

ForceNonUnified 

Build for a device without unified memory whatever it reports, so the host-coherency steps that model needs are exercised on hardware that would otherwise never take them.

Member Function Documentation

◆ completedSerial()

uint64_t donner::gpu::metal::MetalDevice::completedSerial ( ) const
overridevirtual

Serial of the most recent submission whose Metal command buffer has completed on the GPU (0 if none). Updated by completion handlers, which may run on another thread.

Implements donner::gpu::Device.

◆ Create()

std::unique_ptr< MetalDevice > donner::gpu::metal::MetalDevice::Create ( MemoryModel memoryModel = MemoryModel::Detected)
static

Creates a device on the system default Metal device.

Returns nullptr if no Metal device is available (for example on a CI host without a GPU).

Parameters
memoryModelWhich memory model to build resources for; production leaves this detected, and a test forces the non-unified path to cover it on unified hardware.

◆ hostWritePublishCountForTest()

uint64_t donner::gpu::metal::MetalDevice::hostWritePublishCountForTest ( ) const
nodiscard

How many buffer writes have published their range to the device copy. Test accessor.

Counts the buffer path only, where the write is a memcpy through the host pointer and the range has to be published after it. Texture writes go through replaceRegion, which publishes what it wrote on its own, so they are deliberately absent from this count rather than missing from it.

On unified memory nothing is published and this stays zero. The count exists because hardware that addresses one copy produces correct results whether or not the publication happened, so the results cannot show whether it did.

◆ onCreateBindGroup()

Status donner::gpu::metal::MetalDevice::onCreateBindGroup ( uint32_t slotIndex,
const BindGroupDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a bind group passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateBindGroupLayout()

Status donner::gpu::metal::MetalDevice::onCreateBindGroupLayout ( uint32_t slotIndex,
const BindGroupLayoutDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a bind group layout passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateBuffer()

Status donner::gpu::metal::MetalDevice::onCreateBuffer ( uint32_t slotIndex,
const BufferDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a buffer passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateComputePipeline()

Status donner::gpu::metal::MetalDevice::onCreateComputePipeline ( uint32_t slotIndex,
const ComputePipelineDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a compute pipeline passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreatePipelineLayout()

Status donner::gpu::metal::MetalDevice::onCreatePipelineLayout ( uint32_t slotIndex,
const PipelineLayoutDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a pipeline layout passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateRenderPipeline()

Status donner::gpu::metal::MetalDevice::onCreateRenderPipeline ( uint32_t slotIndex,
const RenderPipelineDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a render pipeline passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateSampler()

Status donner::gpu::metal::MetalDevice::onCreateSampler ( uint32_t slotIndex,
const SamplerDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a sampler passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateShaderModule()

Status donner::gpu::metal::MetalDevice::onCreateShaderModule ( uint32_t slotIndex,
const ShaderModuleDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a shader module passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateTexture()

Status donner::gpu::metal::MetalDevice::onCreateTexture ( uint32_t slotIndex,
const TextureDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a texture passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onCreateTextureView()

Status donner::gpu::metal::MetalDevice::onCreateTextureView ( uint32_t slotIndex,
uint32_t textureSlotIndex,
const TextureViewDescriptor & descriptor )
overrideprotectedvirtual

Backend hook: a texture view passed validation and occupies slotIndex.

Parameters
slotIndexSlot index of the new resource.
textureSlotIndexSlot index of the viewed texture.
descriptorValidated descriptor.

Implements donner::gpu::Device.

◆ onDestroyResource()

void donner::gpu::metal::MetalDevice::onDestroyResource ( std::string_view resourceName,
uint32_t slotIndex )
overrideprotectedvirtual

Backend hook: a validated resource was destroyed.

Parameters
resourceNameResource type name, e.g. "buffer".
slotIndexSlot index of the destroyed resource.

Implements donner::gpu::Device.

◆ onSubmit()

Status donner::gpu::metal::MetalDevice::onSubmit ( uint64_t submissionSerial,
uint32_t commandBufferSlotIndex,
std::span< const Command > commands )
overrideprotectedvirtual

Backend hook: a validated command buffer was submitted.

Parameters
submissionSerialSerial assigned to this submission.
commandBufferSlotIndexSlot the command buffer occupied before being consumed.
commandsValidated commands, in recording order.

Implements donner::gpu::Device.

◆ onWriteBuffer()

Status donner::gpu::metal::MetalDevice::onWriteBuffer ( uint32_t slotIndex,
uint64_t offsetBytes,
std::span< const uint8_t > data )
overrideprotectedvirtual

Backend hook: a validated buffer write.

Parameters
slotIndexDestination buffer slot.
offsetBytesDestination byte offset.
dataPayload bytes.

Implements donner::gpu::Device.

◆ onWriteTexture()

Status donner::gpu::metal::MetalDevice::onWriteTexture ( uint32_t slotIndex,
std::span< const uint8_t > data,
const TexelCopyBufferLayout & dataLayout,
const Extent2d & writeSize )
overrideprotectedvirtual

Backend hook: a validated texture write.

Parameters
slotIndexDestination texture slot.
dataPayload bytes.
dataLayoutRow layout of data.
writeSizeExtent written in texels.

Implements donner::gpu::Device.

◆ readBackBuffer()

Result< std::vector< uint8_t > > donner::gpu::metal::MetalDevice::readBackBuffer ( const Buffer & buffer)

Copies the full contents of buffer back to the host and returns the bytes.

Test/readback convenience, pending a buffer mapping API: it validates device identity, slot liveness, and the handle generation, then reads the shared-storage Metal buffer contents directly. Callers must ensure relevant GPU work has completed first (see waitForSerial).

Parameters
bufferBuffer to read back; must be a live buffer of this device.

◆ waitForSerial()

bool donner::gpu::metal::MetalDevice::waitForSerial ( uint64_t serial,
double timeoutSeconds )

Blocks until completedSerial reaches serial or timeoutSeconds elapses, by polling the completion counter (the completion handler runs on a Metal-internal thread, so a poll loop with a short sleep is sufficient and keeps this backend free of extra sync primitives).

Returns false on timeout, and also returns false if any completed command buffer reported an execution error (see lastErrorForTest).

Parameters
serialSubmission serial to wait for.
timeoutSecondsMaximum time to wait, in seconds.

The documentation for this class was generated from the following file: