使用1个Draw调用渲染2个单独的MtkView

时间:2018-10-24 04:52:46

标签: ios objective-c metal metalkit

两个(彼此不重叠)MtkViews运行正常。每个都有不同的制服,顶点和不同的原始类型。已经使用一个RenderCommandEncoder将两个视图之一合并为不同管道渲染。工作良好。 为了降低对iOS设备的能耗影响,我在每个View上都减少了mtkview.preferredFramesPerSecond = 24;

有没有一种方法可以在GPU上并行处理它们,以使它们不会互相总结渲染?

假设我必须使用

id<MTLParallelRenderCommandEncoder> renderEncoder = 
[commandBuffer parallelRenderCommandEncoderWithDescriptor:renderPassDescriptor];

但是该编码器了解...

[renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];

...我当然也使用普通的RenderCommandEncoder。

那么如何正确设置 MTLParallelRenderCommandEncoder

- (void)drawInMTKView:(nonnull MTKView *)view {
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    //commandBuffer.label = @"CombiCommand";

    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if(renderPassDescriptor != nil) {
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0,0,0,0);


        id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        //renderEncoder.label = @"CombiRenderEncoder";
        [renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];


        //----CHART----
        [renderEncoder setRenderPipelineState:_chartPipelineState];
        if (_pat->infoCC.needsDisplay ) {
            [CCChartMetalRenderer changeChartDataWithBuffer:_chartVertexBuffer];
            _chartUniform.statisch = somedata.isStatic;
            _pat->infoCC.needsDisplay=false;
        }
        [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:IndexViewportSize];
        [renderEncoder setVertexBytes:&_chartUniform length:sizeof(_chartUniform) atIndex:IndexUniforms];
        [renderEncoder setVertexBuffer:_chartVertexBuffer offset:0 atIndex:IndexVertices];
        [renderEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:_chartVerticesCount];


        //----NOTE----
        [renderEncoder setRenderPipelineState:_notePipelineState];
        if (_pat->infoNotePatch.needsDisplay ) {
            [NoteMetalRenderer changeVertexDataWithMtlBuffer:_noteVertexBuffer];
            _noteUniform.color = simd_make_float4(1.0, 0.0, 1.0, 1.0);
            _noteUniform.isOn = somedata.isOn;
            _pat->infoNotePatch.needsDisplay=false;
        }
        [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:IndexViewportSize];
        [renderEncoder setVertexBytes:&_noteUniform length:sizeof(_noteUniform) atIndex:IndexUniforms];
        [renderEncoder setVertexBuffer:_noteVertexBuffer offset:0 atIndex:IndexVertices];
        [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_notesCount * sizeof(NoteVertex)];


        //----POS----
        [renderEncoder setRenderPipelineState:_posPipelineState];
        _posUniform.statischValue = somedata.value;
        _posUniform.statisch = somedata.isStatic;
        [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:IndexViewportSize];
        [renderEncoder setVertexBytes:&_posUniform length:sizeof(_posUniform) atIndex:IndexUniforms];
        [renderEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:16];

        //---ENDENCODIG---
        [renderEncoder endEncoding];
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    [commandBuffer commit];
}

和第二个mtkView

- (void)drawInMTKView:(nonnull MTKView *)view {
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    //commandBuffer.label = @"CCTableCommand";

    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if(renderPassDescriptor != nil) {
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0,0,0,0);

        id<MTLRenderCommandEncoder> renderEncoder =
        [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        //renderEncoder.label = @"CCTableRenderEncoder";
        [renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];

        [renderEncoder setRenderPipelineState:_pipelineState];
        [self.class changeVertexDataWithPatch:_pat Ch:_viewCH Quantize:_quantized mtlBuffer:_vertexBuffer];
        _tableUniform.isOn = somedata.isOn;
        [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:IndexViewportSize];
        [renderEncoder setVertexBytes:&_tableUniform length:sizeof(_tableUniform) atIndex:IndexUniforms];
        [renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:IndexVertices];

        [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_numVertices];

        [renderEncoder endEncoding];
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    [commandBuffer commit];
}

2 个答案:

答案 0 :(得分:0)

您如何得出结论,并行命令编码是一种解决方案?

并行命令编码的目的是在线程之间分配CPU端编码工作。它与最终如何安排工作并在GPU上执行工作无关。如果您受CPU限制,则可能需要使用并行编码,但是每个并行渲染命令编码器只能编码影响一个视图的命令。

不可能通过一个绘制调用同时渲染到两个MTKView,因为每个绘制调用都使用与其相应视图的当前可绘制对象关联的渲染命令编码器进行编码。

您似乎误以为您编码的工作必然会在GPU上顺序执行。事实并非如此。如果可以安排独立的工作并发执行,则GPU驱动程序会这样做,但这是您无法控制的。

答案 1 :(得分:0)

每个编码器的ViewportSize的巧妙使用和UIView分层使得可以一次渲染一次。

- (void)drawInMTKView:(nonnull MTKView *)view {
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if(renderPassDescriptor != nil) {

        // allows for transparent layering
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0,0,0,0);

        // allowes the encoders to work on different threads
        id<MTLParallelRenderCommandEncoder> parallelRenderEnc = [commandBuffer parallelRenderCommandEncoderWithDescriptor:renderPassDescriptor];

        // so we need to get the renderEncoders from the parallelRenderEncoder
        id<MTLRenderCommandEncoder> renderEncoder1 = [parallelRenderEnc renderCommandEncoder];
        id<MTLRenderCommandEncoder> renderEncoder2 = [parallelRenderEnc renderCommandEncoder];

        // each of which need their own viewport settings, they dont need to be equaly
        [renderEncoder1 setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];
        [renderEncoder2 setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];

        [renderEncoder1 setRenderPipelineState:_renderPipeLine1];
        //do your Metal SharedBuffer stuff for encoder1 here
        //renderEncoder1 set uniform buffer/bytes
        //renderEncoder1 set vertex buffer/bytes
        //renderEncoder1 drawPrimitives with the MTLPrimitiveType you want

        [renderEncoder2 setRenderPipelineState:_renderPipeLine2];
        //do your Metal SharedBuffer stuff for encoder2 here
        //renderEncoder2 set uniform buffer/bytes
        //renderEncoder2 set vertex buffer/bytes
        //renderEncoder2 drawPrimitives with the MTLPrimitiveType you want

        [renderEncoder1 endEncoding];
        [renderEncoder2 endEncoding];

       [parallelRenderEnc endEncoding]
       [commandBuffer presentDrawable:view.currentDrawable];
    }
    [commandBuffer commit];
}

您可以决定允许哪个commandEncoder在同一线程或它们自己的线程上运行。 现在,您可以使用TripleBuffering来使CPU和GPU不在同一缓冲区上同时工作。