2021 Graphics & Games
WWDC21 · 25 min · Graphics & Games
Discover compilation workflows in Metal
The Metal shading language is a powerful C++ based language that allows apps to render stunning effects while maintaining a flexible shader development pipeline. Discover how to more easily build and extend your render pipelines using Dynamic Libraries and Function Pointers. We’ll also show you how to accelerate your shader compilation at runtime with Binary Function Archives, Function Linking, and Function Stitching.
Watch at developer.apple.com ↗Code shown on screen · 9 snippets
Shading language
// Declare external functions
extern float4 foo(FragmentInput input);
extern float4 bar(FragmentInput input);
// Use functions in shader
fragment float4 main(FragmentInput input [[stage_in]])
{
switch(condition(input))
{
case 0:
return foo(input);
case 1:
return bar(input);
}
} Declare and instantiate visible functions
// Declare a descriptor and set CompileToBinary options
MTLFunctionDescriptor* functionDescriptor = [MTLFunctionDescriptor new];
functionDescriptor.options = MTLFunctionOptionCompileToBinary;
// Backend compile the function
functionDescriptor.name = @"foo";
id<MTLFunction> foo = [library newFunctionWithDescriptor:functionDescriptor Configure pipeline descriptor
// Provide a list of functions that the pipeline stage may call
// AIR functions
renderPipeDesc.fragmentLinkedFunctions.functions = @[foo, bar, baz];
// Binary functions
renderPipeDesc.fragmentLinkedFunctions.binaryFunctions = @[foo, bar, baz]; Create and populate visible function table
// Create visible function table
[renderPipeline newVisibleFunctionTableWithDescriptor:stage:];
// Create function handles
[renderPipeline functionHandleWithFunction:stage:];
// Insert handles into table
[visibleFunctionTable setFunction:atIndex:]; Encoding and calling function pointers
// Bind visible function table objects to each stage
[renderCommandEncoder setFragmentVisibleFunctionTable:atBufferIndex:];
// Usage in shader
fragment float4 shaderFunc(FragmentData vo[[stage_in]],
visible_function_table<float4(float3)>materials[[buffer(0)]])
{
//...
return materials[materialSelector](coord);
} Incremental pipeline creation
// Enable incrementally adding binary functions per stage
renderPipeDesc.supportAddingFragmentBinaryFunctions = YES;
// Create render pipeline functions descriptor
MTLRenderPipelineFunctionsDescriptor extraDesc;
extraDesc.fragmentAdditionalBinaryFunctions = @[bat];
// Instantiate render pipeline state
id<MTLRenderPipelineState> renderPipeline2 =
[renderPipeline1 newRenderPipelineStateWithAdditionalBinaryFunctions:extraDesc Stitching process
[[stitchable]] int FunctionA(device int*, int) {…}
[[stitchable]] int FunctionC(int, int) {…}
[[stitchable]]
int ResultFunction(device int* Input0,
int Input1,
int Input2)
{
int N0 = FunctionA(Input0, Input1);
int N1 = FunctionA(Input0, Input2);
int N2 = FunctionC(N0, N1);
return N2;
} Creating the graph
// Create input nodes
inputs[0] = [[MTLFunctionStitchingInputNode alloc] initWithArgumentIndex:0];
// Create function nodes
n0 = [[MTLFunctionStitchingFunctionNode alloc] initWithName:@"FunctionA"
arguments:@[inputs[0], inputs[1]]
controlDependencies:@[]];
n1 = [[MTLFunctionStitchingFunctionNode alloc] initWithName:@"FunctionA"
arguments:@[inputs[0], inputs[2]]
controlDependencies:@[]];
n2 = [[MTLFunctionStitchingFunctionNode alloc] initWithName:@"FunctionC"
arguments:@[n0, n1]
controlDependencies:@[]];
// Create graph
graph = [[MTLFunctionStitchingGraph alloc] initWithFunctionName:@"ResultFunction"
nodes:@[n0, n1]
outputNode:n2
attributes:@[]]; Configure stitched library descriptor
// Configure stitched library descriptor
MTLStitchedLibraryDescriptor* descriptor = [MTLStitchedLibraryDescriptor new];
descriptor.functions = @[stitchableFunctions];
descriptor.functionGraphs = @[graph];
// Create stitched function
id<MTLLibrary> lib = [device newLibraryWithDescriptor:descriptor
error:&error];
id<MTLFunction> stitchedFunction = [lib newFunctionWithName:@"ResultFunction"]; Resources
Related sessions
-
19 min -
17 min -
14 min -
40 min -
21 min -
36 min