blob: 481a7fb6596857efdfec8ca9c667ee2acae1e027 [file] [log] [blame]
Peter Hanspers1c62b982018-05-03 14:06:04 +02001/*
2 * Copyright 2018 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 "RTCMTLRGBRenderer.h"
12
13#import <Metal/Metal.h>
14#import <MetalKit/MetalKit.h>
15
16#import "WebRTC/RTCLogging.h"
17#import "WebRTC/RTCVideoFrame.h"
18#import "WebRTC/RTCVideoFrameBuffer.h"
19
20#import "RTCMTLRenderer+Private.h"
21#include "rtc_base/checks.h"
22
23static NSString *const shaderSource = MTL_STRINGIFY(
24 using namespace metal;
25
26 typedef struct {
27 packed_float2 position;
28 packed_float2 texcoord;
29 } Vertex;
30
31 typedef struct {
32 float4 position[[position]];
33 float2 texcoord;
34 } VertexIO;
35
36 vertex VertexIO vertexPassthrough(device Vertex * verticies[[buffer(0)]],
37 uint vid[[vertex_id]]) {
38 VertexIO out;
39 device Vertex &v = verticies[vid];
40 out.position = float4(float2(v.position), 0.0, 1.0);
41 out.texcoord = v.texcoord;
42 return out;
43 }
44
45 fragment half4 fragmentColorConversion(
46 VertexIO in[[stage_in]], texture2d<half, access::sample> texture[[texture(0)]],
47 constant bool &isARGB[[buffer(0)]]) {
48 constexpr sampler s(address::clamp_to_edge, filter::linear);
49
50 half4 out = texture.sample(s, in.texcoord);
51 if (isARGB) {
52 out = half4(out.g, out.b, out.a, out.r);
53 }
54
55 return out;
56 });
57
58@implementation RTCMTLRGBRenderer {
59 // Textures.
60 CVMetalTextureCacheRef _textureCache;
61 id<MTLTexture> _texture;
62
63 // Uniforms.
64 id<MTLBuffer> _uniformsBuffer;
65}
66
67- (BOOL)addRenderingDestination:(__kindof MTKView *)view {
68 if ([super addRenderingDestination:view]) {
69 return [self initializeTextureCache];
70 }
71 return NO;
72}
73
74- (BOOL)initializeTextureCache {
75 CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice],
76 nil, &_textureCache);
77 if (status != kCVReturnSuccess) {
78 RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status);
79 return NO;
80 }
81
82 return YES;
83}
84
85- (NSString *)shaderSource {
86 return shaderSource;
87}
88
89- (BOOL)setupTexturesForFrame:(nonnull RTCVideoFrame *)frame {
90 RTC_DCHECK([frame.buffer isKindOfClass:[RTCCVPixelBuffer class]]);
Peter Hanspers5daaf7d2018-06-01 10:34:37 +020091 if (![super setupTexturesForFrame:frame]) {
92 return NO;
93 }
Peter Hanspers1c62b982018-05-03 14:06:04 +020094 CVPixelBufferRef pixelBuffer = ((RTCCVPixelBuffer *)frame.buffer).pixelBuffer;
95
96 id<MTLTexture> gpuTexture = nil;
97 CVMetalTextureRef textureOut = nullptr;
98 bool isARGB;
99
100 int width = CVPixelBufferGetWidth(pixelBuffer);
101 int height = CVPixelBufferGetHeight(pixelBuffer);
102 OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
103
104 MTLPixelFormat mtlPixelFormat;
105 if (pixelFormat == kCVPixelFormatType_32BGRA) {
106 mtlPixelFormat = MTLPixelFormatBGRA8Unorm;
107 isARGB = false;
108 } else if (pixelFormat == kCVPixelFormatType_32ARGB) {
109 mtlPixelFormat = MTLPixelFormatRGBA8Unorm;
110 isARGB = true;
111 } else {
112 RTC_NOTREACHED();
113 return NO;
114 }
115
116 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
117 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, mtlPixelFormat,
118 width, height, 0, &textureOut);
119 if (result == kCVReturnSuccess) {
120 gpuTexture = CVMetalTextureGetTexture(textureOut);
121 }
122 CVBufferRelease(textureOut);
123
124 if (gpuTexture != nil) {
125 _texture = gpuTexture;
JT Teha4888f02018-05-30 16:45:36 +0000126 _uniformsBuffer =
127 [[self currentMetalDevice] newBufferWithBytes:&isARGB
128 length:sizeof(isARGB)
129 options:MTLResourceCPUCacheModeDefaultCache];
Peter Hanspers1c62b982018-05-03 14:06:04 +0200130 return YES;
131 }
132
133 return NO;
134}
135
136- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
137 [renderEncoder setFragmentTexture:_texture atIndex:0];
138 [renderEncoder setFragmentBuffer:_uniformsBuffer offset:0 atIndex:0];
139}
140
141- (void)dealloc {
142 if (_textureCache) {
143 CFRelease(_textureCache);
144 }
145}
146
147@end