blob: 70ecf549abc928645bb89a0f9768e17ae36f3caf [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
16#import "WebRTC/RTCLogging.h"
17#import "WebRTC/RTCVideoFrame.h"
Anders Carlssone5960ce2017-06-22 15:26:30 +020018#import "WebRTC/RTCVideoFrameBuffer.h"
denicija070d5e32017-02-26 11:44:13 -080019
denicijad2088152017-04-28 02:14:54 -070020#import "RTCMTLRenderer+Private.h"
denicija070d5e32017-02-26 11:44:13 -080021
22#define MTL_STRINGIFY(s) @ #s
23
denicija070d5e32017-02-26 11:44:13 -080024static NSString *const shaderSource = MTL_STRINGIFY(
25 using namespace metal; typedef struct {
26 packed_float2 position;
27 packed_float2 texcoord;
28 } Vertex;
29
30 typedef struct {
31 float4 position[[position]];
32 float2 texcoord;
33 } Varyings;
34
35 vertex Varyings vertexPassthrough(device Vertex * verticies[[buffer(0)]],
36 unsigned int vid[[vertex_id]]) {
37 Varyings out;
38 device Vertex &v = verticies[vid];
39 out.position = float4(float2(v.position), 0.0, 1.0);
40 out.texcoord = v.texcoord;
denicija070d5e32017-02-26 11:44:13 -080041 return out;
42 }
43
44 // Receiving YCrCb textures.
45 fragment half4 fragmentColorConversion(
46 Varyings in[[stage_in]], texture2d<float, access::sample> textureY[[texture(0)]],
47 texture2d<float, access::sample> textureCbCr[[texture(1)]]) {
48 constexpr sampler s(address::clamp_to_edge, filter::linear);
49 float y;
50 float2 uv;
51 y = textureY.sample(s, in.texcoord).r;
52 uv = textureCbCr.sample(s, in.texcoord).rg - float2(0.5, 0.5);
53
54 // Conversion for YUV to rgb from http://www.fourcc.org/fccyvrgb.php
55 float4 out = float4(y + 1.403 * uv.y, y - 0.344 * uv.x - 0.714 * uv.y, y + 1.770 * uv.x, 1.0);
56
57 return half4(out);
58 });
59
denicija070d5e32017-02-26 11:44:13 -080060@implementation RTCMTLNV12Renderer {
denicija070d5e32017-02-26 11:44:13 -080061 // Textures.
62 CVMetalTextureCacheRef _textureCache;
63 id<MTLTexture> _yTexture;
64 id<MTLTexture> _CrCbTexture;
denicija070d5e32017-02-26 11:44:13 -080065}
66
67- (BOOL)addRenderingDestination:(__kindof MTKView *)view {
denicijad2088152017-04-28 02:14:54 -070068 if ([super addRenderingDestination:view]) {
denicija070d5e32017-02-26 11:44:13 -080069 [self initializeTextureCache];
denicijad2088152017-04-28 02:14:54 -070070 return YES;
denicija070d5e32017-02-26 11:44:13 -080071 }
denicijad2088152017-04-28 02:14:54 -070072 return NO;
denicija070d5e32017-02-26 11:44:13 -080073}
74
75- (void)initializeTextureCache {
denicijad2088152017-04-28 02:14:54 -070076 CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice],
77 nil, &_textureCache);
denicija070d5e32017-02-26 11:44:13 -080078 if (status != kCVReturnSuccess) {
79 RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status);
80 }
81}
82
denicijad2088152017-04-28 02:14:54 -070083- (NSString *)shaderSource {
84 return shaderSource;
denicija070d5e32017-02-26 11:44:13 -080085}
86
kthelgasona2fb30c2017-03-09 03:34:27 -080087- (BOOL)setupTexturesForFrame:(nonnull RTCVideoFrame *)frame {
denicijad2088152017-04-28 02:14:54 -070088 [super setupTexturesForFrame:frame];
Anders Carlssone5960ce2017-06-22 15:26:30 +020089 CVPixelBufferRef pixelBuffer = ((RTCCVPixelBuffer *)frame.buffer).pixelBuffer;
denicija070d5e32017-02-26 11:44:13 -080090
91 id<MTLTexture> lumaTexture = nil;
92 id<MTLTexture> chromaTexture = nil;
93 CVMetalTextureRef outTexture = nullptr;
94
95 // Luma (y) texture.
96 int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
97 int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
98
99 int indexPlane = 0;
100 CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
101 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, lumaWidth,
102 lumaHeight, indexPlane, &outTexture);
103
104 if (result == kCVReturnSuccess) {
105 lumaTexture = CVMetalTextureGetTexture(outTexture);
106 }
107
108 // Same as CFRelease except it can be passed NULL without crashing.
109 CVBufferRelease(outTexture);
110 outTexture = nullptr;
111
112 // Chroma (CrCb) texture.
113 indexPlane = 1;
114 result = CVMetalTextureCacheCreateTextureFromImage(
115 kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, lumaWidth / 2,
116 lumaHeight / 2, indexPlane, &outTexture);
117 if (result == kCVReturnSuccess) {
118 chromaTexture = CVMetalTextureGetTexture(outTexture);
119 }
120 CVBufferRelease(outTexture);
121
122 if (lumaTexture != nil && chromaTexture != nil) {
123 _yTexture = lumaTexture;
124 _CrCbTexture = chromaTexture;
kthelgasona2fb30c2017-03-09 03:34:27 -0800125 return YES;
denicija070d5e32017-02-26 11:44:13 -0800126 }
kthelgasona2fb30c2017-03-09 03:34:27 -0800127 return NO;
denicija070d5e32017-02-26 11:44:13 -0800128}
129
denicijad2088152017-04-28 02:14:54 -0700130- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
131 [renderEncoder setFragmentTexture:_yTexture atIndex:0];
132 [renderEncoder setFragmentTexture:_CrCbTexture atIndex:1];
133}
134
denicija070d5e32017-02-26 11:44:13 -0800135@end