mirror of
https://github.com/ocornut/imgui.git
synced 2025-09-06 19:38:28 +00:00
Backends: WGPU: added ImGuiBackendFlags_RendererHasTextures support. (#8465)
This commit is contained in:
@@ -6,8 +6,7 @@
|
||||
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
// Missing features or Issues:
|
||||
// [ ] Renderer: Missing texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures).
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -19,6 +18,7 @@
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-06-12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. (#8465)
|
||||
// 2025-02-26: Recreate image bind groups during render. (#8426, #8046, #7765, #8027) + Update for latest webgpu-native changes.
|
||||
// 2024-10-14: Update Dawn support for change of string usages. (#8082, #8083)
|
||||
// 2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
@@ -73,11 +73,15 @@ extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed);
|
||||
#define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro).
|
||||
|
||||
// WebGPU data
|
||||
struct ImGui_ImplWGPU_Texture
|
||||
{
|
||||
WGPUTexture Texture = nullptr;
|
||||
WGPUTextureView TextureView = nullptr;
|
||||
};
|
||||
|
||||
struct RenderResources
|
||||
{
|
||||
WGPUTexture FontTexture = nullptr; // Font texture
|
||||
WGPUTextureView FontTextureView = nullptr; // Texture view for font texture
|
||||
WGPUSampler Sampler = nullptr; // Sampler for the font texture
|
||||
WGPUSampler Sampler = nullptr; // Sampler for textures
|
||||
WGPUBuffer Uniforms = nullptr; // Shader uniforms
|
||||
WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline
|
||||
ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map)
|
||||
@@ -234,23 +238,8 @@ static void SafeRelease(WGPUShaderModule& res)
|
||||
wgpuShaderModuleRelease(res);
|
||||
res = nullptr;
|
||||
}
|
||||
static void SafeRelease(WGPUTextureView& res)
|
||||
{
|
||||
if (res)
|
||||
wgpuTextureViewRelease(res);
|
||||
res = nullptr;
|
||||
}
|
||||
static void SafeRelease(WGPUTexture& res)
|
||||
{
|
||||
if (res)
|
||||
wgpuTextureRelease(res);
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
static void SafeRelease(RenderResources& res)
|
||||
{
|
||||
SafeRelease(res.FontTexture);
|
||||
SafeRelease(res.FontTextureView);
|
||||
SafeRelease(res.Sampler);
|
||||
SafeRelease(res.Uniforms);
|
||||
SafeRelease(res.CommonBindGroup);
|
||||
@@ -381,6 +370,13 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
|
||||
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplWGPU_UpdateTexture(tex);
|
||||
|
||||
// FIXME: Assuming that this only gets called once per frame!
|
||||
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
||||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||
@@ -536,33 +532,52 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
}
|
||||
|
||||
static void ImGui_ImplWGPU_CreateFontsTexture()
|
||||
static void ImGui_ImplWGPU_DestroyTexture(ImTextureData* tex)
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height, size_pp;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp);
|
||||
ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData;
|
||||
if (backend_tex == nullptr)
|
||||
return;
|
||||
|
||||
// Upload texture to graphics system
|
||||
IM_ASSERT(backend_tex->TextureView == (WGPUTextureView)(intptr_t)tex->TexID);
|
||||
wgpuTextureViewRelease(backend_tex->TextureView);
|
||||
wgpuTextureRelease(backend_tex->Texture);
|
||||
IM_DELETE(backend_tex);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
tex->BackendUserData = nullptr;
|
||||
}
|
||||
|
||||
void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
ImGui_ImplWGPU_Texture* backend_tex = IM_NEW(ImGui_ImplWGPU_Texture)();
|
||||
|
||||
// Create texture
|
||||
WGPUTextureDescriptor tex_desc = {};
|
||||
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
|
||||
tex_desc.label = { "Dear ImGui Font Texture", WGPU_STRLEN };
|
||||
tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN };
|
||||
#else
|
||||
tex_desc.label = "Dear ImGui Font Texture";
|
||||
tex_desc.label = "Dear ImGui Texture";
|
||||
#endif
|
||||
tex_desc.dimension = WGPUTextureDimension_2D;
|
||||
tex_desc.size.width = width;
|
||||
tex_desc.size.height = height;
|
||||
tex_desc.size.width = tex->Width;
|
||||
tex_desc.size.height = tex->Height;
|
||||
tex_desc.size.depthOrArrayLayers = 1;
|
||||
tex_desc.sampleCount = 1;
|
||||
tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
|
||||
tex_desc.mipLevelCount = 1;
|
||||
tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
|
||||
bd->renderResources.FontTexture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc);
|
||||
backend_tex->Texture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc);
|
||||
|
||||
// Create texture view
|
||||
WGPUTextureViewDescriptor tex_view_desc = {};
|
||||
tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm;
|
||||
tex_view_desc.dimension = WGPUTextureViewDimension_2D;
|
||||
@@ -571,19 +586,35 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
|
||||
tex_view_desc.baseArrayLayer = 0;
|
||||
tex_view_desc.arrayLayerCount = 1;
|
||||
tex_view_desc.aspect = WGPUTextureAspect_All;
|
||||
bd->renderResources.FontTextureView = wgpuTextureCreateView(bd->renderResources.FontTexture, &tex_view_desc);
|
||||
backend_tex->TextureView = wgpuTextureCreateView(backend_tex->Texture, &tex_view_desc);
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)backend_tex->TextureView);
|
||||
tex->BackendUserData = backend_tex;
|
||||
// We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below.
|
||||
}
|
||||
|
||||
// Upload texture data
|
||||
if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData;
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
|
||||
// We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture.
|
||||
const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x;
|
||||
const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y;
|
||||
const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w;
|
||||
const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h;
|
||||
|
||||
// Update full texture or selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions.
|
||||
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
|
||||
WGPUTexelCopyTextureInfo dst_view = {};
|
||||
#else
|
||||
WGPUImageCopyTexture dst_view = {};
|
||||
#endif
|
||||
dst_view.texture = bd->renderResources.FontTexture;
|
||||
dst_view.texture = backend_tex->Texture;
|
||||
dst_view.mipLevel = 0;
|
||||
dst_view.origin = { 0, 0, 0 };
|
||||
dst_view.origin = { (uint32_t)upload_x, (uint32_t)upload_y, 0 };
|
||||
dst_view.aspect = WGPUTextureAspect_All;
|
||||
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
|
||||
WGPUTexelCopyBufferLayout layout = {};
|
||||
@@ -591,15 +622,14 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
|
||||
WGPUTextureDataLayout layout = {};
|
||||
#endif
|
||||
layout.offset = 0;
|
||||
layout.bytesPerRow = width * size_pp;
|
||||
layout.rowsPerImage = height;
|
||||
WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 };
|
||||
wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size);
|
||||
layout.bytesPerRow = tex->Width * tex->BytesPerPixel;
|
||||
layout.rowsPerImage = upload_h;
|
||||
WGPUExtent3D write_size = { (uint32_t)upload_w, (uint32_t)upload_h, 1 };
|
||||
wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, tex->GetPixelsAt(upload_x, upload_y), (uint32_t)(tex->Width * upload_h * tex->BytesPerPixel), &layout, &write_size);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
|
||||
io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView);
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
|
||||
ImGui_ImplWGPU_DestroyTexture(tex);
|
||||
}
|
||||
|
||||
static void ImGui_ImplWGPU_CreateUniformBuffer()
|
||||
@@ -743,7 +773,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||
|
||||
bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc);
|
||||
|
||||
ImGui_ImplWGPU_CreateFontsTexture();
|
||||
ImGui_ImplWGPU_CreateUniformBuffer();
|
||||
|
||||
// Create sampler
|
||||
@@ -788,8 +817,10 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
|
||||
SafeRelease(bd->pipelineState);
|
||||
SafeRelease(bd->renderResources);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->SetTexID(0); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
ImGui_ImplWGPU_DestroyTexture(tex);
|
||||
|
||||
for (unsigned int i = 0; i < bd->numFramesInFlight; i++)
|
||||
SafeRelease(bd->pFrameResources[i]);
|
||||
@@ -814,6 +845,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
|
||||
io.BackendRendererName = "imgui_impl_webgpu";
|
||||
#endif
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
bd->initInfo = *init_info;
|
||||
bd->wgpuDevice = init_info->Device;
|
||||
@@ -823,8 +855,6 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
|
||||
bd->numFramesInFlight = init_info->NumFramesInFlight;
|
||||
bd->frameIndex = UINT_MAX;
|
||||
|
||||
bd->renderResources.FontTexture = nullptr;
|
||||
bd->renderResources.FontTextureView = nullptr;
|
||||
bd->renderResources.Sampler = nullptr;
|
||||
bd->renderResources.Uniforms = nullptr;
|
||||
bd->renderResources.CommonBindGroup = nullptr;
|
||||
@@ -863,7 +893,7 @@ void ImGui_ImplWGPU_Shutdown()
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user