Drawables
Best Practice: Hold a drawable as briefly as possible.
Most Metal apps implement a layer-backed view defined by a CAMetalLayer
object. This layer vends an efficient displayable resource conforming to the CAMetalDrawable
protocol, commonly referred to as a drawable. A drawable provides a MTLTexture
object that is typically used as a displayable render target attached to a MTLRenderPassDescriptor
object, with the goal of being presented on the screen.
A drawable’s presentation is registered by calling a command buffer’s presentDrawable:
method before calling its commit
method. However, the drawable itself is actually presented only after the command buffer has completed execution and the drawable has been rendered or written to.
A drawable tracks whether it has outstanding render or write requests on it and will not present until those requests have been completed. A command buffer registers its drawable requests only when it is scheduled for execution. Registering a drawable presentation after the command buffer is scheduled guarantees that all command buffer work will be completed before the drawable is actually presented. Do not wait for the command buffer to complete its GPU work before registering a drawable presentation; this will cause a considerable CPU stall.
Hold a Drawable as Briefly as Possible
Drawables are expensive system resources created and maintained by the Core Animation framework. They exist within a limited and reusable resource pool and may or may not be available when requested by your app. If there is no drawable available at the time of your request, the calling thread is blocked until a new drawable becomes available (which is usually at the next display refresh interval).
To hold a drawable as briefly as possible, follow these two steps:
Always acquire a drawable as late as possible; preferably, immediately before encoding an on-screen render pass. A frame’s CPU work may include dynamic data updates and off-screen render passes that you can perform before acquiring a drawable.
Always release a drawable as soon as possible; preferably, immediately after finalizing a frame’s CPU work. It is highly advisable to contain your rendering loop within an autorelease pool block to avoid possible deadlock situations with multiple drawables.
Figure 6-1 shows the lifetime of a drawable in relation to other CPU work.
Use a MetalKit View to Interact with Drawables
Using an MTKView
object is the preferred way to interact with drawables. An MTKView
object is backed by a CAMetalLayer
object and provides the currentDrawable
property to acquire the drawable for the current frame. The current frame renders into this drawable and the presentDrawable:
method schedules the actual presentation to occur at the next display refresh interval. The currentDrawable
property is automatically updated at the end of every frame.
An MTKView
object also provides the currentRenderPassDescriptor
convenience property that references the current drawable’s texture; use this property to create a render command encoder that renders into the current drawable. A call to the currentRenderPassDescriptor
property implicitly acquires the drawable for the current frame, which is then stored in the currentDrawable
property.
Listing 6-1 shows how to use a drawable with a MetalKit view.
- (void)render:(MTKView *)view {
// Update your dynamic data
[self update];
// Create a new command buffer
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
// BEGIN encoding any off-screen render passes
/* ... */
// END encoding any off-screen render passes
// BEGIN encoding your on-screen render pass
// Acquire a render pass descriptor generated from the drawable's texture
// 'currentRenderPassDescriptor' implicitly acquires the drawable
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
// If there's a valid render pass descriptor, use it to render into the current drawable
if(renderPassDescriptor != nil) {
id<MTLRenderCommandEncoder> renderCommandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
/* Set render state and resources */
/* Issue draw calls */
[renderCommandEncoder endEncoding];
// END encoding your on-screen render pass
// Register the drawable presentation
[commandBuffer presentDrawable:view.currentDrawable];
}
/* Register optional callbacks */
// Finalize the CPU work and commit the command buffer to the GPU
[commandBuffer commit];
}
- (void)drawInMTKView:(MTKView *)view {
@autoreleasepool {
[self render:view];
}
}
Copyright © 2018 Apple Inc. All rights reserved. Terms of Use | Privacy Policy | Updated: 2017-03-27