diff --git a/Minecraft-Community-Edition/UI/Backend/RmlUI_Backend.hpp b/Minecraft-Community-Edition/UI/Backend/RmlUI_Backend.hpp new file mode 100644 index 0000000..0ad5f57 --- /dev/null +++ b/Minecraft-Community-Edition/UI/Backend/RmlUI_Backend.hpp @@ -0,0 +1,72 @@ +/* + * This source file is part of RmlUi, the HTML/CSS Interface Middleware + * + * For the latest information, see http://github.com/mikke89/RmlUi + * + * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd + * Copyright (c) 2019-2023 The RmlUi Team, and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RMLUI_BACKENDS_BACKEND_H +#define RMLUI_BACKENDS_BACKEND_H + +#include +#include +#include +#include + +using KeyDownCallback = bool (*)(Rml::Context* context, Rml::Input::KeyIdentifier key, int key_modifier, float native_dp_ratio, bool priority); + +/** + This interface serves as a basic abstraction over the various backends included with RmlUi. It is mainly intended as an example to get something + simple up and running, and provides just enough functionality for the included samples. + + This interface may be used directly for simple applications and testing. However, for anything more advanced we recommend to use the backend as a + starting point and copy relevant parts into the main loop of your application. On the other hand, the underlying platform and renderer used by the + backend are intended to be re-usable as is. + */ +namespace Backend { + + // Initializes the backend, including the custom system and render interfaces, and opens a window for rendering the RmlUi context. + bool Initialize(const char* window_name, int width, int height, bool allow_resize); + // Closes the window and release all resources owned by the backend, including the system and render interfaces. + void Shutdown(); + + // Returns a pointer to the custom system interface which should be provided to RmlUi. + Rml::SystemInterface* GetSystemInterface(); + // Returns a pointer to the custom render interface which should be provided to RmlUi. + Rml::RenderInterface* GetRenderInterface(); + + // Polls and processes events from the current platform, and applies any relevant events to the provided RmlUi context and the key down callback. + // @return False to indicate that the application should be closed. + bool ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback = nullptr, bool power_save = false); + // Request application closure during the next event processing call. + void RequestExit(); + + // Prepares the render state to accept rendering commands from RmlUi, call before rendering the RmlUi context. + void BeginFrame(); + // Presents the rendered frame to the screen, call after rendering the RmlUi context. + void PresentFrame(); + +} // namespace Backend + +#endif \ No newline at end of file diff --git a/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.cpp b/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.cpp new file mode 100644 index 0000000..c6548b7 --- /dev/null +++ b/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.cpp @@ -0,0 +1,221 @@ +#include "RmlUI_Platform_dms.hpp" +#include + +#include +#include +#include + +namespace mce::ui::priv { + SystemInterface_dms::SystemInterface_dms(core::QEventBus& qBus) : qBus(qBus) { + SystemInterface_dms::clock.restart(); + + bool cursors_valid = true; + cursors_valid &= cursor_default.loadFromSystem(sf::Cursor::Arrow); + cursors_valid &= cursor_move.loadFromSystem(sf::Cursor::SizeAll); + cursors_valid &= cursor_pointer.loadFromSystem(sf::Cursor::Hand); + cursors_valid &= cursor_resize.loadFromSystem(sf::Cursor::SizeTopLeftBottomRight) || cursor_resize.loadFromSystem(sf::Cursor::SizeAll); + cursors_valid &= cursor_cross.loadFromSystem(sf::Cursor::Cross); + cursors_valid &= cursor_text.loadFromSystem(sf::Cursor::Text); + cursors_valid &= cursor_unavailable.loadFromSystem(sf::Cursor::NotAllowed); + } + SystemInterface_dms::~SystemInterface_dms() { + + } + void SystemInterface_dms::setWindow(sf::WindowBase* window) { + SystemInterface_dms::window = window; + } + double SystemInterface_dms::GetElapsedTime() { + return SystemInterface_dms::clock.getElapsedTime().asSeconds(); + } + // TODO we need a way to pass the window base to the SystemInterface_dms + void SystemInterface_dms::SetMouseCursor(const Rml::String& cursorName) { + if (cursorName.empty() || cursorName == "arrow") { + window->setMouseCursor(cursor_default); + } + else if (cursorName == "move") { + window->setMouseCursor(cursor_move); + } + else if (cursorName == "pointer") { + window->setMouseCursor(cursor_pointer); + } + else if (cursorName == "resize") { + window->setMouseCursor(cursor_resize); + } + else if (cursorName == "cross") { + window->setMouseCursor(cursor_cross); + } + else if (cursorName == "text") { + window->setMouseCursor(cursor_text); + } + else if (cursorName == "unavailable") { + window->setMouseCursor(cursor_unavailable); + } + else if (Rml::StringUtilities::StartsWith(cursorName, "rmlui-scroll")) { + window->setMouseCursor(cursor_move); + } + } + // TODO we need a way to pass the window base to the SystemInterface_dms + void SystemInterface_dms::SetClipboardText(const Rml::String& text) { + sf::Clipboard::setString(text); + } + // TODO we need a way to pass the window base to the SystemInterface_dms + void SystemInterface_dms::GetClipboardText(Rml::String& text) { + text = sf::Clipboard::getString(); + } + bool SystemInterface_dms::LogMessage(Rml::Log::Type type, const Rml::String& msg) { + event::Log log; + log.channel = "default"; + switch (type) { + case Rml::Log::LT_ERROR: + log.severity = event::Log::Severity::ERROR; + break; + case Rml::Log::LT_WARNING: + log.severity = event::Log::Severity::WARN; + break; + case Rml::Log::LT_INFO: + log.severity = event::Log::Severity::INFO; + break; + case Rml::Log::LT_DEBUG: + log.severity = event::Log::Severity::DEBUG; + break; + default: + log.severity = event::Log::Severity::INFO; + } + + log.msg += "RmlUI: "; + log.msg += msg; + qBus.post(log); + + return true; + } + bool inputEventHandler(Rml::Context* context, sf::WindowHandle hWnd, sf::Event& event) { + bool result = true; + + switch (event.type) { + case sf::Event::MouseMoved: result = context->ProcessMouseMove(event.mouseMove.x, event.mouseMove.y, getKeyModifierState()); break; + case sf::Event::MouseButtonPressed: result = context->ProcessMouseButtonDown(event.mouseButton.button, getKeyModifierState()); break; + case sf::Event::MouseButtonReleased: result = context->ProcessMouseButtonUp(event.mouseButton.button, getKeyModifierState()); break; + case sf::Event::MouseWheelMoved: result = context->ProcessMouseWheel(float(-event.mouseWheel.delta), getKeyModifierState()); break; + case sf::Event::MouseLeft: result = context->ProcessMouseLeave(); break; + case sf::Event::TextEntered: + { + Rml::Character character = Rml::Character(event.text.unicode); + if (character == Rml::Character('\r')) + character = Rml::Character('\n'); + + if (event.text.unicode >= 32 || character == Rml::Character('\n')) + result = context->ProcessTextInput(character); + } + break; + case sf::Event::KeyPressed: result = context->ProcessKeyDown(convertKey(event.key.code), getKeyModifierState()); break; + case sf::Event::KeyReleased: result = context->ProcessKeyUp(convertKey(event.key.code), getKeyModifierState()); break; + default: break; + } + return result; + } + Rml::Input::KeyIdentifier convertKey(sf::Keyboard::Key key) { + switch (key) { + case sf::Keyboard::Key::A: return Rml::Input::KI_A; + case sf::Keyboard::Key::B: return Rml::Input::KI_B; + case sf::Keyboard::Key::C: return Rml::Input::KI_C; + case sf::Keyboard::Key::D: return Rml::Input::KI_D; + case sf::Keyboard::Key::E: return Rml::Input::KI_E; + case sf::Keyboard::Key::F: return Rml::Input::KI_F; + case sf::Keyboard::Key::G: return Rml::Input::KI_G; + case sf::Keyboard::Key::H: return Rml::Input::KI_H; + case sf::Keyboard::Key::I: return Rml::Input::KI_I; + case sf::Keyboard::Key::J: return Rml::Input::KI_J; + case sf::Keyboard::Key::K: return Rml::Input::KI_K; + case sf::Keyboard::Key::L: return Rml::Input::KI_L; + case sf::Keyboard::Key::M: return Rml::Input::KI_M; + case sf::Keyboard::Key::N: return Rml::Input::KI_N; + case sf::Keyboard::Key::O: return Rml::Input::KI_O; + case sf::Keyboard::Key::P: return Rml::Input::KI_P; + case sf::Keyboard::Key::Q: return Rml::Input::KI_Q; + case sf::Keyboard::Key::R: return Rml::Input::KI_R; + case sf::Keyboard::Key::S: return Rml::Input::KI_S; + case sf::Keyboard::Key::T: return Rml::Input::KI_T; + case sf::Keyboard::Key::U: return Rml::Input::KI_U; + case sf::Keyboard::Key::V: return Rml::Input::KI_V; + case sf::Keyboard::Key::W: return Rml::Input::KI_W; + case sf::Keyboard::Key::X: return Rml::Input::KI_X; + case sf::Keyboard::Key::Y: return Rml::Input::KI_Y; + case sf::Keyboard::Key::Z: return Rml::Input::KI_Z; + case sf::Keyboard::Key::Num0: return Rml::Input::KI_0; + case sf::Keyboard::Key::Num1: return Rml::Input::KI_1; + case sf::Keyboard::Key::Num2: return Rml::Input::KI_2; + case sf::Keyboard::Key::Num3: return Rml::Input::KI_3; + case sf::Keyboard::Key::Num4: return Rml::Input::KI_4; + case sf::Keyboard::Key::Num5: return Rml::Input::KI_5; + case sf::Keyboard::Key::Num6: return Rml::Input::KI_6; + case sf::Keyboard::Key::Num7: return Rml::Input::KI_7; + case sf::Keyboard::Key::Num8: return Rml::Input::KI_8; + case sf::Keyboard::Key::Num9: return Rml::Input::KI_9; + case sf::Keyboard::Key::Numpad0: return Rml::Input::KI_NUMPAD0; + case sf::Keyboard::Key::Numpad1: return Rml::Input::KI_NUMPAD1; + case sf::Keyboard::Key::Numpad2: return Rml::Input::KI_NUMPAD2; + case sf::Keyboard::Key::Numpad3: return Rml::Input::KI_NUMPAD3; + case sf::Keyboard::Key::Numpad4: return Rml::Input::KI_NUMPAD4; + case sf::Keyboard::Key::Numpad5: return Rml::Input::KI_NUMPAD5; + case sf::Keyboard::Key::Numpad6: return Rml::Input::KI_NUMPAD6; + case sf::Keyboard::Key::Numpad7: return Rml::Input::KI_NUMPAD7; + case sf::Keyboard::Key::Numpad8: return Rml::Input::KI_NUMPAD8; + case sf::Keyboard::Key::Numpad9: return Rml::Input::KI_NUMPAD9; + case sf::Keyboard::Key::Left: return Rml::Input::KI_LEFT; + case sf::Keyboard::Key::Right: return Rml::Input::KI_RIGHT; + case sf::Keyboard::Key::Up: return Rml::Input::KI_UP; + case sf::Keyboard::Key::Down: return Rml::Input::KI_DOWN; + case sf::Keyboard::Key::Add: return Rml::Input::KI_ADD; + case sf::Keyboard::Backspace: return Rml::Input::KI_BACK; + case sf::Keyboard::Key::Delete: return Rml::Input::KI_DELETE; + case sf::Keyboard::Key::Divide: return Rml::Input::KI_DIVIDE; + case sf::Keyboard::Key::End: return Rml::Input::KI_END; + case sf::Keyboard::Key::Escape: return Rml::Input::KI_ESCAPE; + case sf::Keyboard::Key::F1: return Rml::Input::KI_F1; + case sf::Keyboard::Key::F2: return Rml::Input::KI_F2; + case sf::Keyboard::Key::F3: return Rml::Input::KI_F3; + case sf::Keyboard::Key::F4: return Rml::Input::KI_F4; + case sf::Keyboard::Key::F5: return Rml::Input::KI_F5; + case sf::Keyboard::Key::F6: return Rml::Input::KI_F6; + case sf::Keyboard::Key::F7: return Rml::Input::KI_F7; + case sf::Keyboard::Key::F8: return Rml::Input::KI_F8; + case sf::Keyboard::Key::F9: return Rml::Input::KI_F9; + case sf::Keyboard::Key::F10: return Rml::Input::KI_F10; + case sf::Keyboard::Key::F11: return Rml::Input::KI_F11; + case sf::Keyboard::Key::F12: return Rml::Input::KI_F12; + case sf::Keyboard::Key::F13: return Rml::Input::KI_F13; + case sf::Keyboard::Key::F14: return Rml::Input::KI_F14; + case sf::Keyboard::Key::F15: return Rml::Input::KI_F15; + case sf::Keyboard::Key::Home: return Rml::Input::KI_HOME; + case sf::Keyboard::Key::Insert: return Rml::Input::KI_INSERT; + case sf::Keyboard::Key::LControl: return Rml::Input::KI_LCONTROL; + case sf::Keyboard::Key::LShift: return Rml::Input::KI_LSHIFT; + case sf::Keyboard::Key::Multiply: return Rml::Input::KI_MULTIPLY; + case sf::Keyboard::Key::Pause: return Rml::Input::KI_PAUSE; + case sf::Keyboard::Key::RControl: return Rml::Input::KI_RCONTROL; + case sf::Keyboard::Enter: return Rml::Input::KI_RETURN; + case sf::Keyboard::Key::RShift: return Rml::Input::KI_RSHIFT; + case sf::Keyboard::Key::Space: return Rml::Input::KI_SPACE; + case sf::Keyboard::Key::Subtract: return Rml::Input::KI_SUBTRACT; + case sf::Keyboard::Key::Tab: return Rml::Input::KI_TAB; + default: break; + } + + return Rml::Input::KI_UNKNOWN; + + } + int getKeyModifierState() { + int modifiers = 0; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RShift)) + modifiers |= Rml::Input::KM_SHIFT; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RControl)) + modifiers |= Rml::Input::KM_CTRL; + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RAlt)) + modifiers |= Rml::Input::KM_ALT; + + return modifiers; + } +} \ No newline at end of file diff --git a/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.hpp b/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.hpp new file mode 100644 index 0000000..366d11a --- /dev/null +++ b/Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include + +#include +#include +#include +#include + +namespace mce::ui::priv { + class SystemInterface_dms : public Rml::SystemInterface { + public: + SystemInterface_dms(core::QEventBus& qBus); + ~SystemInterface_dms(); + + void setWindow(sf::WindowBase* window); + + double GetElapsedTime() override; + + void SetMouseCursor(const Rml::String& cursorName) override; + + void SetClipboardText(const Rml::String& text) override; + void GetClipboardText(Rml::String& text) override; + + bool LogMessage(Rml::Log::Type type, const Rml::String& msg) override; + + private: + + sf::WindowBase* window; + core::QEventBus& qBus; + sf::Clock clock; + sf::Cursor cursor_default; + sf::Cursor cursor_move; + sf::Cursor cursor_pointer; + sf::Cursor cursor_resize; + sf::Cursor cursor_cross; + sf::Cursor cursor_text; + sf::Cursor cursor_unavailable; + }; + bool inputEventHandler(Rml::Context* ctx, sf::WindowHandle hWnd, sf::Event& event); + + Rml::Input::KeyIdentifier convertKey(sf::Keyboard::Key key); + + int getKeyModifierState(); +} diff --git a/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.cpp b/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.cpp new file mode 100644 index 0000000..dbac7c7 --- /dev/null +++ b/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.cpp @@ -0,0 +1,613 @@ +#include "RmlUI_Renderer_bgfx.hpp" +#include "RmlUi/Core/Log.h" +#include "Graphics/BgfxRenderContext.hpp" +#include + +namespace mce::ui::priv { + bgfx::VertexLayout RmlVertex::layout() { + static bgfx::VertexLayout vLayout; + vLayout.begin() + .add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float) + .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) + .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float) + .end(); + return vLayout; + } + + // Convert Rml::Matrix4f (column-major, same as glm) to float[16] + static void Matrix4ToFloat16(const Rml::Matrix4f& m, float out[16]) { + // Rml::Matrix4f stores data column-major, same layout as bgfx expects + std::memcpy(out, m.data(), 16 * sizeof(float)); + } + + RenderInterface_bgfx::RenderInterface_bgfx(core::ResourceManager& rm) : + gfx::Renderer(0, rm.getRenderFactory(), rm.getRenderContext().getRenderAPI()), + rsrcMgr(rm), + programs({}), + activeProgram(RmlProgramId::None), + shader(nullptr), + nextGeometryId(1), + geometries({}), + nextFilterId(1), + filters({}), + nextShaderId(1), + shaders({}), + nextTextureId(1), + transform(Rml::Matrix4f::Identity()), + projection(Rml::Matrix4f::Identity()), + scissorEnabled(false), + scissorRegion({}), + clipMaskEnabled(false), + stencilRef(0), // current stencil reference value + stencilValue(1), // value written during clip-mask + currentViewId(0), + baseView(0), // first view allocated this frame + viewDirty(true), + lastBoundTexture(nullptr), + viewportWidth(0), viewportHeight(0), viewportOffsetX(0), viewportOffsetY(0), + valid(false), + drawOrder(0) { + programs.resize(static_cast(RmlProgramId::Count)); + + this->valid = this->loadPrograms(); + + if (this->valid) { + // Build a fullscreen quad [-1,1] covering NDC space. + // Uses tex_coord for UV. + struct FSVert { float x, y; uint32_t col; float u, v; }; + static const FSVert verts[] = { + {-1.f, -1.f, 0xFFFFFFFF, 0.f, 0.f}, + { 1.f, -1.f, 0xFFFFFFFF, 1.f, 0.f}, + { 1.f, 1.f, 0xFFFFFFFF, 1.f, 1.f}, + {-1.f, 1.f, 0xFFFFFFFF, 0.f, 1.f}, + }; + static const int indices[] = { 0, 1, 2, 0, 2, 3 }; + + this->fullscreenQuad = CompileGeometry( + { reinterpret_cast(verts), 4 }, + { indices, 6 } + ); + } + auto api = rsrcMgr.getRenderContext().getRenderAPI(); + this->frameBufferOriginBottomLeft = (api == gfx::RenderContext::API::OpenGL || api == gfx::RenderContext::API::OpenGLES); + } + RenderInterface_bgfx::~RenderInterface_bgfx() { + + } + void RenderInterface_bgfx::setViewport(int w, int h, int ox, int oy) { + viewportWidth = w; + viewportHeight = h; + viewportOffsetX = ox; + viewportOffsetY = oy; + projection = makeProjection(); + } + Rml::Matrix4f RenderInterface_bgfx::makeProjection() const { + const float L = static_cast(viewportOffsetX); + const float R = L + static_cast(viewportWidth); + const float T = static_cast(viewportOffsetY); + const float B = T + static_cast(viewportHeight); + + Rml::Matrix4f proj = Rml::Matrix4f::Identity(); + + float* m = proj.data(); + + m[0] = 2.f / (R - L); + m[5] = 2.f / (T - B); + m[10] = 1.f; + + m[12] = -(R + L) / (R - L); + m[13] = -(T + B) / (T - B); + m[14] = 0.f; + m[15] = 1.f; + + return proj; + } + Rml::CompiledGeometryHandle RenderInterface_bgfx::CompileGeometry(Rml::Span vertices, Rml::Span indices) { + BgfxCompiledGeometry geo; + + gfx::RenderFactory& factory = rsrcMgr.getRenderFactory(); + const bgfx::Memory* vMem = bgfx::copy( + vertices.data(), + static_cast(vertices.size() * sizeof(Rml::Vertex)) + ); + + gfx::flags::Buffer fl; + fl.addFlag(gfx::flags::Buffer::None); + geo.vb = factory.createVertexBuffer(vMem, RmlVertex::layout(), fl, "RmlUIVertex"); + + eastl::vector idx32(indices.size()); + for (size_t i = 0; i < indices.size(); ++i) { + idx32[i] = static_cast(indices[i]); + } + + const bgfx::Memory* imem = bgfx::copy(idx32.data(), static_cast(idx32.size() * sizeof(uint32_t))); + gfx::flags::Buffer iFlags; + iFlags.addFlag(gfx::flags::Buffer::Index32); + geo.ib = factory.createIndexBuffer(imem, iFlags); + + geo.num_indices = static_cast(indices.size()); + + Rml::CompiledGeometryHandle handle = this->nextGeometryId++; + this->geometries[handle] = geo; + return handle; + } + void RenderInterface_bgfx::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) { + submitGeometry(handle, translation, texture); + } + void RenderInterface_bgfx::ReleaseGeometry(Rml::CompiledGeometryHandle handle) { + auto it = this->geometries.find(handle); + if (it == geometries.end()) return; + geometries.erase(it); + } + Rml::TextureHandle RenderInterface_bgfx::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) { + return Rml::TextureHandle(); + } + Rml::TextureHandle RenderInterface_bgfx::GenerateTexture(Rml::Span source_data, Rml::Vector2i source_dimensions) { + + const bgfx::Memory* mem = bgfx::copy(source_data.data(), static_cast(source_data.size())); + auto& factory = rsrcMgr.getRenderFactory(); + eastl::shared_ptr tex = factory.createTexture( + sf::Vector2i(source_dimensions.x, source_dimensions.y), + false, 1, bgfx::TextureFormat::RGBA8, + BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP, + mem + ); + if (tex == nullptr) { + return {}; + } + // when the tex goes out of scope the reference count decreement and it will release the texture but Rml still want to access it + // so we insert it in a container that increement the reference count + generatedTextures.push_back(tex); + return static_cast(tex->getTextureHandle().idx); + + } + void RenderInterface_bgfx::ReleaseTexture(Rml::TextureHandle texture_handle) { + auto it = eastl::find_if(generatedTextures.begin(), generatedTextures.end(), [&texture_handle](const eastl::shared_ptr& p) { + return p->getTextureHandle().idx == static_cast(texture_handle); + }); + + generatedTextures.erase(it); + } + void RenderInterface_bgfx::EnableScissorRegion(bool enable) { + this->scissorEnabled = enable; + } + void RenderInterface_bgfx::SetScissorRegion(Rml::Rectanglei region) { + this->scissorRegion = region; + } + void RenderInterface_bgfx::EnableClipMask(bool enable) { + this->clipMaskEnabled = enable; + } + void RenderInterface_bgfx::RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) { + auto it = this->geometries.find(geometry); + if (it == this->geometries.end()) return; + + const auto& geo = it->second; + + auto& creationShader = this->programs[static_cast(RmlProgramId::Creation)]; + + if (creationShader == nullptr) { + return; + } + + useProgram(RmlProgramId::Creation); + + // Sets the transformation uniform + auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f); + auto mvp = projection * transform * translate_mat; + + float outproj[16]; + + Matrix4ToFloat16(mvp, outproj); + + if (creationShader != nullptr) { + creationShader->setUniform("u_transform", outproj); + } + + Renderer::setVertexBuffer(geo.vb); + Renderer::setIndexBuffer(geo.ib); + + uint64_t state = BGFX_STATE_MSAA; + bgfx::setState(state); + + if (scissorEnabled) { + bgfx::setScissor( + uint16_t(this->scissorRegion.Left()), + uint16_t(this->scissorRegion.Top()), + uint16_t(this->scissorRegion.Width()), + uint16_t(this->scissorRegion.Height()) + ); + } + + uint32_t stencil = 0; + + switch (operation) { + case Rml::ClipMaskOperation::Set: + { + stencilRef = stencilValue++; + + stencil = 0 + | BGFX_STENCIL_TEST_ALWAYS + | BGFX_STENCIL_FUNC_REF(stencilRef) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_ZERO + | BGFX_STENCIL_OP_FAIL_Z_ZERO + | BGFX_STENCIL_OP_PASS_Z_REPLACE; + break; + } + case Rml::ClipMaskOperation::SetInverse: + { + stencilRef = stencilValue++; + + // Fullscreen quad to fill stencil + { + auto fsIt = geometries.find(fullscreenQuad); + if (fsIt != geometries.end()) { + const auto& fsGeo = fsIt->second; + + float identity[16]; + bx::mtxIdentity(identity); + + Renderer::setVertexBuffer(fsGeo.vb); + Renderer::setIndexBuffer(fsGeo.ib); + bgfx::setState(BGFX_STATE_MSAA); + + uint32_t fillStencil = 0 + | BGFX_STENCIL_TEST_ALWAYS + | BGFX_STENCIL_FUNC_REF(stencilRef) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_REPLACE + | BGFX_STENCIL_OP_FAIL_Z_REPLACE + | BGFX_STENCIL_OP_PASS_Z_REPLACE; + + bgfx::setStencil(fillStencil); + + creationShader->setUniform("u_transform", identity); + bgfx::submit(currentViewId, creationShader->getProgramHandle(), drawOrder++); + } + } + auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f); + auto mvp = projection * transform * translate_mat; + + float outproj[16]; + + Matrix4ToFloat16(mvp, outproj); + creationShader->setUniform("u_transform", outproj); + bgfx::setVertexBuffer(0, geo.vb->getNativeHandle()); + bgfx::setIndexBuffer(geo.ib->getNativeHandle()); + bgfx::setState(BGFX_STATE_MSAA); + + stencil = 0 + | BGFX_STENCIL_TEST_ALWAYS + | BGFX_STENCIL_FUNC_REF(0) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_REPLACE + | BGFX_STENCIL_OP_FAIL_Z_REPLACE + | BGFX_STENCIL_OP_PASS_Z_REPLACE; + break; + } + case Rml::ClipMaskOperation::Intersect: + { + uint8_t old_ref = stencilRef; + stencilRef = stencilValue++; + + if (stencilRef == old_ref + 1) { + stencil = 0 + | BGFX_STENCIL_TEST_EQUAL + | BGFX_STENCIL_FUNC_REF(old_ref) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_KEEP + | BGFX_STENCIL_OP_FAIL_Z_KEEP + | BGFX_STENCIL_OP_PASS_Z_INCR; + } + else { + stencil = 0 + | BGFX_STENCIL_TEST_LEQUAL + | BGFX_STENCIL_FUNC_REF(stencilRef) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_KEEP + | BGFX_STENCIL_OP_FAIL_Z_KEEP + | BGFX_STENCIL_OP_PASS_Z_REPLACE; + } + break; + } + } + + if (scissorEnabled) { + bgfx::setScissor( + uint16_t(scissorRegion.Left()), + uint16_t(scissorRegion.Top()), + uint16_t(scissorRegion.Width()), + uint16_t(scissorRegion.Height()) + ); + } + bgfx::setStencil(stencil); + bgfx::submit(currentViewId, creationShader->getProgramHandle(), drawOrder++); + } + void RenderInterface_bgfx::SetTransform(const Rml::Matrix4f* transform) {} + Rml::LayerHandle RenderInterface_bgfx::PushLayer() { + return Rml::LayerHandle(); + } + void RenderInterface_bgfx::CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode, Rml::Span filters) {} + void RenderInterface_bgfx::PopLayer() {} + Rml::TextureHandle RenderInterface_bgfx::SaveLayerAsTexture() { + return Rml::TextureHandle(); + } + Rml::CompiledFilterHandle RenderInterface_bgfx::SaveLayerAsMaskImage() { + return Rml::CompiledFilterHandle(); + } + Rml::CompiledFilterHandle RenderInterface_bgfx::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) { + return Rml::CompiledFilterHandle(); + } + void RenderInterface_bgfx::ReleaseFilter(Rml::CompiledFilterHandle filter) {} + Rml::CompiledShaderHandle RenderInterface_bgfx::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) { + return Rml::CompiledShaderHandle(); + } + void RenderInterface_bgfx::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, Rml::Vector2f translation, Rml::TextureHandle texture) {} + void RenderInterface_bgfx::ReleaseShader(Rml::CompiledShaderHandle effect_handle) {} + bool RenderInterface_bgfx::loadPrograms() { + struct ProgramDef { RmlProgramId id; const char* vs; const char* fs; }; + static const ProgramDef defs[] = { + { RmlProgramId::Color, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_color.fs"}, + { RmlProgramId::Texture, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_texture.fs"}, + { RmlProgramId::Passthrough, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_passthrough.fs" }, + { RmlProgramId::BlendMask, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_blendmask.fs" }, + { RmlProgramId::Blur, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_blur.fs" }, + { RmlProgramId::DropShadow, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_dropshadow.fs" }, + { RmlProgramId::ColorMatrix, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_colormatrix.fs"}, + { RmlProgramId::Creation, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_creation.fs" }, + { RmlProgramId::Gradient, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_gradient.fs" } + }; + + for (auto& def : defs) { + eastl::shared_ptr sp = rsrcMgr.getShader(def.vs, def.fs); + if (!sp) { + Rml::Log::Message(Rml::Log::LT_ERROR, "RmlUi-BGFX: Failed to load program %s, / %s", def.vs, def.fs); + destroyPrograms(); + return false; + } + programs[static_cast(def.id)] = sp; + } + return true; + } + + void RenderInterface_bgfx::destroyPrograms() { + for (int i = 0; i < static_cast(RmlProgramId::Count); ++i) { + programs.pop_back(); + } + } + bgfx::ViewId RenderInterface_bgfx::allocateView() { + auto bgfxCtx = reinterpret_cast(&rsrcMgr.getRenderContext()); + return bgfxCtx->nextViewId++; + } + void RenderInterface_bgfx::setupView(bgfx::ViewId view, const BgfxFrameBuffer& fb) { + bgfx::setViewName(view, std::string("RmlUi_Layer: " + std::to_string(view)).c_str()); + bgfx::setViewFrameBuffer(view, fb.fb->getNativeHandle()); + bgfx::setViewRect(view, 0, 0, uint16_t(fb.width), uint16_t(fb.height)); + bgfx::setViewClear(view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0x00000000, 1.0f, 0); + + float proj[16]; + Matrix4ToFloat16(this->projection, proj); + float identity[16]; + bx::mtxIdentity(identity); + bgfx::setViewTransform(view, identity, proj); + + bgfx::touch(view); + } + void RenderInterface_bgfx::setupViewSpace(bgfx::ViewId view) { + const auto& topLayer = layers.getTopLayer(); + bgfx::setViewRect(view, 0, 0, uint16_t(topLayer.width), uint16_t(topLayer.height)); + } + + void RenderInterface_bgfx::ensureView() { + if (!this->viewDirty) return; + viewDirty = false; + } + + + void RenderInterface_bgfx::useProgram(RmlProgramId id) { + activeProgram = id; + } + void RenderInterface_bgfx::submitGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture, RmlProgramId program_override) { + auto it = this->geometries.find(handle); + if (it == this->geometries.end()) return; + + const auto& geo = it->second; + + RmlProgramId prog = program_override; + if (prog == RmlProgramId::None) { + if (texture == 0) { + prog = RmlProgramId::Color; + } + else if (texture == TexturePostprocess) { + prog = activeProgram; + } + else { + prog = RmlProgramId::Texture; + } + } + + useProgram(prog); + + auto& shader = programs[static_cast(prog)]; + if (!shader) return; + setTransformUniform(translation); + + auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f); + auto mvp = projection * transform * translate_mat; + + float outproj[16]; + + Matrix4ToFloat16(mvp, outproj); + + if (texture != 0 && texture != TexturePostprocess) { + eastl::shared_ptr tex = nullptr; + if (texture == TextureEnableWithoutBinding) { + tex = lastBoundTexture; + } + else { + auto it = eastl::find_if(generatedTextures.begin(), generatedTextures.end(), [&texture](const eastl::shared_ptr& p) { + return p->getTextureHandle().idx == texture; + }); + if (it != generatedTextures.end()) { + tex = *it; + } + lastBoundTexture = tex; + } + } + + auto state = buildBaseState(); + if (prog == RmlProgramId::Color || texture == 0) { + auto nState = BGFX_STATE_BLEND_FUNC(gfx::flags::State::BlendOne, gfx::flags::State::BlendInvSrcAlpha); + state.addFlag(gfx::flags::State::Enum(nState)); + } + else { + auto nState = BGFX_STATE_BLEND_FUNC(gfx::flags::State::BlendOne, gfx::flags::State::BlendInvSrcAlpha); + state.addFlag(gfx::flags::State::Enum(nState)); + } + + // Scissor + if (scissorEnabled) { + bgfx::setScissor( + uint16_t(scissorRegion.Left()), + uint16_t(scissorRegion.Top()), + uint16_t(scissorRegion.Width()), + uint16_t(scissorRegion.Height()) + ); + } + + // Stencil + if (clipMaskEnabled) { + uint32_t stencil = buildStencilState(); + bgfx::setStencil(stencil); + } + + + Renderer::setState(state); + + Renderer::setVertexBuffer(geo.vb); + Renderer::setIndexBuffer(geo.ib); + + if (lastBoundTexture != nullptr) + shader->setUniform("s_texture0", lastBoundTexture); + + shader->setUniform("u_transform", outproj); + + Renderer::submit(shader, drawOrder++); + } + gfx::flags::State RenderInterface_bgfx::buildBaseState() const { + static gfx::flags::State sf; + sf.addFlag(gfx::flags::State::WriteRGB) + .addFlag(gfx::flags::State::WriteA) + .addFlag(gfx::flags::State::MSAA); + return sf; + } + uint32_t RenderInterface_bgfx::buildStencilState() const { + return 0 + | BGFX_STENCIL_TEST_EQUAL + | BGFX_STENCIL_FUNC_REF(stencilRef) + | BGFX_STENCIL_FUNC_RMASK(0xFF) + | BGFX_STENCIL_OP_FAIL_S_KEEP + | BGFX_STENCIL_OP_FAIL_Z_KEEP + | BGFX_STENCIL_OP_PASS_Z_KEEP; + } + void RenderInterface_bgfx::setTransformUniform(Rml::Vector2f translation) {} + + BgfxFrameBuffer RenderInterface_bgfx::createFrameBuffer(int w, int h, bool with_depth_stencil) { + auto& factory = Renderer::getFactory(); + + BgfxFrameBuffer fb; + fb.width = w; + fb.height = h; + + fb.color = factory.createTexture({ uint16_t(w), uint16_t(h) }, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP); + if (with_depth_stencil) { + fb.depth_stencil = factory.createTexture({ uint16_t(w), uint16_t(h) }, false, 1, bgfx::TextureFormat::D24S8, BGFX_TEXTURE_RT); + + bgfx::Attachment attachments[2]{}; + attachments[0].init(fb.color->getTextureHandle()); + attachments[0].init(fb.depth_stencil->getTextureHandle()); + fb.fb = factory.createFrameBuffer(2, attachments, false); + } + else { + bgfx::Attachment att; + att.init(fb.color->getTextureHandle()); + fb.fb = factory.createFrameBuffer(1, &att, false); + } + + return fb; + } + void RenderInterface_bgfx::destroyFrameBuffer(BgfxFrameBuffer& fb) { + fb.fb.reset(); + fb.color.reset(); + fb.depth_stencil.reset(); + fb.fb = nullptr; + fb.color = nullptr; + fb.depth_stencil = nullptr; + } + + + // + // Layer Stack + // + + RenderInterface_bgfx::RenderLayerStack::~RenderLayerStack() { + + } + void RenderInterface_bgfx::RenderLayerStack::destroyAll(RenderInterface_bgfx& ri) { + for (auto& fb : fb_layers_) { + ri.destroyFrameBuffer(fb); + } + fb_layers_.clear(); + for (auto& fb : fb_postprocess_) { + ri.destroyFrameBuffer(fb); + } + fb_postprocess_.clear(); + + layers_size_ = 0; + } + + void RenderInterface_bgfx::RenderLayerStack::beginFrame(RenderInterface_bgfx& ri, int w, int h) { + ri_ = &ri; + if (w != width_ || h != height_) { + RenderLayerStack::destroyAll(ri); + width_ = w; + height_ = h; + } + + // === CRITICAL FIX: Pre-create ALL post-process buffers so vector never reallocates === + // This prevents dangling references mid-frame (the real cause of "garbage data") + fb_postprocess_.reserve(8); + while (fb_postprocess_.size() < 4) { + this->fb_postprocess_.emplace_back(ri.createFrameBuffer(width_, height_, false)); + } + + layers_size_ = 0; + RenderLayerStack::pushLayer(ri); + } + void RenderInterface_bgfx::RenderLayerStack::endFrame() { + // Keep framebuffers alive for reuse next frame. + // Just reset the active layer count. + layers_size_ = 0; + } + + Rml::LayerHandle RenderInterface_bgfx::RenderLayerStack::pushLayer(RenderInterface_bgfx& ri) { + if (layers_size_ >= (int)fb_layers_.size()) { + fb_layers_.push_back(ri.createFrameBuffer(width_, height_, true)); + } + + Rml::LayerHandle handle = static_cast(layers_size_); + layers_size_++; + return handle; + } + void RenderInterface_bgfx::RenderLayerStack::popLayer() { + if (layers_size_ > 1) + layers_size_--; + } + const BgfxFrameBuffer& RenderInterface_bgfx::RenderLayerStack::getLayer(Rml::LayerHandle h) const { + return fb_layers_[static_cast(h)]; + } + + const BgfxFrameBuffer& RenderInterface_bgfx::RenderLayerStack::getTopLayer() const { + return fb_layers_[layers_size_ - 1]; + } +} diff --git a/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.hpp b/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.hpp new file mode 100644 index 0000000..52e8df3 --- /dev/null +++ b/Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.hpp @@ -0,0 +1,277 @@ +#pragma once +#include +#include + +#include +#include +#include + +#include +#include + +#include + +namespace mce::ui::priv { + class RmlVertex { + public: + RmlVertex(); + RmlVertex(sf::Vector2f position, gfx::Color color, sf::Vector2f texCoords); + sf::Vector2f position; + gfx::Color color; + sf::Vector2f texCoords; + + static bgfx::VertexLayout layout(); + }; + enum class RmlProgramId : int { + None = 0, + Color, // vertex-color only + Texture, // textured + vertex-color (premultiplied alpha) + Passthrough, // fullscreen blit / layer composite + BlendMask, // composite with blend-mask texture + Blur, // separable Gaussian blur + DropShadow, // drop-shadow filter + ColorMatrix, // generic 4×5 colour-matrix filter + Creation, // stencil-write (clip-mask geometry) + Gradient, // linear / radial / conic gradient shader + Count + }; + + enum class FilterType { None, Blur, DropShadow, ColorMatrix, MaskImage }; + + struct CompiledFilter { + FilterType type = FilterType::None; + // Blur / DropShadow + float sigma = 0.f; + // DropShadow extras + Rml::Vector2f offset = { 0, 0 }; + Rml::Colourf color = { 0, 0, 0, 1 }; + // ColorMatrix (row-major 4×5, stored as mat4 + vec4 translate) + float color_matrix[16] = {}; + float color_translate[4] = {}; + // MaskImage + eastl::shared_ptr mask_texture = nullptr; + }; + + enum class ShaderType { None, Gradient }; + + struct ShaderGradientStop { float position; Rml::Colourf color; }; + + struct CompiledShaderData { + ShaderType type = ShaderType::None; + // Gradient + int gradient_function = 0; // 0=linear, 1=radial, 2=conic, 3=repeating-linear, … + Rml::Vector2f p = {}; + Rml::Vector2f q = {}; + eastl::vector stops; + eastl::shared_ptr stop_texture = nullptr; + }; + + struct BgfxFrameBuffer { + eastl::shared_ptr fb = nullptr; + eastl::shared_ptr color = nullptr; + eastl::shared_ptr depth_stencil = nullptr; + int width = 0; + int height = 0; + }; + + struct BgfxCompiledGeometry { + eastl::shared_ptr vb = nullptr; + eastl::shared_ptr ib = nullptr; + int num_indices = 0; + }; + + class RenderInterface_bgfx : public Rml::RenderInterface, public gfx::Renderer { + public: + RenderInterface_bgfx(core::ResourceManager& rm); + ~RenderInterface_bgfx(); + + void setViewport(int viewport_width, int viewport_height, + int viewport_offset_x = 0, int viewport_offset_y = 0); + + Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, + Rml::Span indices) override; + void RenderGeometry(Rml::CompiledGeometryHandle handle, + Rml::Vector2f translation, + Rml::TextureHandle texture) override; + void ReleaseGeometry(Rml::CompiledGeometryHandle handle) override; + + Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, + const Rml::String& source) override; + Rml::TextureHandle GenerateTexture(Rml::Span source_data, + Rml::Vector2i source_dimensions) override; + void ReleaseTexture(Rml::TextureHandle texture_handle) override; + + void EnableScissorRegion(bool enable) override; + void SetScissorRegion(Rml::Rectanglei region) override; + + void EnableClipMask(bool enable) override; + void RenderToClipMask(Rml::ClipMaskOperation mask_operation, + Rml::CompiledGeometryHandle geometry, + Rml::Vector2f translation) override; + + void SetTransform(const Rml::Matrix4f* transform) override; + + Rml::LayerHandle PushLayer() override; + void CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, + Rml::BlendMode blend_mode, + Rml::Span filters) override; + void PopLayer() override; + + Rml::TextureHandle SaveLayerAsTexture() override; + + Rml::CompiledFilterHandle SaveLayerAsMaskImage() override; + + Rml::CompiledFilterHandle CompileFilter(const Rml::String& name, + const Rml::Dictionary& parameters) override; + void ReleaseFilter(Rml::CompiledFilterHandle filter) override; + + Rml::CompiledShaderHandle CompileShader(const Rml::String& name, + const Rml::Dictionary& parameters) override; + void RenderShader(Rml::CompiledShaderHandle shader_handle, + Rml::CompiledGeometryHandle geometry_handle, + Rml::Vector2f translation, + Rml::TextureHandle texture) override; + void ReleaseShader(Rml::CompiledShaderHandle effect_handle) override; + + // Special texture-handle sentinels (same semantics as GL3) + static constexpr Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1); + static constexpr Rml::TextureHandle TexturePostprocess = Rml::TextureHandle(-2); + + private: + Rml::Matrix4f makeProjection() const; + + bool loadPrograms(); + void destroyPrograms(); + + bgfx::ViewId allocateView(); + void setupView(bgfx::ViewId view, const BgfxFrameBuffer& fb); + void setupViewSpace(bgfx::ViewId view); + void ensureView(); + + void useProgram(RmlProgramId id); + + void submitGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture, RmlProgramId program_override = RmlProgramId::None); + void drawFullscreenQuad(const eastl::shared_ptr& texture, RmlProgramId program); + void drawFullscreenQuad(const eastl::shared_ptr& texture, RmlProgramId program, Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling, bool flip_v); + void blitLayerToPostprocessPrimary(Rml::LayerHandle layerHandle); + void renderFilters(Rml::Span filterHandles); + void renderBlur(float sigma, BgfxFrameBuffer& sourceDest, BgfxFrameBuffer& temp, Rml::Rectanglei windowFlipped); + + gfx::flags::State buildBaseState() const; + uint32_t buildStencilState() const; + + void setTransformUniform(Rml::Vector2f translation); + + // FrameBuffer helpers + BgfxFrameBuffer createFrameBuffer(int w, int h, bool with_depth_stencil = true); + void destroyFrameBuffer(BgfxFrameBuffer& fb); + + bool frameBufferOriginBottomLeft = true; + private: + class RenderLayerStack { + public: + RenderLayerStack() = default; + ~RenderLayerStack(); + + Rml::LayerHandle pushLayer(RenderInterface_bgfx& ri); + void popLayer(); + + const BgfxFrameBuffer& getLayer(Rml::LayerHandle h) const; + const BgfxFrameBuffer& getTopLayer() const; + Rml::LayerHandle getTopLayerHandle() const; + + BgfxFrameBuffer& getPostProcessPrimary(); + BgfxFrameBuffer& getPostProcessSecondary(); + BgfxFrameBuffer& getPostProcessTertiary(); + BgfxFrameBuffer& getBlendMask(); + + void swapPostProcessPrimarySecondary(); + + void beginFrame(RenderInterface_bgfx& ri, int w, int h); + void endFrame(); + + private: + void destroyAll(RenderInterface_bgfx& ri); + BgfxFrameBuffer& ensurePostProcess(RenderInterface_bgfx& ri, int idx); + + int width_ = 0; + int height_ = 0; + int layers_size_ = 0; + + eastl::vector fb_layers_; + eastl::vector fb_postprocess_; + + RenderInterface_bgfx* ri_ = nullptr; + }; + + RenderLayerStack layers; + + // Programs + eastl::fixed_vector, static_cast(RmlProgramId::Count)> programs; + RmlProgramId activeProgram; + + //// Uniforms (created once, reused) + //bgfx::UniformHandle u_transform = BGFX_INVALID_HANDLE; // mat4 + //bgfx::UniformHandle u_translate = BGFX_INVALID_HANDLE; // vec4 + //bgfx::UniformHandle u_texParams = BGFX_INVALID_HANDLE; // vec4 (uv_offset.xy, uv_scale.xy) + //bgfx::UniformHandle u_blurParams = BGFX_INVALID_HANDLE; // vec4 (sigma, dir_x, dir_y, 0) + //bgfx::UniformHandle u_texelSize = BGFX_INVALID_HANDLE; // vec4 (1/w, 1/h, 0, 0) + //bgfx::UniformHandle u_colorMatrix = BGFX_INVALID_HANDLE; // mat4 + //bgfx::UniformHandle u_colorTranslate = BGFX_INVALID_HANDLE; // vec4 + //bgfx::UniformHandle u_shadowExtra = BGFX_INVALID_HANDLE; // vec4 (offset.xy, 0, 0) + //bgfx::UniformHandle u_shadowColor = BGFX_INVALID_HANDLE; // vec4 + //bgfx::UniformHandle u_gradientParams = BGFX_INVALID_HANDLE; // vec4 (func, num_stops, 0, 0) + //bgfx::UniformHandle u_gradientP = BGFX_INVALID_HANDLE; // vec4 (p.xy, q.xy) + //bgfx::UniformHandle s_texture0 = BGFX_INVALID_HANDLE; // sampler + //bgfx::UniformHandle s_texture1 = BGFX_INVALID_HANDLE; // sampler (blend mask / stops) + eastl::shared_ptr shader; // we load all the uniforms manually when calling `shader->setUniform()` + + // Geometry registries + Rml::CompiledGeometryHandle nextGeometryId; + eastl::unordered_map geometries; + + // Filter / Shader registries + Rml::CompiledFilterHandle nextFilterId; + eastl::unordered_map filters; + + Rml::CompiledShaderHandle nextShaderId; + eastl::unordered_map shaders; + + // Texture bookkeeping + Rml::TextureHandle nextTextureId; + + //Fullscreen-quad geometry (pre-built) + Rml::CompiledGeometryHandle fullscreenQuad = 0; + + // Render state + Rml::Matrix4f transform; + Rml::Matrix4f projection; + + bool scissorEnabled; + Rml::Rectanglei scissorRegion; + + bool clipMaskEnabled; + uint8_t stencilRef; + uint8_t stencilValue; + + uint16_t currentViewId; + uint16_t baseView; + bool viewDirty; + + //bgfx::TextureHandle lastBoundTexture = BGFX_INVALID_HANDLE; + eastl::shared_ptr lastBoundTexture; + + int viewportWidth; + int viewportHeight; + int viewportOffsetX; + int viewportOffsetY; + + bool valid; + + uint32_t drawOrder; + core::ResourceManager& rsrcMgr; // resource manager + + eastl::vector> generatedTextures; + }; +} +