segross / UnrealImGui

Unreal plug-in that integrates Dear ImGui framework into Unreal Engine 4.
MIT License
666 stars 211 forks source link

Dynamic UTexture2D appears white in ImGui #74

Open davidtphx opened 1 year ago

davidtphx commented 1 year ago

Hello!

I'm trying to display a texture in ImGui that I build at runtime and regularly update in my code. However, any custom transient texture I create that way doesn't seem to be displayed - code doesn't crash, but ImGui only displays a white texture instead of the one I wrote (a yellow test texture).

In essence, here is the code I'm using.

CurrentTexture is a transient UTexture2D* UPROPERTY on an actor. In BeginPlay,

CurrentTexture = UTexture2D::CreateTransient(256, 256, EPixelFormat::PF_R8G8B8A8_UINT);
CurrentTexture->UpdateResource();

In Tick, where I'm in the process of writing in my ImGui window:

uint8* Pixels = new uint8[4 * 256 * 256];

for (int32 TextureY = 0; TextureY < 256; ++TextureY)
{
    int32 RowOffset = TextureY * 256;
    for (int32 TextureX = 0; TextureX < 256; ++TextureX)
    {
        int32 PixelIndex = RowOffset + TextureX;

        // Something more interesting should happen here, but let's say I fill the texture with yellow pixels
        Pixels[4 * PixelIndex]     = 255;
        Pixels[4 * PixelIndex + 1] = 255;
        Pixels[4 * PixelIndex + 2] = 0;
        Pixels[4 * PixelIndex + 3] = 255;
    }
}

// This doesn't seem to be doing anything:
State.CurrentTexture->UpdateTextureRegions(
    0,
    1,
    new FUpdateTextureRegion2D(
        0, 0, 0, 0,
        256,
        256
    ),
    4 * 256,
    4,
    Pixels,
    [](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
    {
        delete UpdateRegion;
        delete Data;
    });

// This didn't work either (Pixels was then on the stack):
// uint8* TextureData = static_cast<uint8*>(State.CurrentTexture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE));
// FMemory::Memcpy(TextureData, Pixels, 4 * sizeof(uint8) * 256 * 256);
// State.CurrentTexture->GetPlatformData()->Mips[0].BulkData.Unlock();

State.CurrentTexture->UpdateResource();
State.ImguiTextureHandle = FImGuiModule::Get().RegisterTexture("WorldDebugMinimap", State.CurrentTexture, false);

// Draw the texture.
ImGui::Image(
    State.ImguiTextureHandle,
    ImVec2(256, 256));

What am I doing wrong? Am I missing some flags on the UTexture2D? Does it need to be uploaded manually on the GPU? Do I need to call FImGuiModule::RegisterTexture every time I update the texture, or only once (I tried both, and it doesn't change anything). I tried using RenderDoc, and it seems the texture ImGui draws is a 1x1 white texture in my 256x256 ImGui::Image(...) call. I used a breakpoint in the tick, and was seeing the texture containing the right data... so it seems more like a gpu upload issue?

Any help much appreciated <3 The doc is not super clear for playing with textures, and the demo doesn't feature something with a dynamic transient one either.

Thanks in advance! Awesome plugin :)

davidtphx commented 1 year ago

Found the issue. Seems like the texture format EPixelFormat::PF_R8G8B8A8_UINT is not supported; using EPixelFormat::PFR8G8B8A8 just works --

Would be super nice to see that documented somewhere, and/or fixed :)