Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Code shown on screen · 8 snippets

AVPlayer automatically uses EDR with HDR content objectivec · at 16:00 ↗
// 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 objectivec · at 17:52 ↗
// 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 objectivec · at 18:53 ↗
// 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 objectivec · at 20:30 ↗
// 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 objectivec · at 21:46 ↗
// 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 objectivec · at 29:00 ↗
// 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 objectivec · at 30:28 ↗
// 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 objectivec · at 31:35 ↗
// 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