Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(827)

Unified Diff: webrtc/sdk/objc/Framework/Classes/metal/RTCNV12Renderer.mm

Issue 2651743007: Add metal view, shaders and renderer. (Closed)
Patch Set: Address comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/sdk/objc/Framework/Classes/metal/RTCNV12Renderer.mm
diff --git a/webrtc/sdk/objc/Framework/Classes/metal/RTCNV12Renderer.mm b/webrtc/sdk/objc/Framework/Classes/metal/RTCNV12Renderer.mm
new file mode 100644
index 0000000000000000000000000000000000000000..335883d2d14dce7127c9ba3322302d081b8b4f7a
--- /dev/null
+++ b/webrtc/sdk/objc/Framework/Classes/metal/RTCNV12Renderer.mm
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "RTCNV12Renderer.h"
+
+#import <Metal/Metal.h>
+#import <MetalKit/MetalKit.h>
+
+#import "WebRTC/RTCLogging.h"
+#import "WebRTC/RTCVideoFrame.h"
+
+static const float cubeVertexData[64] = {
+ -1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
+
+ // rotation = 90, offset = 16.
+ -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0,
+
+ // rotation = 180, offset = 32.
+ -1.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
+
+ // rotation = 270, offset = 48.
+ -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0,
+};
+
+static inline int offsetForRotation(int rotation) {
+ switch (rotation) {
+ case 0:
+ return 0;
+ case 90:
+ return 16;
+ case 180:
+ return 32;
+ case 270:
+ return 48;
+ default:
+ return 0;
+ }
+}
+
+// The max number of command buffers in flight.
+// For now setting it up to 1.
+// In future we might use tripple buffering method if it improves performance.
+
+static const NSInteger kMaxInflightBuffers = 1;
+
+@interface RTCNV12Renderer ()<MTKViewDelegate>
+@end
+
+@implementation RTCNV12Renderer {
+ __kindof MTKView *_view;
+
+ // Controller.
+ dispatch_semaphore_t _inflight_semaphore;
+
+ // Renderer.
+ id<MTLDevice> _device;
+ id<MTLCommandQueue> _commandQueue;
+ id<MTLLibrary> _defaultLibrary;
+ id<MTLRenderPipelineState> _pipelineState;
+
+ // Textures.
+ CVMetalTextureCacheRef textureCache;
+ id<MTLTexture> yTexture;
+ id<MTLTexture> CrCbTexture;
+
+ // Buffers.
+ id<MTLBuffer> _vertexBuffer;
+
+ // RTC Frame parameters.
+ int rotation;
magjed_webrtc 2017/02/07 12:12:41 There is no need to store |rotation|, just store o
daniela-webrtc 2017/02/07 13:36:16 Done.
+ int offset;
+}
+
+- (instancetype)initWithView:(__kindof MTKView *)view {
+ if (self = [super init]) {
+ _view = view;
+
+ // Offset of 0 is equal to rotation of 0.
+ rotation = 0;
+ offset = 0;
+ _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers);
+ }
+
+ return self;
+}
+
+- (BOOL)setup {
+ BOOL success = NO;
+ if ([self _setupMetal] && _device) {
+ [self _setupView];
+ [self _loadAssets];
+ [self _setupBuffers];
+ [self initializeTextureCache];
+ success = YES;
+ }
+ return success;
+}
+
+- (BOOL)_setupMetal {
+ // Set the view to use the default device.
+ _device = MTLCreateSystemDefaultDevice();
+
+ // Create a new command queue.
+ _commandQueue = [_device newCommandQueue];
+
+ // Load all the shader files with a metal file extension in the project.
+ NSError *libraryError = nil;
+ NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@"org.webrtc.WebRTC"];
+ NSString *libraryFile = [frameworkBundle pathForResource:@"rtc_shaders" ofType:@"metallib"];
+ if (!libraryFile) {
+ RTCLog(
+ @"Metal Error: library not found in framework bundle with identifier org.webrtc.WebRTC.");
+ return NO;
+ }
+
+ _defaultLibrary = [_device newLibraryWithFile:libraryFile error:&libraryError];
+ if (!_defaultLibrary) {
+ RTCLog(@"Metal error: Failed to load library. %@", libraryError);
+ return NO;
+ }
+
+ return YES;
+}
+
+- (void)_setupView {
+ _view.device = _device;
+ _view.delegate = self;
+ // This is very important, should not be removed.
+ _view.contentMode = UIViewContentModeScaleAspectFit;
+
+ _view.preferredFramesPerSecond = 30;
+ _view.autoResizeDrawable = NO;
+}
+
+- (void)_loadAssets {
+ // As defined in Shaders.metal.
+ id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:@"vertexPassthrough"];
+ id<MTLFunction> fragmentFunction =
+ [_defaultLibrary newFunctionWithName:@"fragmentColorConversion"];
+
+ MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
+ pipelineDescriptor.label = @"Pipeline";
+ pipelineDescriptor.vertexFunction = vertexFunction;
+ pipelineDescriptor.fragmentFunction = fragmentFunction;
+ pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
+ pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
+ NSError *error = nil;
+ _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
+
+ if (!_pipelineState) {
+ RTCLog(@"Metal error: Failed to create pipeline state. %@", error);
+ }
+}
+
+- (void)_setupBuffers {
+ _vertexBuffer = [_device newBufferWithBytes:cubeVertexData
+ length:sizeof(cubeVertexData)
+ options:MTLResourceOptionCPUCacheModeDefault];
+}
+
+- (void)initializeTextureCache {
+ CVReturn status =
+ CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &textureCache);
+ if (status != kCVReturnSuccess) {
+ RTCLog(@"Metal error: Failed to initialize metal texture cache. Return status is %d", status);
+ }
+}
+
+- (void)_render {
+ dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
+
+ id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
+ commandBuffer.label = @"RTCCommandBuffer";
+
+ __block dispatch_semaphore_t block_semaphore = _inflight_semaphore;
+ [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
+ dispatch_semaphore_signal(block_semaphore);
+ }];
+
+ MTLRenderPassDescriptor *_renderPassDescriptor = _view.currentRenderPassDescriptor;
+ if (_renderPassDescriptor) { // valid drawable.
magjed_webrtc 2017/02/07 12:12:41 nit: Valid drawable.
daniela-webrtc 2017/02/07 13:36:16 Done.
+
+ id<MTLRenderCommandEncoder> renderEncoder =
+ [commandBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor];
+ renderEncoder.label = @"RTCEncoder";
+
+ // Set context state.
+ [renderEncoder pushDebugGroup:@"DrawFrame"];
+ [renderEncoder setRenderPipelineState:_pipelineState];
+ [renderEncoder setVertexBuffer:_vertexBuffer offset:sizeof(float[offset]) atIndex:0];
magjed_webrtc 2017/02/07 12:12:41 nit: I haven't seen that way of using sizeof. I'm
daniela-webrtc 2017/02/07 13:36:16 Done.
+ [renderEncoder setFragmentTexture:yTexture atIndex:0];
+ [renderEncoder setFragmentTexture:CrCbTexture atIndex:1];
+
+ [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
+ vertexStart:0
+ vertexCount:4
+ instanceCount:1];
+ [renderEncoder popDebugGroup];
+ [renderEncoder endEncoding];
+
+ [commandBuffer presentDrawable:_view.currentDrawable];
+ }
+
+ [commandBuffer commit];
+}
+
+#pragma mark - MTKViewDelegate
+
+- (void)drawInMTKView:(MTKView *)view {
+ if (!yTexture && !CrCbTexture) {
+ NSLog(@"No current frame. Aborting drawinFrame:");
+ return;
+ }
+ @autoreleasepool {
+ [self _render];
+ }
+}
+
+- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
+}
+
+#pragma mark - RTCVideoRenderer
+- (void)setSize:(CGSize)size {
+ _view.drawableSize = size;
+ [_view draw];
+}
+
+- (void)renderFrame:(nullable RTCVideoFrame *)frame {
+ if (frame == NULL) {
+ return;
+ }
+ [self setupSyncVariablesForFrame:frame];
+}
+
+- (void)setupSyncVariablesForFrame:(nonnull RTCVideoFrame *)frame {
+ CVPixelBufferRef pixelBuffer = frame.nativeHandle;
+
+ id<MTLTexture> lumaTexture = nil;
+ id<MTLTexture> chromaTexture = nil;
+ CVMetalTextureRef outTexture;
+
+ // Luma (y) texture.
+ int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+
+ int indexPlane = 0;
+ CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault, textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, lumaWidth,
+ lumaHeight, indexPlane, &outTexture);
+
+ if (result == kCVReturnSuccess) {
+ lumaTexture = CVMetalTextureGetTexture(outTexture);
+ }
+
+ CVBufferRelease(outTexture);
+ outTexture = nil;
+
+ // Chroma (CrCb) texture.
+ indexPlane = 1;
+ result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer,
+ nil, MTLPixelFormatRG8Unorm, lumaWidth / 2,
+ lumaHeight / 2, indexPlane, &outTexture);
+ if (result == kCVReturnSuccess) {
+ chromaTexture = CVMetalTextureGetTexture(outTexture);
+ }
+ CVBufferRelease(outTexture);
+
+ if (lumaTexture != nil && chromaTexture != nil) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ yTexture = lumaTexture;
+ CrCbTexture = chromaTexture;
+ if (rotation != frame.rotation) {
magjed_webrtc 2017/02/07 12:12:41 Just do an unconditional offset = offsetForRotatio
daniela-webrtc 2017/02/07 13:36:16 Done.
+ rotation = frame.rotation;
+ offset = offsetForRotation(rotation);
+ }
+ });
+ }
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698