QGHWDMetalRenderer.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // QGHWDMetalRenderer.m
  2. // Tencent is pleased to support the open source community by making vap available.
  3. //
  4. // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  5. //
  6. // Licensed under the MIT License (the "License"); you may not use this file except in
  7. // compliance with the License. You may obtain a copy of the License at
  8. //
  9. // http://opensource.org/licenses/MIT
  10. //
  11. // Unless required by applicable law or agreed to in writing, software distributed under the License is
  12. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. // either express or implied. See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #import "QGHWDMetalRenderer.h"
  16. #import "QGHWDShaderTypes.h"
  17. #import "QGVAPLogger.h"
  18. #import <simd/simd.h>
  19. #import <MetalKit/MetalKit.h>
  20. #import "UIDevice+VAPUtil.h"
  21. #import "QGVAPMetalUtil.h"
  22. #import "QGVAPMetalShaderFunctionLoader.h"
  23. #pragma mark - constants
  24. NSString *const kQGHWDVertexFunctionName = @"hwd_vertexShader";
  25. NSString *const kQGHWDYUVFragmentFunctionName = @"hwd_yuvFragmentShader";
  26. static NSInteger const kQGQuadVerticesConstantsRow = 4;
  27. static NSInteger const kQGQuadVerticesConstantsColumn = 32;
  28. static NSInteger const kQGHWDVertexCount = 4;
  29. id<MTLDevice> kQGHWDMetalRendererDevice;
  30. // BT.601, which is the standard for SDTV.
  31. matrix_float3x3 const kQGColorConversionMatrix601Default = {{
  32. {1.164, 1.164, 1.164},
  33. {0.0, -0.392, 2.017},
  34. {1.596, -0.813, 0.0}
  35. }};
  36. /*矩阵形式!!!
  37. 1.0 0.0 1.4
  38. [1.0 -0.343 -0.711 ]
  39. 1.0 1.765 0.0
  40. */
  41. //ITU BT.601 Full Range
  42. matrix_float3x3 const kQGColorConversionMatrix601FullRangeDefault = {{
  43. {1.0, 1.0, 1.0},
  44. {0.0, -0.34413, 1.772},
  45. {1.402, -0.71414, 0.0}
  46. }};
  47. // BT.709, which is the standard for HDTV.
  48. matrix_float3x3 const kQGColorConversionMatrix709Default = {{
  49. {1.164, 1.164, 1.164},
  50. {0.0, -0.213, 2.112},
  51. {1.793, -0.533, 0.0}
  52. }};
  53. // BT.709 Full Range.
  54. matrix_float3x3 const kQGColorConversionMatrix709FullRangeDefault = {{
  55. {1.0, 1.0, 1.0},
  56. {0.0, -.18732, 1.8556},
  57. {1.57481, -.46813, 0.0}
  58. }};
  59. // Blur weight matrix.
  60. matrix_float3x3 const kQGBlurWeightMatrixDefault = {{
  61. {0.0625, 0.125, 0.0625},
  62. {0.125, 0.25, 0.125},
  63. {0.0625, 0.125, 0.0625}
  64. }};
  65. //QGHWDVertex 顶点坐标+纹理坐标(rgb+alpha)
  66. static const float kQGQuadVerticesConstants[kQGQuadVerticesConstantsRow][kQGQuadVerticesConstantsColumn] = {
  67. //左侧alpha
  68. {-1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0,
  69. -1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0,
  70. 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.5, 1.0,
  71. 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 0.0},
  72. //右侧alpha
  73. {-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.5, 1.0,
  74. -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0,
  75. 1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 1.0, 1.0,
  76. 1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0},
  77. //顶部alpha
  78. {-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.5,
  79. -1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0,
  80. 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5,
  81. 1.0, 1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 0.0},
  82. //底部alpha
  83. {-1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0,
  84. -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5,
  85. 1.0, -1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0,
  86. 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5}
  87. };
  88. #if TARGET_OS_SIMULATOR//模拟器
  89. #else
  90. @interface QGHWDMetalRenderer () {
  91. BOOL _renderingResourcesDisposed; //用以标记渲染资源是否被回收
  92. matrix_float3x3 _currentColorConversionMatrix;
  93. }
  94. @property (nonatomic, strong) id<MTLBuffer> vertexBuffer;
  95. @property (nonatomic, strong) id<MTLBuffer> yuvMatrixBuffer;
  96. @property (nonatomic, strong) id<MTLRenderPipelineState> pipelineState;//This will keep track of the compiled render pipeline you’re about to create.
  97. @property (nonatomic, strong) id<MTLCommandQueue> commandQueue;
  98. @property (nonatomic, assign) int vertexCount;
  99. @property (nonatomic, assign) CVMetalTextureCacheRef videoTextureCache;//need release
  100. @property (nonatomic, strong) QGVAPMetalShaderFunctionLoader *shaderFuncLoader;
  101. @end
  102. @implementation QGHWDMetalRenderer
  103. #pragma mark - main
  104. - (instancetype)initWithMetalLayer:(CAMetalLayer *)layer blendMode:(QGHWDTextureBlendMode)mode {
  105. self = [super init];
  106. if (self) {
  107. _blendMode = mode;
  108. if (!kQGHWDMetalRendererDevice) {
  109. kQGHWDMetalRendererDevice = MTLCreateSystemDefaultDevice();
  110. }
  111. layer.device = kQGHWDMetalRendererDevice;
  112. [self setupConstants];
  113. [self setupPipelineStatesWithMetalLayer:layer];
  114. }
  115. return self;
  116. }
  117. /**
  118. 回收渲染数据,减少内存占用
  119. */
  120. - (void)dispose {
  121. _commandQueue = nil;
  122. _pipelineState = nil;
  123. _vertexBuffer = nil;
  124. _yuvMatrixBuffer = nil;
  125. _shaderFuncLoader = nil;
  126. if (_videoTextureCache) {
  127. CVMetalTextureCacheFlush(_videoTextureCache, 0);
  128. CFRelease(_videoTextureCache);
  129. _videoTextureCache = NULL;
  130. }
  131. _renderingResourcesDisposed = YES;
  132. }
  133. - (void)dealloc {
  134. [self dispose];
  135. }
  136. - (void)setupConstants {
  137. //buffers
  138. const void *vertices = [self suitableQuadVertices];
  139. NSUInteger allocationSize = kQGQuadVerticesConstantsColumn * sizeof(float);
  140. _vertexBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:vertices length:allocationSize options:kDefaultMTLResourceOption];
  141. _vertexCount = kQGHWDVertexCount;
  142. _currentColorConversionMatrix = kQGColorConversionMatrix601FullRangeDefault;
  143. struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}};
  144. NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters);
  145. _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption];
  146. }
  147. - (void)updateMetalPropertiesIfNeed:(CVPixelBufferRef)pixelBuffer {
  148. if (!pixelBuffer) {
  149. return ;
  150. }
  151. CFTypeRef yCbCrMatrixType = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL);
  152. matrix_float3x3 matrix = kQGColorConversionMatrix601FullRangeDefault;
  153. if (CFStringCompare(yCbCrMatrixType, kCVImageBufferYCbCrMatrix_ITU_R_709_2, 0) == kCFCompareEqualTo) {
  154. matrix = kQGColorConversionMatrix709FullRangeDefault;
  155. }
  156. if (simd_equal(_currentColorConversionMatrix, matrix)) {
  157. return ;
  158. }
  159. _currentColorConversionMatrix = matrix;
  160. struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}};
  161. NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters);
  162. _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption];
  163. }
  164. - (void)setupPipelineStatesWithMetalLayer:(CAMetalLayer *)metalLayer {
  165. self.shaderFuncLoader = [[QGVAPMetalShaderFunctionLoader alloc] initWithDevice:kQGHWDMetalRendererDevice];
  166. id<MTLFunction> vertexProgram = [self.shaderFuncLoader loadFunctionWithName:kQGHWDVertexFunctionName];
  167. id<MTLFunction> fragmentProgram = [self.shaderFuncLoader loadFunctionWithName:kQGHWDYUVFragmentFunctionName];
  168. if (!vertexProgram || !fragmentProgram) {
  169. VAP_Error(kQGVAPModuleCommon, @"setupPipelineStatesWithMetalLayer fail! cuz: shader load fail");
  170. NSAssert(0, @"check if .metal files been compiled to correct target!");
  171. return ;
  172. }
  173. MTLRenderPipelineDescriptor *pipelineStateDescriptor = [MTLRenderPipelineDescriptor new];
  174. pipelineStateDescriptor.vertexFunction = vertexProgram;
  175. pipelineStateDescriptor.fragmentFunction = fragmentProgram;
  176. pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat;
  177. NSError *psError = nil;
  178. id<MTLRenderPipelineState> pipelineState = [kQGHWDMetalRendererDevice newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&psError];
  179. if (!pipelineState || psError) {
  180. VAP_Error(kQGVAPModuleCommon, @"newRenderPipelineStateWithDescriptor error!:%@", psError);
  181. return ;
  182. }
  183. self.pipelineState = pipelineState;
  184. self.commandQueue = [kQGHWDMetalRendererDevice newCommandQueue];
  185. CVReturn textureCacheError = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, kQGHWDMetalRendererDevice, nil, &_videoTextureCache);
  186. if (textureCacheError != kCVReturnSuccess) {
  187. VAP_Error(kQGVAPModuleCommon, @"create texture cache fail!:%@", textureCacheError);
  188. return ;
  189. }
  190. }
  191. /**
  192. 使用metal渲染管线渲染CVPixelBufferRef,若有需要融合的图层则通过对应的管线一并渲染
  193. @param pixelBuffer 图像数据
  194. @param layer metalLayer
  195. */
  196. - (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer {
  197. if (!layer.superlayer || layer.bounds.size.width <= 0 || layer.bounds.size.height <= 0) {
  198. //https://forums.developer.apple.com/thread/26278
  199. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz layer.superlayer or size error is nil! superlayer:%@ height:%@ width:%@", layer.superlayer, @(layer.bounds.size.height), @(layer.bounds.size.width));
  200. return ;
  201. }
  202. [self reconstructIfNeed:layer];
  203. if (pixelBuffer == NULL || !self.commandQueue || !self.pipelineState) {
  204. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz pixelbuffer is nil!");
  205. return ;
  206. }
  207. [self updateMetalPropertiesIfNeed:pixelBuffer];
  208. CVMetalTextureCacheFlush(_videoTextureCache, 0);
  209. CVMetalTextureRef yTextureRef = nil, uvTextureRef = nil;
  210. size_t yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
  211. size_t yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
  212. size_t uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
  213. size_t uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
  214. //注意格式!r8Unorm
  215. CVReturn yStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, yWidth, yHeight, 0, &yTextureRef);
  216. //注意格式!rg8Unorm
  217. CVReturn uvStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, uvWidth, uvHeight, 1, &uvTextureRef);
  218. if (yStatus != kCVReturnSuccess || uvStatus != kCVReturnSuccess) {
  219. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz failing getting yuv texture-yStatus%@:uvStatus%@", @(yStatus), @(uvStatus));
  220. return ;
  221. }
  222. id<MTLTexture> yTexture = CVMetalTextureGetTexture(yTextureRef);
  223. id<MTLTexture> uvTexture = CVMetalTextureGetTexture(uvTextureRef);
  224. CVBufferRelease(yTextureRef);
  225. CVBufferRelease(uvTextureRef);
  226. CVMetalTextureCacheFlush(_videoTextureCache, 0);
  227. yTextureRef = NULL;
  228. uvTextureRef = NULL;
  229. if (!yTexture || !uvTexture || !layer) {
  230. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz content is nil! y:%@ uv:%@, layer:%@", @(yTexture != nil), @(uvTexture != nil), @(layer != nil));
  231. return ;
  232. }
  233. if (layer.drawableSize.width <= 0 || layer.drawableSize.height <= 0) {
  234. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz drawableSize is 0");
  235. return ;
  236. }
  237. id<CAMetalDrawable> drawable = layer.nextDrawable;
  238. if (!drawable) {
  239. VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz nextDrawable is nil!");
  240. return ;
  241. }
  242. MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor new];
  243. renderPassDescriptor.colorAttachments[0].texture = drawable.texture; //which returns the texture in which you need to draw in order for something to appear on the screen.
  244. renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; //“set the texture to the clear color before doing any drawing,”
  245. renderPassDescriptor.colorAttachments[0].clearColor =MTLClearColorMake(1.0, 1.0, 1.0, 1.0);
  246. id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
  247. id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
  248. [renderEncoder setRenderPipelineState:self.pipelineState];
  249. [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0];
  250. [renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0];
  251. [renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma];
  252. [renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma];
  253. [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:self.vertexCount instanceCount:1];
  254. [renderEncoder endEncoding];
  255. [commandBuffer presentDrawable:drawable];
  256. [commandBuffer commit];
  257. }
  258. #pragma mark - private
  259. /**
  260. 在必要的时候重建渲染数据,以便渲染
  261. @param layer metalLayer
  262. */
  263. - (void)reconstructIfNeed:(CAMetalLayer *)layer {
  264. if (_renderingResourcesDisposed) {
  265. [self setupConstants];
  266. [self setupPipelineStatesWithMetalLayer:layer];
  267. _renderingResourcesDisposed = NO;
  268. }
  269. }
  270. - (const void *)suitableQuadVertices {
  271. switch (self.blendMode) {
  272. case QGHWDTextureBlendMode_AlphaLeft:
  273. return kQGQuadVerticesConstants[0];
  274. case QGHWDTextureBlendMode_AlphaRight:
  275. return kQGQuadVerticesConstants[1];
  276. case QGHWDTextureBlendMode_AlphaTop:
  277. return kQGQuadVerticesConstants[2];
  278. case QGHWDTextureBlendMode_AlphaBottom:
  279. return kQGQuadVerticesConstants[3];
  280. default:
  281. break;
  282. }
  283. return kQGQuadVerticesConstants[0];
  284. }
  285. @end
  286. #endif