DKWechatHelper/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.m
DKJone 888af8954e [v1.0.7](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.7) / 2021-01-29
what's new
* 动态启动图
* 动态聊天背景
* 支持8.0.1
* 更新越狱包8.0.1
* 更新已注入助手的8.0.1未签名包
* 更新越狱源安装包
2021-01-29 16:35:34 +08:00

322 lines
13 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// QGHWDMetalRenderer.m
// Tencent is pleased to support the open source community by making vap available.
//
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions and
// limitations under the License.
#import "QGHWDMetalRenderer.h"
#import "QGHWDShaderTypes.h"
#import "QGVAPLogger.h"
#import <simd/simd.h>
#import <MetalKit/MetalKit.h>
#import "UIDevice+VAPUtil.h"
#import "QGVAPMetalUtil.h"
#import "QGVAPMetalShaderFunctionLoader.h"
#pragma mark - constants
NSString *const kHWDVertexFunctionName = @"hwd_vertexShader";
NSString *const kHWDYUVFragmentFunctionName = @"hwd_yuvFragmentShader";
static NSInteger const kQuadVerticesConstantsRow = 4;
static NSInteger const kQuadVerticesConstantsColumn = 32;
static NSInteger const kHWDVertexCount = 4;
id<MTLDevice> kQGHWDMetalRendererDevice;
// BT.601, which is the standard for SDTV.
matrix_float3x3 const kColorConversionMatrix601Default = {{
{1.164, 1.164, 1.164},
{0.0, -0.392, 2.017},
{1.596, -0.813, 0.0}
}};
/*矩阵形式!!!
1.0 0.0 1.4
[1.0 -0.343 -0.711 ]
1.0 1.765 0.0
*/
//ITU BT.601 Full Range
matrix_float3x3 const kColorConversionMatrix601FullRangeDefault = {{
{1.0, 1.0, 1.0},
{0.0, -0.34413, 1.772},
{1.402, -0.71414, 0.0}
}};
// BT.709, which is the standard for HDTV.
matrix_float3x3 const kColorConversionMatrix709Default = {{
{1.164, 1.164, 1.164},
{0.0, -0.213, 2.112},
{1.793, -0.533, 0.0}
}};
// BT.709 Full Range.
matrix_float3x3 const kColorConversionMatrix709FullRangeDefault = {{
{1.0, 1.0, 1.0},
{0.0, -.18732, 1.8556},
{1.57481, -.46813, 0.0}
}};
// Blur weight matrix.
matrix_float3x3 const kBlurWeightMatrixDefault = {{
{0.0625, 0.125, 0.0625},
{0.125, 0.25, 0.125},
{0.0625, 0.125, 0.0625}
}};
//QGHWDVertex 顶点坐标+纹理坐标rgb+alpha
static const float kQuadVerticesConstants[kQuadVerticesConstantsRow][kQuadVerticesConstantsColumn] = {
//左侧alpha
{-1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.5, 1.0,
1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 0.0},
//右侧alpha
{-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.5, 1.0,
-1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0,
1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 1.0, 1.0,
1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0},
//顶部alpha
{-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.5,
-1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5,
1.0, 1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 0.0},
//底部alpha
{-1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5,
1.0, -1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0,
1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5}
};
#if TARGET_OS_SIMULATOR//模拟器
#else
@interface QGHWDMetalRenderer () {
BOOL _renderingResourcesDisposed; //用以标记渲染资源是否被回收
matrix_float3x3 _currentColorConversionMatrix;
}
@property (nonatomic, strong) id<MTLBuffer> vertexBuffer;
@property (nonatomic, strong) id<MTLBuffer> yuvMatrixBuffer;
@property (nonatomic, strong) id<MTLRenderPipelineState> pipelineState;//This will keep track of the compiled render pipeline youre about to create.
@property (nonatomic, strong) id<MTLCommandQueue> commandQueue;
@property (nonatomic, assign) int vertexCount;
@property (nonatomic, assign) CVMetalTextureCacheRef videoTextureCache;//need release
@property (nonatomic, strong) QGVAPMetalShaderFunctionLoader *shaderFuncLoader;
@end
@implementation QGHWDMetalRenderer
#pragma mark - main
- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer blendMode:(QGHWDTextureBlendMode)mode {
self = [super init];
if (self) {
_blendMode = mode;
if (!kQGHWDMetalRendererDevice) {
kQGHWDMetalRendererDevice = MTLCreateSystemDefaultDevice();
}
layer.device = kQGHWDMetalRendererDevice;
[self setupConstants];
[self setupPipelineStatesWithMetalLayer:layer];
}
return self;
}
/**
回收渲染数据,减少内存占用
*/
- (void)dispose {
_commandQueue = nil;
_pipelineState = nil;
_vertexBuffer = nil;
_yuvMatrixBuffer = nil;
_shaderFuncLoader = nil;
if (_videoTextureCache) {
CVMetalTextureCacheFlush(_videoTextureCache, 0);
CFRelease(_videoTextureCache);
_videoTextureCache = NULL;
}
_renderingResourcesDisposed = YES;
}
- (void)dealloc {
[self dispose];
}
- (void)setupConstants {
//buffers
const void *vertices = [self suitableQuadVertices];
NSUInteger allocationSize = kQuadVerticesConstantsColumn * sizeof(float);
_vertexBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:vertices length:allocationSize options:kDefaultMTLResourceOption];
_vertexCount = kHWDVertexCount;
_currentColorConversionMatrix = kColorConversionMatrix601FullRangeDefault;
struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}};
NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters);
_yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption];
}
- (void)updateMetalPropertiesIfNeed:(CVPixelBufferRef)pixelBuffer {
if (!pixelBuffer) {
return ;
}
CFTypeRef yCbCrMatrixType = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL);
matrix_float3x3 matrix = kColorConversionMatrix601FullRangeDefault;
if (CFStringCompare(yCbCrMatrixType, kCVImageBufferYCbCrMatrix_ITU_R_709_2, 0) == kCFCompareEqualTo) {
matrix = kColorConversionMatrix709FullRangeDefault;
}
if (simd_equal(_currentColorConversionMatrix, matrix)) {
return ;
}
_currentColorConversionMatrix = matrix;
struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}};
NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters);
_yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption];
}
- (void)setupPipelineStatesWithMetalLayer:(CAMetalLayer *)metalLayer {
self.shaderFuncLoader = [[QGVAPMetalShaderFunctionLoader alloc] initWithDevice:kQGHWDMetalRendererDevice];
id<MTLFunction> vertexProgram = [self.shaderFuncLoader loadFunctionWithName:kHWDVertexFunctionName];
id<MTLFunction> fragmentProgram = [self.shaderFuncLoader loadFunctionWithName:kHWDYUVFragmentFunctionName];
if (!vertexProgram || !fragmentProgram) {
VAP_Error(kQGVAPModuleCommon, @"setupPipelineStatesWithMetalLayer fail! cuz: shader load fail");
NSAssert(0, @"check if .metal files been compiled to correct target!");
return ;
}
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [MTLRenderPipelineDescriptor new];
pipelineStateDescriptor.vertexFunction = vertexProgram;
pipelineStateDescriptor.fragmentFunction = fragmentProgram;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat;
NSError *psError = nil;
id<MTLRenderPipelineState> pipelineState = [kQGHWDMetalRendererDevice newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&psError];
if (!pipelineState || psError) {
VAP_Error(kQGVAPModuleCommon, @"newRenderPipelineStateWithDescriptor error!:%@", psError);
return ;
}
self.pipelineState = pipelineState;
self.commandQueue = [kQGHWDMetalRendererDevice newCommandQueue];
CVReturn textureCacheError = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, kQGHWDMetalRendererDevice, nil, &_videoTextureCache);
if (textureCacheError != kCVReturnSuccess) {
VAP_Error(kQGVAPModuleCommon, @"create texture cache fail!:%@", textureCacheError);
return ;
}
}
/**
使用metal渲染管线渲染CVPixelBufferRef若有需要融合的图层则通过对应的管线一并渲染
@param pixelBuffer 图像数据
@param layer metalLayer
*/
- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer {
if (!layer.superlayer || layer.bounds.size.width <= 0 || layer.bounds.size.height <= 0) {
//https://forums.developer.apple.com/thread/26278
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));
return ;
}
[self reconstructIfNeed:layer];
if (pixelBuffer == NULL || !self.commandQueue || !self.pipelineState) {
VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz pixelbuffer is nil!");
return ;
}
[self updateMetalPropertiesIfNeed:pixelBuffer];
CVMetalTextureCacheFlush(_videoTextureCache, 0);
CVMetalTextureRef yTextureRef = nil, uvTextureRef = nil;
size_t yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
size_t yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
size_t uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
size_t uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
//注意格式r8Unorm
CVReturn yStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, yWidth, yHeight, 0, &yTextureRef);
//注意格式rg8Unorm
CVReturn uvStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, uvWidth, uvHeight, 1, &uvTextureRef);
if (yStatus != kCVReturnSuccess || uvStatus != kCVReturnSuccess) {
VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz failing getting yuv texture-yStatus%@:uvStatus%@", @(yStatus), @(uvStatus));
return ;
}
id<MTLTexture> yTexture = CVMetalTextureGetTexture(yTextureRef);
id<MTLTexture> uvTexture = CVMetalTextureGetTexture(uvTextureRef);
CVBufferRelease(yTextureRef);
CVBufferRelease(uvTextureRef);
CVMetalTextureCacheFlush(_videoTextureCache, 0);
yTextureRef = NULL;
uvTextureRef = NULL;
if (!yTexture || !uvTexture || !layer) {
VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz content is nil! y:%@ uv:%@, layer:%@", @(yTexture != nil), @(uvTexture != nil), @(layer != nil));
return ;
}
if (layer.drawableSize.width <= 0 || layer.drawableSize.height <= 0) {
VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz drawableSize is 0");
return ;
}
id<CAMetalDrawable> drawable = layer.nextDrawable;
if (!drawable) {
VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz nextDrawable is nil!");
return ;
}
MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor new];
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.
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; //“set the texture to the clear color before doing any drawing,”
renderPassDescriptor.colorAttachments[0].clearColor =MTLClearColorMake(1.0, 1.0, 1.0, 1.0);
id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder setRenderPipelineState:self.pipelineState];
[renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0];
[renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0];
[renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma];
[renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma];
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:self.vertexCount instanceCount:1];
[renderEncoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}
#pragma mark - private
/**
在必要的时候重建渲染数据,以便渲染
@param layer metalLayer
*/
- (void)reconstructIfNeed:(CAMetalLayer *)layer {
if (_renderingResourcesDisposed) {
[self setupConstants];
[self setupPipelineStatesWithMetalLayer:layer];
_renderingResourcesDisposed = NO;
}
}
- (const void *)suitableQuadVertices {
switch (self.blendMode) {
case QGHWDTextureBlendMode_AlphaLeft:
return kQuadVerticesConstants[0];
case QGHWDTextureBlendMode_AlphaRight:
return kQuadVerticesConstants[1];
case QGHWDTextureBlendMode_AlphaTop:
return kQuadVerticesConstants[2];
case QGHWDTextureBlendMode_AlphaBottom:
return kQuadVerticesConstants[3];
default:
break;
}
return kQuadVerticesConstants[0];
}
@end
#endif