blob: 98835cb5182b2ae6717e7f83383207eb7e144c2c [file] [log] [blame]
denicija070d5e32017-02-26 11:44:13 -08001/*
denicijad2088152017-04-28 02:14:54 -07002 * Copyright 2017 The WebRTC Project Authors. All rights reserved.
denicija070d5e32017-02-26 11:44:13 -08003 *
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 "RTCMTLNV12Renderer.h"
12
13#import <Metal/Metal.h>
14#import <MetalKit/MetalKit.h>
15
denicijad2088152017-04-28 02:14:54 -070016#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
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +020022#include "rtc_base/checks.h"
denicija070d5e32017-02-26 11:44:13 -080023
denicija070d5e32017-02-26 11:44:13 -080024static NSString *const shaderSource = MTL_STRINGIFY(
Peter Hanspers1c62b982018-05-03 14:06:04 +020025 using namespace metal;
26
27 typedef struct {
denicija070d5e32017-02-26 11:44:13 -080028 packed_float2 position;
29 packed_float2 texcoord;
30 } Vertex;
31
32 typedef struct {
33 float4 position[[position]];
34 float2 texcoord;
35 } Varyings;
36
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020037 vertex Varyings vertexPassthrough(constant Vertex *verticies[[buffer(0)]],
denicija070d5e32017-02-26 11:44:13 -080038 unsigned int vid[[vertex_id]]) {
39 Varyings out;
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020040 constant Vertex &v = verticies[vid];
denicija070d5e32017-02-26 11:44:13 -080041 out.position = float4(float2(v.position), 0.0, 1.0);
42 out.texcoord = v.texcoord;
denicija070d5e32017-02-26 11:44:13 -080043 return out;
44 }
45
46 // Receiving YCrCb textures.
47 fragment half4 fragmentColorConversion(
Kári Tristan Helgason93d4c102019-08-19 15:17:37 +020048 Varyings in[[stage_in]],
49 texture2d<float, access::sample> textureY[[texture(0)]],
denicija070d5e32017-02-26 11:44:13 -080050 texture2d<float, access::sample> textureCbCr[[texture(1)]]) {
51 constexpr sampler s(address::clamp_to_edge, filter::linear);
52 float y;
53 float2 uv;
54 y = textureY.sample(s, in.texcoord).r;
55 uv = textureCbCr.sample(s, in.texcoord).rg - float2(0.5, 0.5);
56
57 // Conversion for YUV to rgb from http://www.fourcc.org/fccyvrgb.php
58 float4 out = float4(y + 1.403 * uv.y, y - 0.344 * uv.x - 0.714 * uv.y, y + 1.770 * uv.x, 1.0);
59
60 return half4(out);
61 });
62
denicija070d5e32017-02-26 11:44:13 -080063@implementation RTCMTLNV12Renderer {
denicija070d5e32017-02-26 11:44:13 -080064 // Textures.
65 CVMetalTextureCacheRef _textureCache;
66 id<MTLTexture> _yTexture;
67 id<MTLTexture> _CrCbTexture;
denicija070d5e32017-02-26 11:44:13 -080068}
69
70- (BOOL)addRenderingDestination:(__kindof MTKView *)view {
denicijad2088152017-04-28 02:14:54 -070071 if ([super addRenderingDestination:view]) {
Peter Hanspers1c62b982018-05-03 14:06:04 +020072 return [self initializeTextureCache];
denicija070d5e32017-02-26 11:44:13 -080073 }
denicijad2088152017-04-28 02:14:54 -070074 return NO;
denicija070d5e32017-02-26 11:44:13 -080075}
76
Peter Hanspers1c62b982018-05-03 14:06:04 +020077- (BOOL)initializeTextureCache {
denicijad2088152017-04-28 02:14:54 -070078 CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice],
79 nil, &_textureCache);
denicija070d5e32017-02-26 11:44:13 -080080 if (status != kCVReturnSuccess) {
81 RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status);
Peter Hanspers1c62b982018-05-03 14:06:04 +020082 return NO;
denicija070d5e32017-02-26 11:44:13 -080083 }
Peter Hanspers1c62b982018-05-03 14:06:04 +020084
85 return YES;
denicija070d5e32017-02-26 11:44:13 -080086}
87
denicijad2088152017-04-28 02:14:54 -070088- (NSString *)shaderSource {
89 return shaderSource;
denicija070d5e32017-02-26 11:44:13 -080090}
91
Peter Hanspers7af087a2018-06-12 14:14:48 +020092- (void)getWidth:(nonnull int *)width
93 height:(nonnull int *)height
94 cropWidth:(nonnull int *)cropWidth
95 cropHeight:(nonnull int *)cropHeight
96 cropX:(nonnull int *)cropX
97 cropY:(nonnull int *)cropY
98 ofFrame:(nonnull RTCVideoFrame *)frame {
99 RTCCVPixelBuffer *pixelBuffer = (RTCCVPixelBuffer *)frame.buffer;
100 *width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer);
101 *height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer);
102 *cropWidth = pixelBuffer.cropWidth;
103 *cropHeight = pixelBuffer.cropHeight;
104 *cropX = pixelBuffer.cropX;
105 *cropY = pixelBuffer.cropY;
106}
107
kthelgasona2fb30c2017-03-09 03:34:27 -0800108- (BOOL)setupTexturesForFrame:(nonnull RTCVideoFrame *)frame {
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200109 RTC_DCHECK([frame.buffer isKindOfClass:[RTCCVPixelBuffer class]]);
Peter Hanspers5daaf7d2018-06-01 10:34:37 +0200110 if (![super setupTexturesForFrame:frame]) {
111 return NO;
112 }
Anders Carlssone5960ce2017-06-22 15:26:30 +0200113 CVPixelBufferRef pixelBuffer = ((RTCCVPixelBuffer *)frame.buffer).pixelBuffer;
denicija070d5e32017-02-26 11:44:13 -0800114
115 id<MTLTexture> lumaTexture = nil;
116 id<MTLTexture> chromaTexture = nil;
117 CVMetalTextureRef outTexture = nullptr;
118
119 // Luma (y) texture.
120 int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
121 int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
122
123 int indexPlane = 0;
124 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
125 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, lumaWidth,
126 lumaHeight, indexPlane, &outTexture);
127
128 if (result == kCVReturnSuccess) {
129 lumaTexture = CVMetalTextureGetTexture(outTexture);
130 }
131
132 // Same as CFRelease except it can be passed NULL without crashing.
133 CVBufferRelease(outTexture);
134 outTexture = nullptr;
135
136 // Chroma (CrCb) texture.
137 indexPlane = 1;
138 result = CVMetalTextureCacheCreateTextureFromImage(
139 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, lumaWidth / 2,
140 lumaHeight / 2, indexPlane, &outTexture);
141 if (result == kCVReturnSuccess) {
142 chromaTexture = CVMetalTextureGetTexture(outTexture);
143 }
144 CVBufferRelease(outTexture);
145
146 if (lumaTexture != nil && chromaTexture != nil) {
147 _yTexture = lumaTexture;
148 _CrCbTexture = chromaTexture;
kthelgasona2fb30c2017-03-09 03:34:27 -0800149 return YES;
denicija070d5e32017-02-26 11:44:13 -0800150 }
kthelgasona2fb30c2017-03-09 03:34:27 -0800151 return NO;
denicija070d5e32017-02-26 11:44:13 -0800152}
153
denicijad2088152017-04-28 02:14:54 -0700154- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
155 [renderEncoder setFragmentTexture:_yTexture atIndex:0];
156 [renderEncoder setFragmentTexture:_CrCbTexture atIndex:1];
157}
158
Danielacdd1f682017-09-28 15:48:08 +0200159- (void)dealloc {
160 if (_textureCache) {
161 CFRelease(_textureCache);
162 }
163}
denicija070d5e32017-02-26 11:44:13 -0800164@end