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

Side by Side Diff: webrtc/sdk/objc/Framework/Classes/Metal/RTCNV12Renderer.mm

Issue 2651743007: Add metal view, shaders and renderer. (Closed)
Patch Set: Add whitespace that was removed with format 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/RTCMTLVideoView.h>
17 #import "WebRTC/RTCLogging.h"
18 #import "WebRTC/RTCVideoFrame.h"
19
20 #include "webrtc/api/video/video_rotation.h"
21
22
23 #define MTL_STRINGIFY(s) @#s
24
25 static const float cubeVertexData[64] = {
26 -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,
27
28 // rotation = 90, offset = 16.
29 -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,
30
31 // rotation = 180, offset = 32.
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,
33
34 // rotation = 270, offset = 48.
35 -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,
36 };
37
38 static inline int offsetForRotation(webrtc::VideoRotation rotation) {
39 switch (rotation) {
40 case webrtc::kVideoRotation_0:
41 return 0;
42 case webrtc::kVideoRotation_90:
43 return 16;
44 case webrtc::kVideoRotation_180:
45 return 32;
46 case webrtc::kVideoRotation_270:
47 return 48;
48 }
49 return 0;
50 }
51
52 static NSString *const shaderSource = MTL_STRINGIFY(
53 using namespace metal; typedef struct {
54 packed_float2 position;
55 packed_float2 texcoord;
56 } Vertex;
57
58 typedef struct {
59 float4 position[[position]];
60 float2 texcoord;
61 } Varyings;
62
63 vertex Varyings vertexPassthrough(device Vertex * verticies[[buffer(0)]],
64 unsigned int vid[[vertex_id]]) {
65 Varyings out;
66 device Vertex &v = verticies[vid];
67 out.position = float4(float2(v.position), 0.0, 1.0);
68 out.texcoord = v.texcoord;
69
70 return out;
71 }
72
73 fragment half4 fragmentColorConversion(
74 Varyings in[[stage_in]], texture2d<float, access::sample> textureY[[text ure(0)]],
75 texture2d<float, access::sample> textureCbCr[[texture(1)]]) {
76 constexpr sampler s(address::clamp_to_edge, filter::linear);
77 float y;
78 float2 uv;
79 y = textureY.sample(s, in.texcoord).r;
80 uv = textureCbCr.sample(s, in.texcoord).rg - float2(0.5, 0.5);
81
82 float4 out = float4(y + 1.403 * uv.y, y - 0.344 * uv.x - 0.714 * uv.y, y + 1.770 * uv.x, 1.0);
83
84 return half4(out);
85 });
86
87 // The max number of command buffers in flight.
88 // For now setting it up to 1.
89 // In future we might use triple buffering method if it improves performance.
90
91 static const NSInteger kMaxInflightBuffers = 1;
92
93 @interface RTCNV12Renderer ()
94 @end
95
96 @implementation RTCNV12Renderer {
97 __kindof MTKView *_view;
98
99 // Controller.
100 dispatch_semaphore_t _inflight_semaphore;
101
102 // Renderer.
103 id<MTLDevice> _device;
104 id<MTLCommandQueue> _commandQueue;
105 id<MTLLibrary> _defaultLibrary;
106 id<MTLRenderPipelineState> _pipelineState;
107
108 // Textures.
109 CVMetalTextureCacheRef textureCache;
110 id<MTLTexture> yTexture;
111 id<MTLTexture> CrCbTexture;
tkchin_webrtc 2017/02/16 23:28:04 nit _textureCache, _yTexture: _crCbTexture
daniela-webrtc 2017/02/20 15:17:08 Done.
112
113 // Buffers.
114 id<MTLBuffer> _vertexBuffer;
115
116 // RTC Frame parameters.
117 int offset;
118 }
119
120 - (instancetype)init {
121 if (self = [super init]) {
122 // Offset of 0 is equal to rotation of 0.
123 offset = 0;
124 _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers);
125 }
126
127 return self;
128 }
129
130 - (BOOL)addRenderingDestination:(__kindof MTKView *)view {
131 return [self setupWithView:view];
132 }
133
134 #pragma mark - Private
135
136 - (BOOL)setupWithView:(__kindof MTKView *)view {
137 BOOL success = NO;
138 if ([self setupMetal]) {
139 [self setupView:view];
140 [self loadAssets];
141 [self setupBuffers];
142 [self initializeTextureCache];
143 success = YES;
144 }
145 return success;
146 }
147
148 #pragma mark - GPU methods
149
150 - (BOOL)setupMetal {
151 // Set the view to use the default device.
152 _device = MTLCreateSystemDefaultDevice();
153 if (!_device) {
154 return NO;
155 }
156
157 // Create a new command queue.
158 _commandQueue = [_device newCommandQueue];
159
160 // Load metal library from source.
161 NSError *libraryError = nil;
162
163 id<MTLLibrary> sourceLibrary =
164 [_device newLibraryWithSource:shaderSource options:NULL error:&libraryErro r];
165
166 if (libraryError) {
167 RTCLog(@"Metal Error: Library with source failed\n%@", libraryError);
tkchin_webrtc 2017/02/16 23:28:04 RTCLogError for errors. Here and elsewhere.
daniela-webrtc 2017/02/20 15:17:07 Done.
168 return NO;
169 }
170
171 if (!sourceLibrary) {
172 RTCLog(@"Metal error: Failed to load library. %@", libraryError);
173 return NO;
174 }
175 _defaultLibrary = sourceLibrary;
176
177 return YES;
178 }
179
180 - (void)setupView:(__kindof MTKView *)view {
181 view.device = _device;
182
183 view.preferredFramesPerSecond = 30;
184 view.autoResizeDrawable = NO;
185
186 // We need to keep reference to the view as it's needed down the rendering pip eline.
187 _view = view;
188 }
189
190 - (void)loadAssets {
191 // As defined in shaderSource.
192 id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:@"vertex Passthrough"];
193 id<MTLFunction> fragmentFunction =
194 [_defaultLibrary newFunctionWithName:@"fragmentColorConversion"];
195
196 MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescripto r alloc] init];
197 pipelineDescriptor.label = @"Pipeline";
198 pipelineDescriptor.vertexFunction = vertexFunction;
199 pipelineDescriptor.fragmentFunction = fragmentFunction;
200 pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
201 pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
202 NSError *error = nil;
203 _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescrip tor error:&error];
204
205 if (!_pipelineState) {
206 RTCLog(@"Metal error: Failed to create pipeline state. %@", error);
207 }
208 }
209
210 - (void)setupBuffers {
211 _vertexBuffer = [_device newBufferWithBytes:cubeVertexData
212 length:sizeof(cubeVertexData)
213 options:MTLResourceOptionCPUCacheModeDefau lt];
214 }
215
216 - (void)initializeTextureCache {
217 CVReturn status =
218 CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &texture Cache);
219 if (status != kCVReturnSuccess) {
220 RTCLog(@"Metal error: Failed to initialize metal texture cache. Return statu s is %d", status);
221 }
222 }
223
224 - (void)render {
225 dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
226
227 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
228 commandBuffer.label = @"RTCCommandBuffer";
tkchin_webrtc 2017/02/16 23:28:04 stuff these constants into globals at the top of t
daniela-webrtc 2017/02/20 15:17:07 Done.
229
230 __block dispatch_semaphore_t block_semaphore = _inflight_semaphore;
231 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
232 dispatch_semaphore_signal(block_semaphore);
233 }];
234
235 MTLRenderPassDescriptor *_renderPassDescriptor = _view.currentRenderPassDescri ptor;
236 if (_renderPassDescriptor) { // Valid drawable.
237 id<MTLRenderCommandEncoder> renderEncoder =
238 [commandBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor] ;
239 renderEncoder.label = @"RTCEncoder";
240
241 // Set context state.
242 [renderEncoder pushDebugGroup:@"DrawFrame"];
243 [renderEncoder setRenderPipelineState:_pipelineState];
244 [renderEncoder setVertexBuffer:_vertexBuffer offset:offset * sizeof(float) a tIndex:0];
245 [renderEncoder setFragmentTexture:yTexture atIndex:0];
246 [renderEncoder setFragmentTexture:CrCbTexture atIndex:1];
247
248 [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
249 vertexStart:0
250 vertexCount:4
251 instanceCount:1];
252 [renderEncoder popDebugGroup];
253 [renderEncoder endEncoding];
254
255 [commandBuffer presentDrawable:_view.currentDrawable];
256 }
257
258 [commandBuffer commit];
259 }
260
261 #pragma mark - RTCMTLRenderer
262
263 - (void)draw {
264 if (!yTexture && !CrCbTexture) {
265 NSLog(@"No current frame. Aborting drawinFrame:");
tkchin_webrtc 2017/02/16 23:28:04 RTCLog, don't use NSLog (otherwise won't show up i
daniela-webrtc 2017/02/20 15:17:07 Done.
266 return;
267 }
268 @autoreleasepool {
tkchin_webrtc 2017/02/16 23:28:04 why is an autoreleasepool required here?
269 [self render];
270 }
271 }
272
273 #pragma mark - RTCVideoRenderer
274
275 - (void)setSize:(CGSize)size {
tkchin_webrtc 2017/02/16 23:28:04 I think this is a legacy API fyi. It'd be nice to
276 _view.drawableSize = size;
277 [_view draw];
278 }
279
280 - (void)renderFrame:(nullable RTCVideoFrame *)frame {
281 if (frame == NULL) {
tkchin_webrtc 2017/02/16 23:28:04 use nil instead of NULL for non-objC object point
daniela-webrtc 2017/02/20 15:17:07 Done.
282 return;
283 }
284 [self setupSyncVariablesForFrame:frame];
285 }
286
287 - (void)setupSyncVariablesForFrame:(nonnull RTCVideoFrame *)frame {
288 CVPixelBufferRef pixelBuffer = frame.nativeHandle;
289
290 id<MTLTexture> lumaTexture = nil;
291 id<MTLTexture> chromaTexture = nil;
292 CVMetalTextureRef outTexture;
tkchin_webrtc 2017/02/16 23:28:04 nit: initialize to nullptr
daniela-webrtc 2017/02/20 15:17:07 Done.
293
294 // Luma (y) texture.
295 int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
296 int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
297
298 int indexPlane = 0;
299 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
300 kCFAllocatorDefault, textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm , lumaWidth,
301 lumaHeight, indexPlane, &outTexture);
302
303 if (result == kCVReturnSuccess) {
304 lumaTexture = CVMetalTextureGetTexture(outTexture);
305 }
306
307 CVBufferRelease(outTexture);
tkchin_webrtc 2017/02/16 23:28:04 nit: consider using a guard anyway to make the cod
daniela-webrtc 2017/02/20 15:17:07 Added code comment to clarify.
308 outTexture = nil;
tkchin_webrtc 2017/02/16 23:28:04 nullptr for refs
daniela-webrtc 2017/02/20 15:17:07 Done.
309
310 // Chroma (CrCb) texture.
311 indexPlane = 1;
312 result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textur eCache, pixelBuffer,
313 nil, MTLPixelFormatRG8Unorm , lumaWidth / 2,
314 lumaHeight / 2, indexPlane, &outTexture);
315 if (result == kCVReturnSuccess) {
316 chromaTexture = CVMetalTextureGetTexture(outTexture);
317 }
318 CVBufferRelease(outTexture);
319
320 if (lumaTexture != nil && chromaTexture != nil) {
321 dispatch_async(dispatch_get_main_queue(), ^{
tkchin_webrtc 2017/02/16 23:28:04 seems expensive to dispatch at 30fps since this is
322 yTexture = lumaTexture;
323 CrCbTexture = chromaTexture;
324 offset = offsetForRotation((webrtc::VideoRotation)frame.rotation);
325 });
326 }
327 }
328
329 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698