Skip to content

Instantly share code, notes, and snippets.

@meshula
Created January 7, 2025 23:55
Show Gist options
  • Save meshula/656875909c8b11fa22ac1055085cca55 to your computer and use it in GitHub Desktop.
Save meshula/656875909c8b11fa22ac1055085cca55 to your computer and use it in GitHub Desktop.

First, invoke hydra to render, then fetch the texture from the HdxTaskController, which is on HdEngine.

    // do the render
    _engine->Render();

    auto tc = _engine->GetHdxTaskController();
    HdRenderBuffer* buffer = tc->GetRenderOutput(HdAovTokens->color);
    buffer->Resolve();
    VtValue aov = buffer->GetResource(false);
    if (aov.IsHolding<HgiTextureHandle>()) {
        HgiTextureHandle textureHandle = aov.Get<HgiTextureHandle>();
        if (textureHandle) {
            HgiTextureDesc const& desc = textureHandle->GetDescriptor();

            Hgi* hgi = _engine->GetHgi();
            if (hgi) {
                auto buffer = GetGPUTexture(*hgi,
                                            textureHandle,
                                            width, height,
                                            desc.format);

                if (texcap.width != width || texcap.height != height || texcap.handle < 0) {
                    if (texcap.handle > 0) {
                        LabRemoveTexture(texcap.handle);
                    }
                    texcap.width = width;
                    texcap.height = height;
                    texcap.handle = LabCreateRGBAf16Texture(width, height, buffer);
                }
                else {
                    LabUpdateRGBAf16Texture(texcap.handle, (uint8_t*) buffer);
                }

                ImGui::Image((ImTextureID) LabTextureHardwareHandle(texcap.handle),
                             ImVec2(width, height),
                             ImVec2(0, 1), ImVec2(1, 0));
            }
        }
    }

Getting the GPU texture: the texture copy is completed synchronously, so Hydra won't overwrite the buffer while you are using it.

static uint8_t*
GetGPUTexture(
    Hgi& hgi,
    HgiTextureHandle const& texHandle,
    int width,
    int height,
    HgiFormat format)
{
    // Copy the pixels from gpu into a cpu buffer so we can save it to disk.
    const size_t bufferByteSize =
        width * height * HgiGetDataSizeOfFormat(format);
    static uint8_t* buffer = nullptr;
    static size_t sz = 0;
    if (sz < bufferByteSize) {
        if (buffer) {
            free(buffer);
            buffer = nullptr;
        }
        sz = 0;
    }
    if (!buffer) {
        buffer = (uint8_t*) malloc(bufferByteSize);
        sz = bufferByteSize;
    }

    HgiTextureGpuToCpuOp copyOp;
    copyOp.gpuSourceTexture = texHandle;
    copyOp.sourceTexelOffset = GfVec3i(0);
    copyOp.mipLevel = 0;
    copyOp.cpuDestinationBuffer = buffer;
    copyOp.destinationByteOffset = 0;
    copyOp.destinationBufferByteSize = bufferByteSize;

    HgiBlitCmdsUniquePtr blitCmds = hgi.CreateBlitCmds();
    blitCmds->CopyTextureGpuToCpu(copyOp);
    hgi.SubmitCmds(blitCmds.get(), HgiSubmitWaitTypeWaitUntilCompleted);

    return buffer;
}

Here is creating a 16 bit texture:

id<MTLTexture> CreateRGBAf16Texture(id<MTLDevice> device, int width, int height,
                                  uint8_t* rgba8_pixels) {
    MTLTextureDescriptor *descriptor =
            [MTLTextureDescriptor
                     texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA16Float
                     width:width
                     height:height
                     mipmapped:NO];
    descriptor.storageMode = MTLStorageModeShared;
    descriptor.usage = MTLTextureUsageUnknown;

    id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];
    if (texture == nil) {
        printf("Could not create a Metal texture of size %d x %d\n", width, height);
        return nullptr;
    }
    
    if (rgba8_pixels) {
        [texture replaceRegion:MTLRegionMake2D(0, 0, width, height)
                   mipmapLevel:0
                     withBytes:rgba8_pixels
                   bytesPerRow:width * 8];
    }
    
    return texture;
}

Updating the texture:

extern "C"
void LabUpdateRGBAf16Texture(int texture, uint8_t* pixels) {
    if (gLabMetalProvider) {
        id<MTLTexture> txt = [gLabMetalProvider Texture:texture];
        if (txt) {
            [txt replaceRegion:MTLRegionMake2D(0, 0, txt.width, txt.height)
                   mipmapLevel:0
                     withBytes:pixels
                   bytesPerRow:txt.width * 8];
        }
    }
}

Deleting the texture is trivial, if ARC, nil all references, otherwise invoke release on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment