2022 Graphics & Games
WWDC22 · 34 min · Graphics & Games
Go bindless with Metal 3
Learn how you can unleash powerful rendering techniques like ray tracing when you go bindless with Metal 3. We’ll show you how to make your app’s bindless journey a joy by simplifying argument buffers, allocating acceleration structures from heaps, and benefitting from the improvements to the Metal’s validation layer and Debugger Tools. We’ll also explore how you can command more CPU and GPU performance with long-term resource structures.
Watch at developer.apple.com ↗Code shown on screen · 8 snippets
Write argument buffers in Metal 3
// Write argument buffers in Metal 3
struct Mesh
{
uint64_t normals; // 64-bit uint for constant packed_float3*
};
NSUInteger meshArgumentSize = sizeof(struct Mesh);
id<MTLBuffer> meshArgumentBuffer = [device newBufferWithLength:meshArgumentSize
options:storageMode];
struct Mesh* meshes = (struct Mesh *)(meshArgumentBuffer.contents);
meshes->normals = normalBuffer.gpuAddress + normalBufferOffset; // Shader struct:
// Shader struct:
struct Mesh
{
constant packed_float3* normals;
};
// Host-side struct:
struct Mesh
{
uint64_t normals;
}; Shared struct:
// Shared struct:
struct Mesh
{
CONSTANT_PTR(packed_float3) normals;
}; Write unbounded arrays of resources in Metal 3
// Write unbounded arrays of resources in Metal 3
struct Mesh
{
uint64_t normals; // 64-bit uint for constant packed_float3*
};
NSUInteger meshArgumentSize = sizeof(struct Mesh) * meshes.count;
id<MTLBuffer> meshArgumentBuffer = [device newBufferWithLength:meshArgumentSize
options:storageMode];
struct Mesh* meshes = (struct Mesh *)(meshArgumentBuffer.contents);
for ( NSUInteger i = 0; i < meshes.count; ++i )
{
meshes[i].normals = normalBuffers[i].gpuAddress + normalBufferOffsets[i];
} Metal shading language: unbounded arrays option 1
// Metal shading language:
struct Mesh
{
constant packed_float3* normals;
};
fragment half4 fragmentShader(ColorInOut v [[stage_in]],
constant Mesh* meshes [[buffer(0)]] )
{
/* determine mesh to read, e.g. geometry_id */
packed_float3 n0 = meshes[ geometry_id ].normals[0];
packed_float3 n1 = meshes[ geometry_id ].normals[1];
packed_float3 n2 = meshes[ geometry_id ].normals[2];
/* interpolate normals and calculate shading */
} Metal shading language: unbounded arrays option 2
// Metal shading language:
struct Mesh
{
constant packed_float3* normals;
};
struct Scene
{
constant Mesh* meshes; // mesh array
constant Material* materials; // material array
};
fragment half4 fragmentShader(ColorInOut v [[stage_in]],
constant Scene& scene [[buffer(0)]] )
{
/* determine mesh to read, e.g. geometry_id */
packed_float3 n0 = scene.meshes[ geometry_id ].normals[0];
packed_float3 n1 = scene.meshes[ geometry_id ].normals[1];
packed_float3 n2 = scene.meshes[ geometry_id ].normals[2];
/* interpolate normals and calculate shading */
} Size and alignment for MTLAccelerationStructure in a MTLHeap
heapAccelerationStructureSizeAndAlignWithDescriptor: Store individual indirect resources in NSMutableSet
// Argument buffer loading
for (NSUInteger i = 0; i < mesh.submeshes.count; ++i) {
Submesh* submesh = mesh.submeshes[i];
id<MTLBuffer> indexBuffer = submesh.indexBuffer;
NSArray* textures = submesh.textures;
// Copy index buffer into argument buffer
submeshAB[i].indices = indexBuffer.gpuAddress;
// Copy material textures into argument buffer
for (NSUInteger m = 0; m < textures.count; ++m) {
submeshAB[i].textures[m] = textures[m].gpuResourceID;
}
// Remember indirect resources
[sceneResources addObject:indexBuffer];
[sceneResources addObjectsFromArray:textures];
} Resources
Related sessions
-
19 min -
16 min -
31 min -
38 min -
30 min -
30 min -
21 min