blob: eb4c2ba10606755d97bd4b6441520d2604e7ce27 [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
Peter Hanspers1c62b982018-05-03 14:06:04 +020016#import "RTCMTLRenderer+Private.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020017#import "base/RTCLogging.h"
18#import "base/RTCVideoFrame.h"
19#import "base/RTCVideoFrameBuffer.h"
20#import "components/video_frame_buffer/RTCCVPixelBuffer.h"
21
Peter Hanspers1c62b982018-05-03 14:06:04 +020022#include "rtc_base/checks.h"
23
24static NSString *const shaderSource = MTL_STRINGIFY(
25 using namespace metal;
26
27 typedef struct {
28 packed_float2 position;
29 packed_float2 texcoord;
30 } Vertex;
31
32 typedef struct {
33 float4 position[[position]];
34 float2 texcoord;
35 } VertexIO;
36
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020037 vertex VertexIO vertexPassthrough(constant Vertex *verticies[[buffer(0)]],
Peter Hanspers1c62b982018-05-03 14:06:04 +020038 uint vid[[vertex_id]]) {
39 VertexIO out;
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020040 constant Vertex &v = verticies[vid];
Peter Hanspers1c62b982018-05-03 14:06:04 +020041 out.position = float4(float2(v.position), 0.0, 1.0);
42 out.texcoord = v.texcoord;
43 return out;
44 }
45
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020046 fragment half4 fragmentColorConversion(VertexIO in[[stage_in]],
47 texture2d<half, access::sample> texture[[texture(0)]],
Peter Hanspers1c62b982018-05-03 14:06:04 +020048 constant bool &isARGB[[buffer(0)]]) {
49 constexpr sampler s(address::clamp_to_edge, filter::linear);
50
51 half4 out = texture.sample(s, in.texcoord);
52 if (isARGB) {
53 out = half4(out.g, out.b, out.a, out.r);
54 }
55
56 return out;
57 });
58
59@implementation RTCMTLRGBRenderer {
60 // Textures.
61 CVMetalTextureCacheRef _textureCache;
62 id<MTLTexture> _texture;
63
64 // Uniforms.
65 id<MTLBuffer> _uniformsBuffer;
66}
67
68- (BOOL)addRenderingDestination:(__kindof MTKView *)view {
69 if ([super addRenderingDestination:view]) {
70 return [self initializeTextureCache];
71 }
72 return NO;
73}
74
75- (BOOL)initializeTextureCache {
76 CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice],
77 nil, &_textureCache);
78 if (status != kCVReturnSuccess) {
79 RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status);
80 return NO;
81 }
82
83 return YES;
84}
85
86- (NSString *)shaderSource {
87 return shaderSource;
88}
89
Peter Hanspers7af087a2018-06-12 14:14:48 +020090- (void)getWidth:(nonnull int *)width
91 height:(nonnull int *)height
92 cropWidth:(nonnull int *)cropWidth
93 cropHeight:(nonnull int *)cropHeight
94 cropX:(nonnull int *)cropX
95 cropY:(nonnull int *)cropY
96 ofFrame:(nonnull RTCVideoFrame *)frame {
97 RTCCVPixelBuffer *pixelBuffer = (RTCCVPixelBuffer *)frame.buffer;
98 *width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer);
99 *height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer);
100 *cropWidth = pixelBuffer.cropWidth;
101 *cropHeight = pixelBuffer.cropHeight;
102 *cropX = pixelBuffer.cropX;
103 *cropY = pixelBuffer.cropY;
104}
105
Peter Hanspers1c62b982018-05-03 14:06:04 +0200106- (BOOL)setupTexturesForFrame:(nonnull RTCVideoFrame *)frame {
107 RTC_DCHECK([frame.buffer isKindOfClass:[RTCCVPixelBuffer class]]);
Peter Hanspers5daaf7d2018-06-01 10:34:37 +0200108 if (![super setupTexturesForFrame:frame]) {
109 return NO;
110 }
Peter Hanspers1c62b982018-05-03 14:06:04 +0200111 CVPixelBufferRef pixelBuffer = ((RTCCVPixelBuffer *)frame.buffer).pixelBuffer;
112
113 id<MTLTexture> gpuTexture = nil;
114 CVMetalTextureRef textureOut = nullptr;
115 bool isARGB;
116
117 int width = CVPixelBufferGetWidth(pixelBuffer);
118 int height = CVPixelBufferGetHeight(pixelBuffer);
119 OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
120
121 MTLPixelFormat mtlPixelFormat;
122 if (pixelFormat == kCVPixelFormatType_32BGRA) {
123 mtlPixelFormat = MTLPixelFormatBGRA8Unorm;
124 isARGB = false;
125 } else if (pixelFormat == kCVPixelFormatType_32ARGB) {
126 mtlPixelFormat = MTLPixelFormatRGBA8Unorm;
127 isARGB = true;
128 } else {
129 RTC_NOTREACHED();
130 return NO;
131 }
132
133 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
134 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, mtlPixelFormat,
135 width, height, 0, &textureOut);
136 if (result == kCVReturnSuccess) {
137 gpuTexture = CVMetalTextureGetTexture(textureOut);
138 }
139 CVBufferRelease(textureOut);
140
141 if (gpuTexture != nil) {
142 _texture = gpuTexture;
JT Teha4888f02018-05-30 16:45:36 +0000143 _uniformsBuffer =
144 [[self currentMetalDevice] newBufferWithBytes:&isARGB
145 length:sizeof(isARGB)
146 options:MTLResourceCPUCacheModeDefaultCache];
Peter Hanspers1c62b982018-05-03 14:06:04 +0200147 return YES;
148 }
149
150 return NO;
151}
152
153- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
154 [renderEncoder setFragmentTexture:_texture atIndex:0];
155 [renderEncoder setFragmentBuffer:_uniformsBuffer offset:0 atIndex:0];
156}
157
158- (void)dealloc {
159 if (_textureCache) {
160 CFRelease(_textureCache);
161 }
162}
163
164@end