-1

Having two different (not overlapping each other) MtkViews running properly. Each with different Uniforms, Vertices and different Primitive Types. Already made one of both Views a combination of different pipelines rendering with one RenderCommandEncoder. Works fine. To lower energy impact on iOS devices I reduced the mtkview.preferredFramesPerSecond = 24; on each View.

Is there a way to process them in parallel on GPU so they do not sum up rendering after each other?

Assuming I have to use

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

but this Encoder does not know about...

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

...which I used of course with the normal RenderCommandEncoder.

So how to properly setup 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];
}

and the second 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];
}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Ol Sen
  • 3,163
  • 2
  • 21
  • 30

2 Answers2

0

How have you come to the conclusion that parallel command encoding is a solution?

The purpose of parallel command encoding is to distribute the CPU-side encoding work across threads. It has nothing to do with how that work is eventually scheduled and performed on the GPU. If you're CPU-bound, you might have a case for using parallel encoding, but each parallel render command encoder can only encode commands that affect one view.

It's not possible to render to two MTKViews simultaneously with one draw call, because each draw call is encoded with a render command encoder that is affiliated with its corresponding view's current drawable.

You seem to be under the misapprehension that the work you encode will necessarily be executed sequentially on the GPU. This isn't the case. If it's possible to schedule independent work to execute concurrently, the GPU driver will do so, but that's out of your control.

warrenm
  • 31,094
  • 6
  • 92
  • 116
0

Clever use of ViewportSize per Encoder and UIView layering make it possible to render at 'once'.

- (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];
}

You can decide which of the commandEncoders are allowed to run on the same thread or on their own thread. Now you could go for tripleBuffering to make CPU and GPU work not on the same buffer at the same time.

Ol Sen
  • 3,163
  • 2
  • 21
  • 30