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.