首頁 > 軟體

Flutter+Metal實現影象處理詳細流程

2022-06-18 14:01:39

背景

在之前自制的影象處理App中,使用了OpenGL處理圖片,這次使用Metal替代OpenGL,來達到更好的效能,順便熟悉一下Metal的渲染流程

基本思路

Flutter使用CVPixelBuffer和iOS互動,我們可以直接使用CVPixelBuffer建立MTLTexture,然後將MTLTexture設定為渲染目標。這樣Metal框架可以直接將渲染結果寫入CVPixelBuffer,達到更加高效的目的。

Metal環境設定

主要初始化DevicePipelineStateCommandQueue三個物件。我們需要依賴Device分配各種Metal資源,PipelineState管理著渲染流水線的各個環節的設定,比如vertex shader,fragment shader,輸出畫素格式等。CommandQueue用於管理執行的繪製命令。

_device = MTLCreateSystemDefaultDevice();
id<MTLLibrary> lib = [_device newDefaultLibrary];
id<MTLFunction> vertexFunc = [lib newFunctionWithName:vertexFuncName];
id<MTLFunction> fragFunc = [lib newFunctionWithName:fragFuncName];
MTLRenderPipelineDescriptor *renderPipelineDesc = [MTLRenderPipelineDescriptor new];
renderPipelineDesc.vertexFunction = vertexFunc;
renderPipelineDesc.fragmentFunction = fragFunc;
renderPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
_pipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDesc error:nil];
_commandQueue = [_device newCommandQueue];

從CVPixelBuffer建立MTLTexture紋理

首先建立一個CVPixelBuffer物件

NSDictionary *pixelAttributes = @{( id )kCVPixelBufferIOSurfacePropertiesKey : @{}};
CVPixelBufferCreate(
            kCFAllocatorDefault,
                    imageWidth,
                    imageHeight,
            kCVPixelFormatType_32BGRA,
                    (__bridge CFDictionaryRef)pixelAttributes,
                    &_renderTargetPixelBuffer);

利用CVMetalTextureCacheCreateTextureFromImageCVPixelBuffer建立MTLTexture

CVReturn ret = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _mtContext.device, nil, &_textureCache);
CVMetalTextureRef renderTargetMetalTextureRef;
ret = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, _renderTargetPixelBuffer, nil, MTLPixelFormatBGRA8Unorm, imageWidth, imageHeight, 0, &renderTargetMetalTextureRef);
id<MTLTexture> mtlTexture = CVMetalTextureGetTexture(renderTargetMetalTextureRef);

渲染到紋理

CommandQueue獲得一個CommandBuffer,用於儲存需要執行的繪製命令

_activeCmdBuffer = [_commandQueue commandBuffer];

建立MTLRenderPassDescriptor設定本次繪製的相關設定,比如繪製到哪裡,這裡指定通過CVPixelBuffer建立出來的MTLTexture,是否清除當前內容,清除的顏色

MTLRenderPassDescriptor *renderPassDesc = [MTLRenderPassDescriptor new];
renderPassDesc.colorAttachments[0].texture = target;
renderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1);

通過CommandBufferMTLRenderPassDescriptor建立一個MTLRenderCommandEncoder

_activeEncoder = [_activeCmdBuffer renderCommandEncoderWithDescriptor:renderPassDesc];

指定MTLRenderCommandEncoder所在的PipelineState

[_activeEncoder setRenderPipelineState:_pipelineState];

使用MTLRenderCommandEncoder繫結BufferTexture,在Metal裡,Uniform和Vertex Buffer 都是通過MTLBuffer繫結到Shader中

[_activeEncoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[_activeEncoder setFragmentBuffer:uniformBuffer offset:0 atIndex:0];
[_activeEncoder setFragmentBuffer:texture offset:0 atIndex:0];

繪製圖形

[_activeEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:vertexCount instanceCount:1];

顯式的結束MTLRenderCommandEncoder

[_activeEncoder endEncoding];

提交CommandBuffer

[_activeCmdBuffer commit];

等待繪製結束,如果你想要非同步等待,需要在[_activeCmdBuffer commit]之前設定completedHandler

// 同步等待
[_activeCmdBuffer waitUntilCompleted];
// 非同步等待
[_activeCmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull buf) {
}];

到此繪製的內容就已經在CVPixelBuffer中了,再將CVPixelBuffer提交給Flutter顯示即可。

到此這篇關於Flutter+Metal實現影象處理的文章就介紹到這了,更多相關Flutter影象處理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com