diff --git a/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs b/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs index 6056c07..08357cf 100644 --- a/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs +++ b/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs @@ -19,16 +19,14 @@ public static float JoyAxisDeadZone } /// - /// Setting this property will reload fonts and modify the ImGuiStyle + /// Setting this property will reload fonts and modify the ImGuiStyle. + /// Can only be set outside of a process frame (eg, use CallDeferred) /// public static float Scale { get => _backend.Scale; set { - //if (_inProcessFrame) - // throw new InvalidOperationException("scale cannot be changed during process frame"); - if (_backend.Scale != value && value >= 0.25f) { _backend.Scale = value; @@ -89,9 +87,6 @@ public static void AddFontDefault() public static void RebuildFontAtlas() { - //if (_inProcessFrame) - // throw new InvalidOperationException("fonts cannot be changed during process frame"); - _backend.RebuildFontAtlas(); } @@ -105,6 +100,9 @@ public static void Connect(Action action) Connect(Callable.From(action)); } + /// + /// Must call from a tool script before doing anything else + /// public static bool ToolInit() { if (_backend is BackendNative nbe) diff --git a/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs b/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs index 2b3a52e..757e3b5 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs @@ -168,11 +168,12 @@ private void SetupBuffers(ImDrawDataPtr drawData) int globalIdxOffset = 0; int globalVtxOffset = 0; - // set up buffers int idxBufSize = drawData.TotalIdxCount * sizeof(ushort); byte[] idxBuf = _bufPool.Rent(idxBufSize); + int vertBufSize = drawData.TotalVtxCount * vertSize; byte[] vertBuf = _bufPool.Rent(vertBufSize); + for (int i = 0; i < drawData.CmdLists.Size; ++i) { ImDrawListPtr cmdList = drawData.CmdLists[i]; diff --git a/gdext/src/RdRenderer.cpp b/gdext/src/RdRenderer.cpp index a1c493a..ccc2df0 100644 --- a/gdext/src/RdRenderer.cpp +++ b/gdext/src/RdRenderer.cpp @@ -47,7 +47,6 @@ struct RdRenderer::Impl int vtxBufferSize = 0; // size in vertices void SetupBuffers(ImDrawData* drawData); - void RenderOne(RID vprid, ImDrawData* drawData); Impl() { @@ -84,35 +83,6 @@ void RdRenderer::Impl::SetupBuffers(ImDrawData* drawData) { RenderingDevice* RD = RenderingServer::get_singleton()->get_rendering_device(); - // clean out invalidated uniform sets so they can be refreshed - for (auto it = uniformSets.begin(); it != uniformSets.end();) - { - if (!RD->uniform_set_is_valid(it->second)) - it = uniformSets.erase(it); - else - ++it; - } - - // allocate merged index and vertex buffers - if (idxBufferSize < drawData->TotalIdxCount) - { - if (idxBuffer.get_id() != 0) - RD->free_rid(idxBuffer); - idxBuffer = RD->index_buffer_create(drawData->TotalIdxCount, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16); - idxBufferSize = drawData->TotalIdxCount; - } - - if (vtxBufferSize < drawData->TotalVtxCount) - { - if (vtxBuffer.get_id() != 0) - RD->free_rid(vtxBuffer); - vtxBuffer = RD->vertex_buffer_create(drawData->TotalVtxCount * sizeof(ImDrawVert)); - vtxBufferSize = drawData->TotalVtxCount; - } - - if (drawData->TotalIdxCount == 0) - return; - int globalIdxOffset = 0; int globalVtxOffset = 0; @@ -128,27 +98,30 @@ void RdRenderer::Impl::SetupBuffers(ImDrawData* drawData) { ImDrawList* cmdList = drawData->CmdLists[i]; - std::copy(cmdList->IdxBuffer.begin(), - cmdList->IdxBuffer.end(), - reinterpret_cast(idxBuf.ptrw() + globalIdxOffset)); std::copy(cmdList->VtxBuffer.begin(), cmdList->VtxBuffer.end(), reinterpret_cast(vertBuf.ptrw() + globalVtxOffset)); + globalVtxOffset += cmdList->VtxBuffer.size_in_bytes(); + std::copy(cmdList->IdxBuffer.begin(), + cmdList->IdxBuffer.end(), + reinterpret_cast(idxBuf.ptrw() + globalIdxOffset)); globalIdxOffset += cmdList->IdxBuffer.size_in_bytes(); - globalVtxOffset += cmdList->VtxBuffer.size_in_bytes(); + // create a uniform set for each texture for (int cmdi = 0; cmdi < cmdList->CmdBuffer.Size; ++cmdi) { const ImDrawCmd& drawCmd = cmdList->CmdBuffer[cmdi]; ImTextureID texid = drawCmd.GetTexID(); if (!texid) continue; + RID texrid = make_rid(texid); + if (!RD->texture_is_valid(texrid)) + continue; usedTextures.insert(texid); if (!uniformSets.contains(texid)) { - RID texrid = make_rid(texid); Ref uniform; uniform.instantiate(); uniform->set_binding(0); @@ -157,9 +130,7 @@ void RdRenderer::Impl::SetupBuffers(ImDrawData* drawData) uniform->add_id(texrid); uniforms[0] = uniform; - - RID uniformSet = RD->uniform_set_create(uniforms, shader, 0); - uniformSets[texid] = uniformSet; + uniformSets[texid] = RD->uniform_set_create(uniforms, shader, 0); } } } @@ -241,6 +212,9 @@ bool RdRenderer::Init() {}, blend); + if (!impl->pipeline.is_valid()) + return false; + RDSamplerState sampler_state; sampler_state.set_min_filter(RenderingDevice::SAMPLER_FILTER_LINEAR); sampler_state.set_mag_filter(RenderingDevice::SAMPLER_FILTER_LINEAR); @@ -260,7 +234,8 @@ void RdRenderer::Render(RID fb, ImDrawData* drawData) { RenderingDevice* RD = RenderingServer::get_singleton()->get_rendering_device(); - impl->SetupBuffers(drawData); + if (!fb.is_valid()) + return; godot::PackedFloat32Array pcfloats; pcfloats.resize(4); @@ -270,6 +245,30 @@ void RdRenderer::Render(RID fb, ImDrawData* drawData) pcfloats[3] = -1.0f - (drawData->DisplayPos.y * pcfloats[1]); godot::PackedByteArray pcbuf = pcfloats.to_byte_array(); + // allocate merged index and vertex buffers + if (impl->idxBufferSize < drawData->TotalIdxCount) + { + if (impl->idxBuffer.get_id() != 0) + RD->free_rid(impl->idxBuffer); + impl->idxBuffer = RD->index_buffer_create(drawData->TotalIdxCount, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16); + impl->idxBufferSize = drawData->TotalIdxCount; + } + + if (impl->vtxBufferSize < drawData->TotalVtxCount) + { + if (impl->vtxBuffer.get_id() != 0) + RD->free_rid(impl->vtxBuffer); + impl->vtxBuffer = RD->vertex_buffer_create(drawData->TotalVtxCount * sizeof(ImDrawVert)); + impl->vtxBufferSize = drawData->TotalVtxCount; + } + + // check if our font texture is still valid + std::erase_if(impl->uniformSets, [RD](const auto& kv) { return !RD->uniform_set_is_valid(kv.second); }); + + if (drawData->CmdListsCount > 0) + impl->SetupBuffers(drawData); + + // draw int64_t dl = RD->draw_list_begin(fb, RenderingDevice::INITIAL_ACTION_CLEAR, RenderingDevice::FINAL_ACTION_READ, @@ -341,6 +340,27 @@ void RdRenderer::InitViewport(RID vprid) RenderingServer::get_singleton()->viewport_set_clear_mode(vprid, RenderingServer::VIEWPORT_CLEAR_NEVER); } +void RdRenderer::FreeUnusedTextures() +{ + RenderingDevice* RD = RenderingServer::get_singleton()->get_rendering_device(); + + // clean up unused textures + std::vector keys; + keys.reserve(impl->uniformSets.size()); + for (const auto& kv : impl->uniformSets) + keys.push_back(kv.first); + + for (ImTextureID texid : keys) + { + if (!impl->usedTextures.contains(texid)) + { + RD->free_rid(impl->uniformSets[texid]); + impl->uniformSets.erase(texid); + } + } + impl->usedTextures.clear(); +} + void RdRenderer::Render() { auto& pio = ImGui::GetPlatformIO(); @@ -353,6 +373,7 @@ void RdRenderer::Render() Render(GetFramebuffer(vprid), vp->DrawData); } } + FreeUnusedTextures(); } void RdRenderer::ReplaceTextureRIDs(ImDrawData* drawData) diff --git a/gdext/src/RdRenderer.h b/gdext/src/RdRenderer.h index 4034986..6c2c778 100644 --- a/gdext/src/RdRenderer.h +++ b/gdext/src/RdRenderer.h @@ -33,6 +33,7 @@ class RdRenderer : public Renderer void Render(RID fb, ImDrawData* drawData); static void ReplaceTextureRIDs(ImDrawData* drawData); RID GetFramebuffer(RID vprid); + void FreeUnusedTextures(); private: struct Impl; diff --git a/gdext/src/RdRendererThreadSafe.cpp b/gdext/src/RdRendererThreadSafe.cpp index 3f81adc..b55bbe5 100644 --- a/gdext/src/RdRendererThreadSafe.cpp +++ b/gdext/src/RdRendererThreadSafe.cpp @@ -1,5 +1,6 @@ #include "RdRendererThreadSafe.h" #include "common.h" +#include #include #include @@ -51,6 +52,8 @@ struct RdRendererThreadSafe::Impl RdRendererThreadSafe::RdRendererThreadSafe() : impl(std::make_unique()) { + RenderingServer* RS = RenderingServer::get_singleton(); + RS->connect("frame_pre_draw", Callable(Engine::get_singleton()->get_singleton("ImGuiLayer"), "on_frame_pre_draw")); } RdRendererThreadSafe::~RdRendererThreadSafe() @@ -64,8 +67,10 @@ void RdRendererThreadSafe::Render() for (int i = 0; i < pio.Viewports.size(); ++i) { - // TODO: skip minimized windows ImGuiViewport* vp = pio.Viewports[i]; + if (vp->Flags & ImGuiViewportFlags_IsMinimized) + continue; + ReplaceTextureRIDs(vp->DrawData); RID vprid = make_rid(vp->RendererUserData); newData[i].first = GetFramebuffer(vprid); @@ -88,15 +93,14 @@ void RdRendererThreadSafe::OnFramePreDraw() dataArray = std::move(impl->dataToDraw); } - if (dataArray.size() == 0) - return; - RenderingDevice* RD = RenderingServer::get_singleton()->get_rendering_device(); for (auto& kv : dataArray) { if (RD->framebuffer_is_valid(kv.first)) RdRenderer::Render(kv.first, kv.second->data); } + + FreeUnusedTextures(); } } // namespace ImGui::Godot