OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2016 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 "WebRTC/RTCMTLRenderer.h" | |
12 | |
13 #import <Metal/Metal.h> | |
14 #import <MetalKit/MetalKit.h> | |
15 | |
16 #import "WebRTC/RTCLogging.h" | |
17 | |
18 float cubeVertexData[64] = | |
kthelgason
2017/01/24 14:17:59
static const
daniela-webrtc
2017/01/25 08:46:19
Done.
| |
19 { | |
20 -1.0, -1.0, 0.0, 1.0, | |
21 1.0, -1.0, 1.0, 1.0, | |
22 -1.0, 1.0, 0.0, 0.0, | |
23 1.0, 1.0, 1.0, 0.0, | |
24 | |
25 // rotation = 90, offset = 16. | |
26 -1.0, -1.0, 1.0, 1.0, | |
27 1.0, -1.0, 1.0, 0.0, | |
28 -1.0, 1.0, 0.0, 1.0, | |
29 1.0, 1.0, 0.0, 0.0, | |
30 | |
31 // rotation = 180, offset = 32. | |
32 -1.0, -1.0, 1.0, 0.0, | |
33 1.0, -1.0, 0.0, 0.0, | |
34 -1.0, 1.0, 1.0, 1.0, | |
35 1.0, 1.0, 0.0, 1.0, | |
36 | |
37 // rotation = 270, offset = 48. | |
38 -1.0, -1.0, 0.0, 0.0, | |
39 1.0, -1.0, 0.0, 1.0, | |
40 -1.0, 1.0, 1.0, 0.0, | |
41 1.0, 1.0, 1.0, 1.0, | |
42 }; | |
43 | |
44 static inline int offsetForRotation(int rotation) { | |
45 switch (rotation) { | |
46 case 0: | |
47 return 0; | |
48 break; | |
49 case 90: | |
50 return 16; | |
51 break; | |
52 case 180: | |
53 return 32; | |
54 break; | |
55 case 270: | |
56 return 48; | |
57 break; | |
58 default: | |
59 return 0; | |
60 break; | |
61 } | |
62 } | |
63 | |
64 // The max number of command buffers in flight. | |
65 // For now setting it up to 1. | |
66 // In future we might opt in to use tripple buffering method if it improves perf ormance. | |
kthelgason
2017/01/24 14:18:00
I think this sentence could be made more concise:
daniela-webrtc
2017/01/25 08:46:19
Done.
| |
67 | |
68 static const NSInteger kMaxInflightBuffers = 1; | |
69 | |
70 @interface RTCMTLRenderer ()<MTKViewDelegate> | |
71 @end | |
72 | |
73 @implementation RTCMTLRenderer { | |
74 __kindof MTKView *_view; | |
75 | |
76 // Controller. | |
77 dispatch_semaphore_t _inflight_semaphore; | |
78 | |
79 // Renderer. | |
80 id<MTLDevice> _device; | |
81 id<MTLCommandQueue> _commandQueue; | |
82 id<MTLLibrary> _defaultLibrary; | |
83 id<MTLRenderPipelineState> _pipelineState; | |
84 | |
85 // Textures. | |
86 CVMetalTextureCacheRef textureCache; | |
87 id<MTLTexture> yTexture; | |
88 id<MTLTexture> CrCbTexture; | |
89 | |
90 // Buffers. | |
91 id<MTLBuffer> _vertexBuffer; | |
92 | |
93 // RTC Frame parameters. | |
94 int rotation; | |
95 int offset; | |
96 } | |
97 | |
98 - (instancetype)initWithView:(__kindof MTKView *)view { | |
99 if (self = [super init]) { | |
100 _view = view; | |
101 | |
102 // Offset of 0 is equal to rotation of 0. | |
103 rotation = 0; | |
104 offset = 0; | |
105 _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers); | |
106 } | |
107 | |
108 return self; | |
109 } | |
110 | |
111 - (BOOL)setup { | |
112 BOOL success = NO; | |
113 if ([self _setupMetal] && _device) { | |
114 [self _setupView]; | |
115 [self _loadAssets]; | |
116 [self _setupBuffers]; | |
117 [self initializeTextureCache]; | |
118 success = YES; | |
119 } | |
120 return success; | |
121 } | |
122 | |
123 - (BOOL)_setupMetal { | |
kthelgason
2017/01/24 14:17:59
We haven't really been doing the _methodName. For
daniela-webrtc
2017/01/25 08:46:19
Yes it's not common. I saw this in Apple's Metal
| |
124 // Set the view to use the default device. | |
125 _device = MTLCreateSystemDefaultDevice(); | |
126 | |
127 // Create a new command queue. | |
128 _commandQueue = [_device newCommandQueue]; | |
129 | |
130 // Load all the shader files with a metal file extension in the project. | |
131 NSError *libraryError = nil; | |
132 NSString *libraryFile = [[NSBundle mainBundle] pathForResource:@"rtc_shaders" ofType:@"metallib"]; | |
133 if (!libraryFile) { | |
134 RTCLog(@"Metal Error: library not found in main bundle."); | |
135 return NO; | |
136 } | |
137 | |
138 _defaultLibrary = [_device newLibraryWithFile:libraryFile error:&libraryError] ; | |
139 if (!_defaultLibrary) { | |
140 RTCLog(@"Metal error: Failed to load library. %@", libraryError); | |
141 return NO; | |
142 } | |
143 | |
144 return YES; | |
145 } | |
146 | |
147 - (void)_setupView { | |
148 _view.device = _device; | |
149 _view.delegate = self; | |
150 // VERY VERY MUCHISIMO IMPORTANTE. | |
kthelgason
2017/01/24 14:18:00
Maybe a less flamboyant comment? :D although I do
daniela-webrtc
2017/01/25 08:46:19
Ooops! This one slipped :D was meant to be part of
| |
151 _view.contentMode = UIViewContentModeScaleAspectFit; | |
152 | |
153 _view.preferredFramesPerSecond = 30; | |
154 _view.autoResizeDrawable = NO; | |
155 } | |
156 | |
157 - (void)_loadAssets { | |
158 // As defined in Shaders.metal. | |
159 id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:@"vertex Passthrough"]; | |
160 id<MTLFunction> fragmentFunction = | |
161 [_defaultLibrary newFunctionWithName:@"fragmentColorConversion"]; | |
162 | |
163 MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescripto r alloc] init]; | |
164 pipelineDescriptor.label = @"Pipeline"; | |
165 pipelineDescriptor.vertexFunction = vertexFunction; | |
166 pipelineDescriptor.fragmentFunction = fragmentFunction; | |
167 pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat; | |
168 pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid; | |
169 NSError *error = nil; | |
170 _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescrip tor error:&error]; | |
171 | |
172 if (!_pipelineState) { | |
173 RTCLog(@"Metal error: Failed to create pipeline state. %@", error); | |
174 } | |
175 } | |
176 | |
177 - (void)_setupBuffers { | |
178 _vertexBuffer = [_device newBufferWithBytes:cubeVertexData | |
179 length:sizeof(cubeVertexData) | |
180 options:MTLResourceOptionCPUCacheModeDefau lt]; | |
181 } | |
182 | |
183 - (void)initializeTextureCache { | |
184 CVReturn status = | |
185 CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &texture Cache); | |
186 if (status != kCVReturnSuccess) { | |
187 RTCLog(@"Metal error: Falied to initialize metal texture cache. Return statu s is %d", status); | |
magjed_webrtc
2017/01/24 14:01:56
spelling nit: Failed
daniela-webrtc
2017/01/25 08:46:19
Done.
| |
188 } | |
189 } | |
190 | |
191 - (void)_render { | |
192 dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER); | |
193 | |
194 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; | |
195 commandBuffer.label = @"RTCCommandBuffer"; | |
196 | |
197 __block dispatch_semaphore_t block_semaphore = _inflight_semaphore; | |
198 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) { | |
199 dispatch_semaphore_signal(block_semaphore); | |
200 }]; | |
201 | |
202 MTLRenderPassDescriptor *_renderPassDescriptor = _view.currentRenderPassDescri ptor; | |
203 if (_renderPassDescriptor) { // valid drawable. | |
204 | |
205 id<MTLRenderCommandEncoder> renderEncoder = | |
206 [commandBuffer renderCommandEncoderWithDescriptor:_renderPassDescriptor] ; | |
207 renderEncoder.label = @"RTCEncoder"; | |
208 | |
209 // Set context state. | |
210 [renderEncoder pushDebugGroup:@"DrawFrame"]; | |
211 [renderEncoder setRenderPipelineState:_pipelineState]; | |
212 [renderEncoder setVertexBuffer:_vertexBuffer offset:sizeof(float[offset]) at Index:0]; | |
213 [renderEncoder setFragmentTexture:yTexture atIndex:0]; | |
214 [renderEncoder setFragmentTexture:CrCbTexture atIndex:1]; | |
215 | |
216 [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip | |
217 vertexStart:0 | |
218 vertexCount:4 | |
219 instanceCount:1]; | |
220 [renderEncoder popDebugGroup]; | |
221 [renderEncoder endEncoding]; | |
222 | |
223 [commandBuffer presentDrawable:_view.currentDrawable]; | |
224 } | |
225 | |
226 [commandBuffer commit]; | |
227 } | |
228 | |
229 #pragma mark - MTKViewDelegate | |
230 | |
231 - (void)drawInMTKView:(MTKView *)view { | |
232 if (!yTexture && !CrCbTexture) { | |
233 NSLog(@"No current frame. Aborting drawinFrame:"); | |
234 return; | |
235 } | |
236 @autoreleasepool { | |
237 [self _render]; | |
238 } | |
239 } | |
240 | |
241 - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { | |
242 } | |
243 | |
244 #pragma mark - RTCVideoRenderer | |
245 - (void)setSize:(CGSize)size { | |
246 _view.drawableSize = size; | |
247 [_view draw]; | |
248 } | |
249 | |
250 - (void)renderFrame:(nullable RTCVideoFrame *)frame { | |
251 if (frame == NULL) { | |
252 return; | |
253 } | |
254 [self setupSyncVariablesForFrame:frame]; | |
kthelgason
2017/01/24 14:18:00
I don't see the point of extracting this into a me
daniela-webrtc
2017/01/25 08:46:19
It can be inlined but it was my personal preferenc
| |
255 } | |
256 | |
257 - (void)setupSyncVariablesForFrame:(nonnull RTCVideoFrame *)frame { | |
258 CVPixelBufferRef pixelBuffer = frame.nativeHandle; | |
259 | |
260 id<MTLTexture> lumaTexture = nil; | |
261 id<MTLTexture> chromaTexture = nil; | |
262 CVMetalTextureRef outTexture; | |
263 | |
264 // Luma (y) texture. | |
265 int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); | |
266 int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); | |
267 | |
268 CVReturn result = CVMetalTextureCacheCreateTextureFromImage( | |
269 kCFAllocatorDefault, textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm , lumaWidth, | |
270 lumaHeight, 0, &outTexture); | |
magjed_webrtc
2017/01/24 14:01:56
Maybe add a comment for the literal 0: /* indexPla
daniela-webrtc
2017/01/25 08:46:19
Good idea. I've actually added a separate variable
| |
271 | |
272 if (result == kCVReturnSuccess) { | |
273 lumaTexture = CVMetalTextureGetTexture(outTexture); | |
274 } | |
275 | |
276 CVBufferRelease(outTexture); | |
magjed_webrtc
2017/01/24 14:01:56
Is it ok to release |outTexture| here and keep usi
daniela-webrtc
2017/01/25 08:46:19
Not sure I understand the question. Are you asking
| |
277 outTexture = nil; | |
278 | |
279 // Chroma (CrCb) texture. | |
280 result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textur eCache, pixelBuffer, | |
281 nil, MTLPixelFormatRG8Unorm , lumaWidth / 2, | |
282 lumaHeight / 2, 1, &outText ure); | |
magjed_webrtc
2017/01/24 14:01:56
ditto: Maybe add a comment for the literal 1: /* i
daniela-webrtc
2017/01/25 08:46:19
^
| |
283 if (result == kCVReturnSuccess) { | |
284 chromaTexture = CVMetalTextureGetTexture(outTexture); | |
285 } | |
286 CVBufferRelease(outTexture); | |
287 | |
288 if (lumaTexture != nil && chromaTexture != nil) { | |
289 dispatch_async(dispatch_get_main_queue(), ^{ | |
290 yTexture = lumaTexture; | |
291 CrCbTexture = chromaTexture; | |
292 if (rotation != frame.rotation) { | |
293 rotation = frame.rotation; | |
294 offset = offsetForRotation(rotation); | |
295 } | |
296 }); | |
297 } | |
298 } | |
299 | |
300 - (nullable id<MTLTexture>)transformFrame:(RTCVideoFrame *)videoFrame | |
magjed_webrtc
2017/01/24 14:01:56
Is this function used?
daniela-webrtc
2017/01/25 08:46:19
Great catch. It was part of a previous implementat
| |
301 toTextureIndexPlane:(int)indexPlane { | |
302 CVPixelBufferRef pixelBuffer = videoFrame.nativeHandle; | |
303 | |
304 id<MTLTexture> mtlTexture = nil; | |
305 int width = CVPixelBufferGetWidthOfPlane(pixelBuffer, indexPlane); | |
306 int height = CVPixelBufferGetHeightOfPlane(pixelBuffer, indexPlane); | |
307 | |
308 CVMetalTextureRef texture; | |
309 | |
310 MTLPixelFormat format = MTLPixelFormatRG8Unorm; | |
311 if (indexPlane == 0) { | |
312 format = MTLPixelFormatR8Unorm; | |
313 } | |
314 CVReturn status = | |
315 CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCach e, pixelBuffer, nil, | |
316 format, width, height, indexPlan e, &texture); | |
317 if (status != kCVReturnSuccess) { | |
318 RTCLog(@"Metal errror: Failed to create metal texture for plane index %d", i ndexPlane); | |
319 return nil; | |
320 } | |
321 | |
322 mtlTexture = CVMetalTextureGetTexture(texture); | |
323 | |
324 // Same like CFRelease except it can be passed NULL without crashing. | |
325 CVBufferRelease(texture); | |
326 return mtlTexture; | |
327 } | |
328 @end | |
OLD | NEW |