2021 Audio & VideoPhotos & CameraGraphics & Games
WWDC21 · 34 min · Audio & Video / Photos & Camera / Graphics & Games
Explore HDR rendering with EDR
EDR is Apple’s High Dynamic Range representation and rendering pipeline. Explore how you can render HDR content using EDR in your app and unleash the dynamic range capabilities of your HDR display including Apple’s internal displays and Pro Display XDR. We’ll show you how game and pro app developers can take advantage of the native EDR APIs on macOS for even more control, and provide best practices for deciding when HDR is appropriate, applying tone-mapping, and delivering HDR content.
Watch at developer.apple.com ↗Code shown on screen · 8 snippets
AVPlayer automatically uses EDR with HDR content
// Instantiate AVPlayer with HDR Video Content
VPLayer* player = [AVPLayer playerWithURL:HDRVideoURL];
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
// Play HDR Video via EDR
AVPlayerViewController* controller = [[AVPlayerViewController alloc] init];
controller.player = player;
[player play]; EDR with CAMetalLayer 1
// Opt-in to EDR
metalLayer.wantsExtendedDynamicRangeContent = YES;
// Set extended-range colorspace
metalLayer.colorspace =
CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
// Select FP16 pixel buffer format
metalLayer.pixelFormat = MTLPixelFormatRGBA16Float; EDR with CAMetalLayer 2
// Create CGImage from HDR Image
CGImageSourceRef isr = CGImageSourceCreateWithURL((CFURLRef)HDRimageURL, NULL);
CGImageRef img = CGImageSourceCreateImageAtIndex(isr, 0, NULL);
// Draw into floating point bitmap context
size_t width = CGImageGetWidth(img);
size_t height = CGImageGetHeight(img);
CGBitmapInfo info = kCGBitmapByteOrder16Host | kCGImageAlphaPremultipliedLast |
kCGBitmapFloatComponents;
CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 16, 0,
metalLayer.colorspace, info);
CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), img);
// Create floating point texture
MTLTextureDescriptor* desc = [[MTLTextureDescriptor alloc] init];
desc.pixelFormat = MTLPixelFormatRGBA16Float;
desc.textureType = MTLTextureType2D;
id<MTLTexture> tex = [metalLayer.device newTextureWithDescriptor:desc];
// Load EDR bitmap into texture
const void* data = CGBitmapContextGetData(ctx);
[tex replaceRegion:MTLRegionMake2D(0, 0, width, height)
mipmapLevel:0
withBytes:data
bytesPerRow:CGBitmapContextGetBytesPerRow(ctx)];
// Draw with the texture in your EDR enabled metal pipeline EDR with NSOpenGLView
// Opt-in to EDR
- (void) viewWillMoveToWindow:(nullable NSWindow *)newWindow {
self.wantsExtendedDynamicRangeOpenGLSurface = YES;
}
// Select OpenGL float pixel buffer format
NSOpenGLPixelFormatAttribute attribs[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAMultiSample,
NSOpenGLPFAColorFloat,
NSOpenGLPFAColorSize, 64,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
0};
NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
// Draw EDR content into NSOpenGLView EDR with NSOpenGLView
// Get existing colorspace from the window
CGColorSpaceRef color_space = [view.window.colorSpace CGColorSpace];
// Promote the colorspace to extended-range
CGColorSpaceRef color_space_extended = CGColorSpaceCreateExtended(color_space);
// Apply the extended-range colorspace to your app
NSColorSpace* extended_ns_color_space
= [[NSColorSpace alloc] initWithCGColorSpace:color_space_extended];
view.window.colorSpace = extended_ns_color_space;
CGColorSpaceRelease(color_space_extended); EDR display change notifications via NSScreen
// Read static values
NSScreen* screen = window.screen;
double maxPotentialEDR = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
double maxReferenceEDR = screen.maximumReferenceExtendedDynamicRangeColorComponentValue;
// Register for dynamic EDR notifications
NSNotificationCenter* notification = [NSNotificationCenter defaultCenter];
[notification addObserver:self
selector:@selector(screenChangedEvent:)
name:NSApplicationDidChangeScreenParametersNotification
object:nil];
// Query for latest values
- (void)screenChangedEvent:(NSNotification *)notification {
double maxEDR = screen.maximumExtendedDynamicRangeColorComponentValue;
} CAEDRMetadata tone-mapper
// HLG
CAEDRMetadata* edrMetaData = [CAEDRMetadata HLGMetadata];
// HDR10
CAEDRMetadata* edrMetaData
= [CAEDRMetadata HDR10MetadataWithMinLuminance:minLuminance
maxLuminance:maxContentMasteringDisplayBrightness
opticalOutputScale:outputScale];
// Set on CAMetalLayer
metalLayer.EDRMetadata = edrMetaData; Computing your app’s brightest pixel
// Create the linear pixel we want to render
double EDRmaxComponents[4] = {EDRmax, EDRmax, EDRmax, 1.0};
CGColorSpaceRef linearColorSpace =
CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
CGColorRef EDRmaxColorLinear = CGColorCreate(linearColorSpace, EDRmaxComponents);
// Convert from linear to application’s colorspace
CGColorSpaceRef winColorSpace = [self.window.colorSpace CGColorSpace];
CGColorRef EDRmaxColor = CGColorCreateCopyByMatchingToColorSpace(winColorSpace,
kCGRenderingIntentDefault,
EDRmaxColorLinear,
NULL); Resources
Related sessions
-
21 min -
15 min -
22 min -
24 min -
23 min -
55 min -
49 min -
59 min -
57 min