Chromium Code Reviews

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

Issue 2778693003: MacOS: Add metal renderer and view. (Closed)
Patch Set: Address comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
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 "RTCMTLI420Renderer.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 #include "webrtc/api/video/video_rotation.h"
20
21 #define MTL_STRINGIFY(s) @ #s
22
23 // As defined in shaderSource.
24 static NSString *const vertexFunctionName = @"vertexPassthrough";
25 static NSString *const fragmentFunctionName = @"fragmentColorConversion";
26
27 static NSString *const pipelineDescriptorLabel = @"RTCPipeline";
28 static NSString *const commandBufferLabel = @"RTCCommandBuffer";
29 static NSString *const renderEncoderLabel = @"RTCEncoder";
30 static NSString *const renderEncoderDebugGroup = @"RTCDrawFrame";
31
32 static const float cubeVertexData[64] = {
33 -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,
34
35 // rotation = 90, offset = 16.
36 -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,
37
38 // rotation = 180, offset = 32.
39 -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,
40
41 // rotation = 270, offset = 48.
42 -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,
43 };
44
45 static inline int offsetForRotation(webrtc::VideoRotation rotation) {
46 switch (rotation) {
47 case webrtc::kVideoRotation_0:
48 return 0;
49 case webrtc::kVideoRotation_90:
50 return 16;
51 case webrtc::kVideoRotation_180:
52 return 32;
53 case webrtc::kVideoRotation_270:
54 return 48;
55 }
56 return 0;
57 }
58
59 static NSString *const shaderSource = MTL_STRINGIFY(
60 using namespace metal; typedef struct {
61 packed_float2 position;
62 packed_float2 texcoord;
63 } Vertex;
64
65 typedef struct {
66 float4 position[[position]];
67 float2 texcoord;
68 } Varyings;
69
70 vertex Varyings vertexPassthrough(device Vertex * verticies[[buffer(0)]],
71 unsigned int vid[[vertex_id]]) {
72 Varyings out;
73 device Vertex &v = verticies[vid];
74 out.position = float4(float2(v.position), 0.0, 1.0);
75 out.texcoord = v.texcoord;
76
77 return out;
78 }
79
80 fragment half4 fragmentColorConversion(
81 Varyings in[[stage_in]], texture2d<float, access::sample> textureY[[text ure(0)]],
82 texture2d<float, access::sample> textureU[[texture(1)]],
83 texture2d<float, access::sample> textureV[[texture(2)]]) {
84 constexpr sampler s(address::clamp_to_edge, filter::linear);
85 float y;
86 float u;
87 float v;
88 float r;
89 float g;
90 float b;
91 // Conversion for YUV to rgb from http://www.fourcc.org/fccyvrgb.php
92 y = textureY.sample(s, in.texcoord).r;
93 u = textureU.sample(s, in.texcoord).r;
94 v = textureV.sample(s, in.texcoord).r;
95 u = u - 0.5;
96 v = v - 0.5;
97 r = y + 1.403 * v;
98 g = y - 0.344 * u - 0.714 * v;
99 b = y + 1.770 * u;
100
101 float4 out = float4(r, g, b, 1.0);
102
103 return half4(out);
104 });
105
106 // The max number of command buffers in flight.
107 // For now setting it up to 1.
108 // In future we might use triple buffering method if it improves performance.
109
110 static const NSInteger kMaxInflightBuffers = 1;
111
112 @implementation RTCMTLI420Renderer {
113 __kindof MTKView *_view;
114
115 // Controller.
116 dispatch_semaphore_t _inflight_semaphore;
117
118 // Renderer.
119 id<MTLDevice> _device;
120 id<MTLCommandQueue> _commandQueue;
121 id<MTLLibrary> _defaultLibrary;
122 id<MTLRenderPipelineState> _pipelineState;
123
124 // Textures.
125 id<MTLTexture> _yTexture;
126 id<MTLTexture> _uTexture;
127 id<MTLTexture> _vTexture;
128
129 MTLTextureDescriptor *_descriptor;
130 MTLTextureDescriptor *_chromaDescriptor;
131
132 int _width;
133 int _height;
134 int _chromaWidth;
135 int _chromaHeight;
136
137 // Buffers.
138 id<MTLBuffer> _vertexBuffer;
139
140 // RTC Frame parameters.
141 int _offset;
142 }
143
144 - (instancetype)init {
145 if (self = [super init]) {
146 // Offset of 0 is equal to rotation of 0.
147 _offset = 0;
148 _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers);
149 }
150
151 return self;
152 }
153
154 - (BOOL)addRenderingDestination:(__kindof MTKView *)view {
155 return [self setupWithView:view];
156 }
157
158 #pragma mark - Private
159
160 - (BOOL)setupWithView:(__kindof MTKView *)view {
161 BOOL success = NO;
162 if ([self setupMetal]) {
163 [self setupView:view];
164 [self loadAssets];
165 [self setupBuffers];
166 success = YES;
167 }
168 return success;
169 }
170
171 #pragma mark - GPU methods
172
173 - (BOOL)setupMetal {
174 // Set the view to use the default device.
175 _device = MTLCreateSystemDefaultDevice();
176 if (!_device) {
177 return NO;
178 }
179
180 // Create a new command queue.
181 _commandQueue = [_device newCommandQueue];
182
183 // Load metal library from source.
184 NSError *libraryError = nil;
185
186 id<MTLLibrary> sourceLibrary =
187 [_device newLibraryWithSource:shaderSource options:NULL error:&libraryErro r];
188
189 if (libraryError) {
190 RTCLogError(@"Metal: Library with source failed\n%@", libraryError);
191 return NO;
192 }
193
194 if (!sourceLibrary) {
195 RTCLogError(@"Metal: Failed to load library. %@", libraryError);
196 return NO;
197 }
198 _defaultLibrary = sourceLibrary;
199
200 return YES;
201 }
202
203 - (void)setupView:(__kindof MTKView *)view {
204 view.device = _device;
205
206 view.preferredFramesPerSecond = 30;
207 view.autoResizeDrawable = NO;
208
209 // We need to keep reference to the view as it's needed down the rendering pip eline.
210 _view = view;
211 }
212
213 - (void)loadAssets {
214 id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:vertexFu nctionName];
215 id<MTLFunction> fragmentFunction = [_defaultLibrary newFunctionWithName:fragme ntFunctionName];
216
217 MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescripto r alloc] init];
218 pipelineDescriptor.label = pipelineDescriptorLabel;
219 pipelineDescriptor.vertexFunction = vertexFunction;
220 pipelineDescriptor.fragmentFunction = fragmentFunction;
221 pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat;
222 pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
223 NSError *error = nil;
224 _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescrip tor error:&error];
225
226 if (!_pipelineState) {
227 RTCLogError(@"Metal: Failed to create pipeline state. %@", error);
228 }
229 }
230
231 - (void)setupBuffers {
232 _vertexBuffer = [_device newBufferWithBytes:cubeVertexData
233 length:sizeof(cubeVertexData)
234 options:MTLStorageModeShared];
235 }
236
237 - (void)render {
238 dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
239
240 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
241 commandBuffer.label = commandBufferLabel;
242
243 __block dispatch_semaphore_t block_semaphore = _inflight_semaphore;
244 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
245 dispatch_semaphore_signal(block_semaphore);
246 }];
247
248 MTLRenderPassDescriptor *_renderPassDescriptor = _view.currentRenderPassDescri ptor;
249 if (_renderPassDescriptor) { // Valid drawable.
250 id<MTLRenderCommandEncoder> renderEncoder =
251 [commandBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor] ;
252 renderEncoder.label = renderEncoderLabel;
253
254 // Set context state.
255 [renderEncoder pushDebugGroup:renderEncoderDebugGroup];
256 [renderEncoder setRenderPipelineState:_pipelineState];
257 [renderEncoder setVertexBuffer:_vertexBuffer offset:_offset * sizeof(float) atIndex:0];
258 [renderEncoder setFragmentTexture:_yTexture atIndex:0];
259 [renderEncoder setFragmentTexture:_uTexture atIndex:1];
260 [renderEncoder setFragmentTexture:_vTexture atIndex:2];
261
262 [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
263 vertexStart:0
264 vertexCount:4
265 instanceCount:1];
266 [renderEncoder popDebugGroup];
267 [renderEncoder endEncoding];
268
269 [commandBuffer presentDrawable:_view.currentDrawable];
270 }
271
272 [commandBuffer commit];
273 }
274
275 #pragma mark - RTCMTLRenderer
276
277 - (void)drawFrame:(RTCVideoFrame *)frame {
278 if (!frame) {
279 return;
280 }
281 if ([self setupTexturesForFrame:frame]) {
282 @autoreleasepool {
283 [self render];
284 }
285 }
286 }
287
288 - (BOOL)setupTexturesForFrame:(nonnull RTCVideoFrame *)frame {
289 // Luma (y) texture.
290
291 if (!_descriptor || (_width != frame.width && _height != frame.height)) {
292 _width = frame.width;
293 _height = frame.height;
294 _descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPi xelFormatR8Unorm
295 width:_widt h
296 height:_heig ht
297 mipmapped:NO];
298 _descriptor.usage = MTLTextureUsageShaderRead;
299 _yTexture = [_device newTextureWithDescriptor:_descriptor];
300 }
301
302 // Chroma (u,v) textures
303 [_yTexture replaceRegion:MTLRegionMake2D(0, 0, _width, _height)
304 mipmapLevel:0
305 withBytes:frame.dataY
306 bytesPerRow:frame.strideY];
307
308 if (!_chromaDescriptor ||
309 (_chromaWidth != frame.width / 2 && _chromaHeight != frame.height / 2)) {
310 _chromaWidth = frame.width / 2;
311 _chromaHeight = frame.height / 2;
312 _chromaDescriptor =
313 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR 8Unorm
314 width:_chromaWidth
315 height:_chromaHeight
316 mipmapped:NO];
317 _chromaDescriptor.usage = MTLTextureUsageShaderRead;
318 _uTexture = [_device newTextureWithDescriptor:_chromaDescriptor];
319 _vTexture = [_device newTextureWithDescriptor:_chromaDescriptor];
320 }
321
322 [_uTexture replaceRegion:MTLRegionMake2D(0, 0, _chromaWidth, _chromaHeight)
323 mipmapLevel:0
324 withBytes:frame.dataU
325 bytesPerRow:frame.strideU];
326 [_vTexture replaceRegion:MTLRegionMake2D(0, 0, _chromaWidth, _chromaHeight)
327 mipmapLevel:0
328 withBytes:frame.dataV
329 bytesPerRow:frame.strideV];
330
331 _offset = offsetForRotation((webrtc::VideoRotation)frame.rotation);
332
333 return (_uTexture != nil) && (_yTexture != nil) && (_vTexture != nil);
334 }
335
336 @end
OLDNEW

Powered by Google App Engine