diff --git a/src/engine/client/backend/opengl/backend_opengl.cpp b/src/engine/client/backend/opengl/backend_opengl.cpp index d892ca472..2e3223871 100644 --- a/src/engine/client/backend/opengl/backend_opengl.cpp +++ b/src/engine/client/backend/opengl/backend_opengl.cpp @@ -937,7 +937,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_ { glDisable(GL_SCISSOR_TEST); } - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if(ClipWasEnabled) { diff --git a/src/engine/client/backend/opengl/backend_opengl3.cpp b/src/engine/client/backend/opengl/backend_opengl3.cpp index b4e15dbb5..3f4fb575a 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.cpp +++ b/src/engine/client/backend/opengl/backend_opengl3.cpp @@ -414,6 +414,8 @@ bool CCommandProcessorFragment_OpenGL3_3::Cmd_Init(const SCommand_Init *pCommand m_vTextures.resize(CCommandBuffer::MAX_TEXTURES); + m_ClearColor.r = m_ClearColor.g = m_ClearColor.b = -1.f; + // fix the alignment to allow even 1byte changes, e.g. for alpha components glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -685,7 +687,11 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Clear(const CCommandBuffer::SComma { glDisable(GL_SCISSOR_TEST); } - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + if(pCommand->m_Color.r != m_ClearColor.r || pCommand->m_Color.g != m_ClearColor.g || pCommand->m_Color.b != m_ClearColor.b) + { + glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f); + m_ClearColor = pCommand->m_Color; + } glClear(GL_COLOR_BUFFER_BIT); if(ClipWasEnabled) { diff --git a/src/engine/client/backend/opengl/backend_opengl3.h b/src/engine/client/backend/opengl/backend_opengl3.h index 93e0347e6..05b82a4df 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.h +++ b/src/engine/client/backend/opengl/backend_opengl3.h @@ -67,6 +67,8 @@ protected: std::vector m_vBufferObjectIndices; + CCommandBuffer::SColorf m_ClearColor; + void InitPrimExProgram(CGLSLPrimitiveExProgram *pProgram, class CGLSLCompiler *pCompiler, class IStorage *pStorage, bool Textured, bool Rotationless); bool IsNewApi() override { return true; } diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index f16832b5d..edc9eda4d 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -1059,6 +1059,8 @@ private: SDL_Window *m_pWindow; + std::array m_aClearColor = {0, 0, 0, 0}; + struct SRenderCommandExecuteBuffer { CCommandBuffer::ECommandBufferCMD m_Command; @@ -1075,6 +1077,8 @@ private: VkBuffer m_IndexBuffer; + bool m_ClearColorInRenderThread = false; + bool m_HasDynamicState = false; VkViewport m_Viewport; VkRect2D m_Scissor; @@ -2441,7 +2445,7 @@ protected: RenderPassInfo.renderArea.offset = {0, 0}; RenderPassInfo.renderArea.extent = m_VKSwapImgAndViewportExtent.m_SwapImageViewport; - VkClearValue ClearColorVal = {{{0.0f, 0.0f, 0.0f, 0.0f}}}; + VkClearValue ClearColorVal = {{{m_aClearColor[0], m_aClearColor[1], m_aClearColor[2], m_aClearColor[3]}}}; RenderPassInfo.clearValueCount = 1; RenderPassInfo.pClearValues = &ClearColorVal; @@ -6711,19 +6715,37 @@ public: void Cmd_Clear_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Clear *pCommand) { + if(!pCommand->m_ForceClear) + { + bool ColorChanged = m_aClearColor[0] != pCommand->m_Color.r || m_aClearColor[1] != pCommand->m_Color.g || + m_aClearColor[2] != pCommand->m_Color.b || m_aClearColor[3] != pCommand->m_Color.a; + m_aClearColor[0] = pCommand->m_Color.r; + m_aClearColor[1] = pCommand->m_Color.g; + m_aClearColor[2] = pCommand->m_Color.b; + m_aClearColor[3] = pCommand->m_Color.a; + if(ColorChanged) + ExecBuffer.m_ClearColorInRenderThread = true; + } + else + { + ExecBuffer.m_ClearColorInRenderThread = true; + } ExecBuffer.m_EstimatedRenderCallCount = 0; } [[nodiscard]] bool Cmd_Clear(const SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Clear *pCommand) { - std::array aAttachments = {VkClearAttachment{VK_IMAGE_ASPECT_COLOR_BIT, 0, VkClearValue{VkClearColorValue{{0.0f, 0.0f, 0.0f, 0.0f}}}}}; - std::array aClearRects = {VkClearRect{{{0, 0}, m_VKSwapImgAndViewportExtent.m_SwapImageViewport}, 0, 1}}; + if(ExecBuffer.m_ClearColorInRenderThread) + { + std::array aAttachments = {VkClearAttachment{VK_IMAGE_ASPECT_COLOR_BIT, 0, VkClearValue{VkClearColorValue{{pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, pCommand->m_Color.a}}}}}; + std::array aClearRects = {VkClearRect{{{0, 0}, m_VKSwapImgAndViewportExtent.m_SwapImageViewport}, 0, 1}}; - VkCommandBuffer *pCommandBuffer; - if(!GetGraphicCommandBuffer(pCommandBuffer, ExecBuffer.m_ThreadIndex)) - return false; - auto &CommandBuffer = *pCommandBuffer; - vkCmdClearAttachments(CommandBuffer, aAttachments.size(), aAttachments.data(), aClearRects.size(), aClearRects.data()); + VkCommandBuffer *pCommandBuffer; + if(!GetGraphicCommandBuffer(pCommandBuffer, ExecBuffer.m_ThreadIndex)) + return false; + auto &CommandBuffer = *pCommandBuffer; + vkCmdClearAttachments(CommandBuffer, aAttachments.size(), aAttachments.data(), aClearRects.size(), aClearRects.data()); + } return true; } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index ffee9fc48..5236209de 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -891,7 +891,17 @@ const char *CClient::ErrorString() const void CClient::Render() { - Graphics()->Clear(); + if(g_Config.m_ClOverlayEntities) + { + ColorRGBA bg = color_cast(ColorHSLA(g_Config.m_ClBackgroundEntitiesColor)); + Graphics()->Clear(bg.r, bg.g, bg.b); + } + else + { + ColorRGBA bg = color_cast(ColorHSLA(g_Config.m_ClBackgroundColor)); + Graphics()->Clear(bg.r, bg.g, bg.b); + } + GameClient()->OnRender(); DebugRender(); @@ -2718,7 +2728,7 @@ void CClient::Run() } // make sure the first frame just clears everything to prevent undesired colors when waiting for io - Graphics()->Clear(); + Graphics()->Clear(0, 0, 0); Graphics()->Swap(); // init sound, allowed to fail @@ -3773,7 +3783,7 @@ void CClient::UpdateAndSwap() { Input()->Update(); Graphics()->Swap(); - Graphics()->Clear(); + Graphics()->Clear(0, 0, 0); } void CClient::ServerBrowserUpdate() diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 8fbb1841d..ec2642fd6 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -833,9 +833,14 @@ void CGraphics_Threaded::TextureSet(CTextureHandle TextureId) m_State.m_Texture = TextureId.Id(); } -void CGraphics_Threaded::Clear() +void CGraphics_Threaded::Clear(float r, float g, float b, bool ForceClearNow) { CCommandBuffer::SCommand_Clear Cmd; + Cmd.m_Color.r = r; + Cmd.m_Color.g = g; + Cmd.m_Color.b = b; + Cmd.m_Color.a = 0; + Cmd.m_ForceClear = ForceClearNow; AddCmd(Cmd); } diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index adfb06779..3255c67aa 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -213,6 +213,8 @@ public: { SCommand_Clear() : SCommand(CMD_CLEAR) {} + SColorf m_Color; + bool m_ForceClear; }; struct SCommand_Signal : public SCommand @@ -989,7 +991,7 @@ public: void TextureSet(CTextureHandle TextureId) override; - void Clear() override; + void Clear(float r, float g, float b, bool ForceClearNow = false) override; void QuadsBegin() override; void QuadsEnd() override; diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 7fd9bfc3a..14017affd 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -306,7 +306,8 @@ public: virtual void WindowDestroyNtf(uint32_t WindowId) = 0; virtual void WindowCreateNtf(uint32_t WindowId) = 0; - virtual void Clear() = 0; + // ForceClearNow forces the backend to trigger a clear, even at performance cost, else it might be delayed by one frame + virtual void Clear(float r, float g, float b, bool ForceClearNow = false) = 0; virtual void ClipEnable(int x, int y, int w, int h) = 0; virtual void ClipDisable() = 0; diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 496f954da..681e6d777 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1412,23 +1412,6 @@ void CMapLayers::OnRender() CUIRect Screen; Graphics()->GetScreen(&Screen.x, &Screen.y, &Screen.w, &Screen.h); - if(m_Type == TYPE_BACKGROUND || m_Type == TYPE_BACKGROUND_FORCE) - { - Graphics()->TextureClear(); - Graphics()->QuadsBegin(); - if(g_Config.m_ClOverlayEntities) - { - Graphics()->SetColor(color_cast(ColorHSLA(g_Config.m_ClBackgroundEntitiesColor))); - } - else - { - Graphics()->SetColor(color_cast(ColorHSLA(g_Config.m_ClBackgroundColor))); - } - IGraphics::CQuadItem QuadItem(Screen.x, Screen.y, Screen.w, Screen.h); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); - } - vec2 Center = GetCurCamera()->m_Center; bool PassedGameLayer = false; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 9b30e7f8b..d41cd9607 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1092,12 +1092,12 @@ void CGameClient::RenderShutdownMessage() dbg_assert(false, "Invalid client state for quitting message"); // This function only gets called after the render loop has already terminated, so we have to call Swap manually. - Graphics()->Clear(); + Graphics()->Clear(0.0f, 0.0f, 0.0f); Ui()->MapScreen(); TextRender()->TextColor(TextRender()->DefaultTextColor()); Ui()->DoLabel(Ui()->Screen(), pMessage, 16.0f, TEXTALIGN_MC); Graphics()->Swap(); - Graphics()->Clear(); + Graphics()->Clear(0.0f, 0.0f, 0.0f); } void CGameClient::OnRconType(bool UsernameReq) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 8b8ef2184..dd6ba9777 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -7718,7 +7718,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar) void CEditor::Render() { // basic start - Graphics()->Clear(); + Graphics()->Clear(0.0f, 0.0f, 0.0f); CUIRect View = *Ui()->Screen(); Ui()->MapScreen(); m_CursorType = CURSOR_NORMAL;