blob: e7358793b9e568a872fc50347d4dea67176f1b74 [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]]);
JT Teha4888f02018-05-30 16:45:36 +000091 [super setupTexturesForFrame:frame];
Peter Hanspers1c62b982018-05-03 14:06:04 +020092 CVPixelBufferRef pixelBuffer = ((RTCCVPixelBuffer *)frame.buffer).pixelBuffer;
93
94 id<MTLTexture> gpuTexture = nil;
95 CVMetalTextureRef textureOut = nullptr;
96 bool isARGB;
97
98 int width = CVPixelBufferGetWidth(pixelBuffer);
99 int height = CVPixelBufferGetHeight(pixelBuffer);
100 OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
101
102 MTLPixelFormat mtlPixelFormat;
103 if (pixelFormat == kCVPixelFormatType_32BGRA) {
104 mtlPixelFormat = MTLPixelFormatBGRA8Unorm;
105 isARGB = false;
106 } else if (pixelFormat == kCVPixelFormatType_32ARGB) {
107 mtlPixelFormat = MTLPixelFormatRGBA8Unorm;
108 isARGB = true;
109 } else {
110 RTC_NOTREACHED();
111 return NO;
112 }
113
114 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
115 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, mtlPixelFormat,
116 width, height, 0, &textureOut);
117 if (result == kCVReturnSuccess) {
118 gpuTexture = CVMetalTextureGetTexture(textureOut);
119 }
120 CVBufferRelease(textureOut);
121
122 if (gpuTexture != nil) {
123 _texture = gpuTexture;
JT Teha4888f02018-05-30 16:45:36 +0000124 _uniformsBuffer =
125 [[self currentMetalDevice] newBufferWithBytes:&isARGB
126 length:sizeof(isARGB)
127 options:MTLResourceCPUCacheModeDefaultCache];
Peter Hanspers1c62b982018-05-03 14:06:04 +0200128 return YES;
129 }
130
131 return NO;
132}
133
134- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
135 [renderEncoder setFragmentTexture:_texture atIndex:0];
136 [renderEncoder setFragmentBuffer:_uniformsBuffer offset:0 atIndex:0];
137}
138
139- (void)dealloc {
140 if (_textureCache) {
141 CFRelease(_textureCache);
142 }
143}
144
145@end