plmrn
plmrn

Reputation: 21

How can I access the contents of a MTLBuffer after GPU rendering?

I'm working on an OpenFX plugin to process images in grading/post-production software.

All my processing is done in a series of Metal kernel functions. The image is sent to the GPU as buffers (float array), one for the input and one for the output.

The output is then used by the OpenFX framework for display inside the host application, so up till then I didn't have to take care of it.

I now need to be able to read the output values once the GPU has processed the commands. I have tried to use the "contents" method applied on the buffer but my plugin keeps crashing (in the worst case), or gives me very weird values when it "works" (I'm not supposed to have anything over 1 and under 0, but I get very large numbers, 0 or negative 0, nan... So I assume I have a memory access issue of sorts).

At first I thought it was an issue with Private/Shared memory, so I tried to modify the buffer to be shared. But I'm still struggling!

Full disclosure: I have no specific training in MSL, I'm learning as I go with this project so I might be doing and-or saying very stupid things. I have looked around for hours before deciding to ask for help. Thanks to all who will help out in any way!

Below is the code (without everything that doesn't concern my current issue). If it is lacking anything of interest please let me know.

id < MTLBuffer > srcDeviceBuf = reinterpret_cast<id<MTLBuffer> >(const_cast<float*>(p_Input)) ;

//Below is the destination Image buffer creation the way it used to be done before my edits
//id < MTLBuffer > dstDeviceBuf = reinterpret_cast<id<MTLBuffer> >(p_Output);

//My attempt at creating a Shared memory buffer
MTLResourceOptions bufferOptions = MTLResourceStorageModeShared;
int bufferLength = sizeof(float)*1920*1080*4;
id <MTLBuffer> dstDeviceBuf = [device newBufferWithBytes:p_Output length:bufferLength options:bufferOptions];

id<MTLCommandBuffer> commandBuffer = [queue commandBuffer];
commandBuffer.label = [NSString stringWithFormat:@"RunMetalKernel"];

id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];
//First method to be computed
[computeEncoder setComputePipelineState:_initModule];
int exeWidth = [_initModule threadExecutionWidth];

MTLSize threadGroupCount = MTLSizeMake(exeWidth, 1, 1);
MTLSize threadGroups = MTLSizeMake((p_Width + exeWidth - 1) / exeWidth,
        p_Height, 1);

[computeEncoder setBuffer:srcDeviceBuf offset: 0 atIndex: 0];
[computeEncoder setBuffer:dstDeviceBuf offset: 0 atIndex: 8];

//encodes first module to be executed
[computeEncoder dispatchThreadgroups:threadGroups threadsPerThreadgroup: threadGroupCount];

//Modules encoding
if (p_lutexport_on) {
    //Fills the image with patch values for the LUT computation
    [computeEncoder setComputePipelineState:_LUTExportModule];
    [computeEncoder dispatchThreadgroups:threadGroups threadsPerThreadgroup: threadGroupCount];
}

[computeEncoder endEncoding];
[commandBuffer commit];


if (p_lutexport_on) {
    
    //Here is where I try to read the buffer values (and inserts them into a custom object "p_lut_exp_lut"

    float* result = static_cast<float*>([dstDeviceBuf contents]);

    //Retrieve the output values and populate the LUT with them
    int lutLine = 0;
    float3 out;
    for (int index(0); index < 35937 * 4; index += 4) {
        out.x = result[index];
        out.y = result[index + 1];
        out.z = result[index + 2];

        p_lutexp_lut->setValuesAtLine(lutLine, out);
        lutLine++;
    }

    p_lutexp_lut->toFile();
}

Upvotes: 2

Views: 811

Answers (1)

Hamid Yusifli
Hamid Yusifli

Reputation: 10137

If a command buffer includes write or read operations on a given MTLBuffer, you must ensure that these operations complete before reading the buffers contents. You can use the addCompletedHandler: method, waitUntilCompleted method, or custom semaphores to signal that a command buffer has completed execution.

[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
    /* read or write buffer here */
}];
[commandBuffer commit];

Upvotes: 1

Related Questions