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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #import "RTCNV12Renderer.h"
12
13 #import <Metal/Metal.h>
14 #import <MetalKit/MetalKit.h>
15
16 #import "WebRTC/RTCLogging.h"
17 #import "WebRTC/RTCVideoFrame.h"
18
19 static const float cubeVertexData[64] = {
20 -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,
21
22 // rotation = 90, offset = 16.
23 -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,
24
25 // rotation = 180, offset = 32.
26 -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,
27
28 // rotation = 270, offset = 48.
29 -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,
30 };
31
32 static inline int offsetForRotation(int rotation) {
33 switch (rotation) {
34 case 0:
35 return 0;
36 case 90:
37 return 16;
38 case 180:
39 return 32;
40 case 270:
41 return 48;
42 default:
43 return 0;
44 }
45 }
46
47 // The max number of command buffers in flight.
48 // For now setting it up to 1.
49 // In future we might use tripple buffering method if it improves performance.
50
51 static const NSInteger kMaxInflightBuffers = 1;
52
53 @interface RTCNV12Renderer ()<MTKViewDelegate>
54 @end
55
56 @implementation RTCNV12Renderer {
57 __kindof MTKView *_view;
58
59 // Controller.
60 dispatch_semaphore_t _inflight_semaphore;
61
62 // Renderer.
63 id<MTLDevice> _device;
64 id<MTLCommandQueue> _commandQueue;
65 id<MTLLibrary> _defaultLibrary;
66 id<MTLRenderPipelineState> _pipelineState;
67
68 // Textures.
69 CVMetalTextureCacheRef textureCache;
70 id<MTLTexture> yTexture;
71 id<MTLTexture> CrCbTexture;
72
73 // Buffers.
74 id<MTLBuffer> _vertexBuffer;
75
76 // RTC Frame parameters.
77 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.
78 int offset;
79 }
80
81 - (instancetype)initWithView:(__kindof MTKView *)view {
82 if (self = [super init]) {
83 _view = view;
84
85 // Offset of 0 is equal to rotation of 0.
86 rotation = 0;
87 offset = 0;
88 _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers);
89 }
90
91 return self;
92 }
93
94 - (BOOL)setup {
95 BOOL success = NO;
96 if ([self _setupMetal] && _device) {
97 [self _setupView];
98 [self _loadAssets];
99 [self _setupBuffers];
100 [self initializeTextureCache];
101 success = YES;
102 }
103 return success;
104 }
105
106 - (BOOL)_setupMetal {
107 // Set the view to use the default device.
108 _device = MTLCreateSystemDefaultDevice();
109
110 // Create a new command queue.
111 _commandQueue = [_device newCommandQueue];
112
113 // Load all the shader files with a metal file extension in the project.
114 NSError *libraryError = nil;
115 NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@"org.webrtc.WebRTC "];
116 NSString *libraryFile = [frameworkBundle pathForResource:@"rtc_shaders" ofType :@"metallib"];
117 if (!libraryFile) {
118 RTCLog(
119 @"Metal Error: library not found in framework bundle with identifier org .webrtc.WebRTC.");
120 return NO;
121 }
122
123 _defaultLibrary = [_device newLibraryWithFile:libraryFile error:&libraryError] ;
124 if (!_defaultLibrary) {
125 RTCLog(@"Metal error: Failed to load library. %@", libraryError);
126 return NO;
127 }
128
129 return YES;
130 }
131
132 - (void)_setupView {
133 _view.device = _device;
134 _view.delegate = self;
135 // This is very important, should not be removed.
136 _view.contentMode = UIViewContentModeScaleAspectFit;
137
138 _view.preferredFramesPerSecond = 30;
139 _view.autoResizeDrawable = NO;
140 }
141
142 - (void)_loadAssets {
143 // As defined in Shaders.metal.
144 id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:@"vertex Passthrough"];
145 id<MTLFunction> fragmentFunction =
146 [_defaultLibrary newFunctionWithName:@"fragmentColorConversion"];
147
148 MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescripto r alloc] init];
149 pipelineDescriptor.label = @"Pipeline";
150 pipelineDescriptor.vertexFunction = vertexFunction;
151 pipelineDescriptor.fragmentFunction = fragmentFunction;
152 pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
153 pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
154 NSError *error = nil;
155 _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescrip tor error:&error];
156
157 if (!_pipelineState) {
158 RTCLog(@"Metal error: Failed to create pipeline state. %@", error);
159 }
160 }
161
162 - (void)_setupBuffers {
163 _vertexBuffer = [_device newBufferWithBytes:cubeVertexData
164 length:sizeof(cubeVertexData)
165 options:MTLResourceOptionCPUCacheModeDefau lt];
166 }
167
168 - (void)initializeTextureCache {
169 CVReturn status =
170 CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &texture Cache);
171 if (status != kCVReturnSuccess) {
172 RTCLog(@"Metal error: Failed to initialize metal texture cache. Return statu s is %d", status);
173 }
174 }
175
176 - (void)_render {
177 dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
178
179 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
180 commandBuffer.label = @"RTCCommandBuffer";
181
182 __block dispatch_semaphore_t block_semaphore = _inflight_semaphore;
183 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
184 dispatch_semaphore_signal(block_semaphore);
185 }];
186
187 MTLRenderPassDescriptor *_renderPassDescriptor = _view.currentRenderPassDescri ptor;
188 if (_renderPassDescriptor) { // valid drawable.
magjed_webrtc 2017/02/07 12:12:41 nit: Valid drawable.
daniela-webrtc 2017/02/07 13:36:16 Done.
189
190 id<MTLRenderCommandEncoder> renderEncoder =
191 [commandBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor] ;
192 renderEncoder.label = @"RTCEncoder";
193
194 // Set context state.
195 [renderEncoder pushDebugGroup:@"DrawFrame"];
196 [renderEncoder setRenderPipelineState:_pipelineState];
197 [renderEncoder setVertexBuffer:_vertexBuffer offset:sizeof(float[offset]) at Index: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.
198 [renderEncoder setFragmentTexture:yTexture atIndex:0];
199 [renderEncoder setFragmentTexture:CrCbTexture atIndex:1];
200
201 [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
202 vertexStart:0
203 vertexCount:4
204 instanceCount:1];
205 [renderEncoder popDebugGroup];
206 [renderEncoder endEncoding];
207
208 [commandBuffer presentDrawable:_view.currentDrawable];
209 }
210
211 [commandBuffer commit];
212 }
213
214 #pragma mark - MTKViewDelegate
215
216 - (void)drawInMTKView:(MTKView *)view {
217 if (!yTexture && !CrCbTexture) {
218 NSLog(@"No current frame. Aborting drawinFrame:");
219 return;
220 }
221 @autoreleasepool {
222 [self _render];
223 }
224 }
225
226 - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
227 }
228
229 #pragma mark - RTCVideoRenderer
230 - (void)setSize:(CGSize)size {
231 _view.drawableSize = size;
232 [_view draw];
233 }
234
235 - (void)renderFrame:(nullable RTCVideoFrame *)frame {
236 if (frame == NULL) {
237 return;
238 }
239 [self setupSyncVariablesForFrame:frame];
240 }
241
242 - (void)setupSyncVariablesForFrame:(nonnull RTCVideoFrame *)frame {
243 CVPixelBufferRef pixelBuffer = frame.nativeHandle;
244
245 id<MTLTexture> lumaTexture = nil;
246 id<MTLTexture> chromaTexture = nil;
247 CVMetalTextureRef outTexture;
248
249 // Luma (y) texture.
250 int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
251 int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
252
253 int indexPlane = 0;
254 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
255 kCFAllocatorDefault, textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm , lumaWidth,
256 lumaHeight, indexPlane, &outTexture);
257
258 if (result == kCVReturnSuccess) {
259 lumaTexture = CVMetalTextureGetTexture(outTexture);
260 }
261
262 CVBufferRelease(outTexture);
263 outTexture = nil;
264
265 // Chroma (CrCb) texture.
266 indexPlane = 1;
267 result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textur eCache, pixelBuffer,
268 nil, MTLPixelFormatRG8Unorm , lumaWidth / 2,
269 lumaHeight / 2, indexPlane, &outTexture);
270 if (result == kCVReturnSuccess) {
271 chromaTexture = CVMetalTextureGetTexture(outTexture);
272 }
273 CVBufferRelease(outTexture);
274
275 if (lumaTexture != nil && chromaTexture != nil) {
276 dispatch_async(dispatch_get_main_queue(), ^{
277 yTexture = lumaTexture;
278 CrCbTexture = chromaTexture;
279 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.
280 rotation = frame.rotation;
281 offset = offsetForRotation(rotation);
282 }
283 });
284 }
285 }
286
287 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698