rmlui is WIP

This commit is contained in:
Joud Kandeel
2026-05-20 09:20:59 +02:00
parent fde355d50b
commit 29057e8923
5 changed files with 1229 additions and 0 deletions

View File

@@ -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 <RmlUi/Core/Input.h>
#include <RmlUi/Core/RenderInterface.h>
#include <RmlUi/Core/SystemInterface.h>
#include <RmlUi/Core/Types.h>
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

View File

@@ -0,0 +1,221 @@
#include "RmlUI_Platform_dms.hpp"
#include <RmlUi/Core/Context.h>
#include <RmlUi/Core/StringUtilities.h>
#include <Core/WindowBase.hpp>
#include <Core/Clipboard.hpp>
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;
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <RmlUi/Core/Input.h>
#include <RmlUi/Core/SystemInterface.h>
#include <RmlUi/Core/Types.h>
#include <Core/WindowBase.hpp>
#include <Core/Event.hpp>
#include <Core/Clock.hpp>
#include <Core/QEventBus.hpp>
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();
}

View File

@@ -0,0 +1,613 @@
#include "RmlUI_Renderer_bgfx.hpp"
#include "RmlUi/Core/Log.h"
#include "Graphics/BgfxRenderContext.hpp"
#include <bx/math.h>
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<eastl_size_t>(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<const Rml::Vertex*>(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<float>(viewportOffsetX);
const float R = L + static_cast<float>(viewportWidth);
const float T = static_cast<float>(viewportOffsetY);
const float B = T + static_cast<float>(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<const Rml::Vertex> vertices, Rml::Span<const int> indices) {
BgfxCompiledGeometry geo;
gfx::RenderFactory& factory = rsrcMgr.getRenderFactory();
const bgfx::Memory* vMem = bgfx::copy(
vertices.data(),
static_cast<uint32_t>(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<uint32_t> idx32(indices.size());
for (size_t i = 0; i < indices.size(); ++i) {
idx32[i] = static_cast<uint32_t>(indices[i]);
}
const bgfx::Memory* imem = bgfx::copy(idx32.data(), static_cast<uint32_t>(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<int>(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<const Rml::byte> source_data, Rml::Vector2i source_dimensions) {
const bgfx::Memory* mem = bgfx::copy(source_data.data(), static_cast<uint32_t>(source_data.size()));
auto& factory = rsrcMgr.getRenderFactory();
eastl::shared_ptr<gfx::Texture> 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<Rml::TextureHandle>(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<gfx::Texture>& p) {
return p->getTextureHandle().idx == static_cast<uint16_t>(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<int>(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<const Rml::CompiledFilterHandle> 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<gfx::ShaderProgram> 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<eastl_size_t>(def.id)] = sp;
}
return true;
}
void RenderInterface_bgfx::destroyPrograms() {
for (int i = 0; i < static_cast<int>(RmlProgramId::Count); ++i) {
programs.pop_back();
}
}
bgfx::ViewId RenderInterface_bgfx::allocateView() {
auto bgfxCtx = reinterpret_cast<gfx::BgfxRenderContext*>(&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<int>(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<gfx::Texture> tex = nullptr;
if (texture == TextureEnableWithoutBinding) {
tex = lastBoundTexture;
}
else {
auto it = eastl::find_if(generatedTextures.begin(), generatedTextures.end(), [&texture](const eastl::shared_ptr<gfx::Texture>& 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<Rml::LayerHandle>(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<int>(h)];
}
const BgfxFrameBuffer& RenderInterface_bgfx::RenderLayerStack::getTopLayer() const {
return fb_layers_[layers_size_ - 1];
}
}

View File

@@ -0,0 +1,277 @@
#pragma once
#include <RmlUi/Core/RenderInterface.h>
#include <RmlUi/Core/Types.h>
#include <Graphics/Renderer.hpp>
#include <Graphics/Color.hpp>
#include <IO/VirtualFileSystem.hpp>
#include <EASTL/unordered_map.h>
#include <EASTL/fixed_vector.h>
#include <Core/ResourceManager.hpp>
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<gfx::Texture> 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<ShaderGradientStop> stops;
eastl::shared_ptr<gfx::Texture> stop_texture = nullptr;
};
struct BgfxFrameBuffer {
eastl::shared_ptr<gfx::FrameBuffer> fb = nullptr;
eastl::shared_ptr<gfx::Texture> color = nullptr;
eastl::shared_ptr<gfx::Texture> depth_stencil = nullptr;
int width = 0;
int height = 0;
};
struct BgfxCompiledGeometry {
eastl::shared_ptr<gfx::VertexBuffer> vb = nullptr;
eastl::shared_ptr<gfx::IndexBuffer> 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<const Rml::Vertex> vertices,
Rml::Span<const int> 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<const Rml::byte> 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<const Rml::CompiledFilterHandle> 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<gfx::Texture>& texture, RmlProgramId program);
void drawFullscreenQuad(const eastl::shared_ptr<gfx::Texture>& texture, RmlProgramId program, Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling, bool flip_v);
void blitLayerToPostprocessPrimary(Rml::LayerHandle layerHandle);
void renderFilters(Rml::Span<const Rml::CompiledFilterHandle> 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<BgfxFrameBuffer> fb_layers_;
eastl::vector<BgfxFrameBuffer> fb_postprocess_;
RenderInterface_bgfx* ri_ = nullptr;
};
RenderLayerStack layers;
// Programs
eastl::fixed_vector<eastl::shared_ptr<gfx::ShaderProgram>, static_cast<int>(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<gfx::ShaderProgram> shader; // we load all the uniforms manually when calling `shader->setUniform()`
// Geometry registries
Rml::CompiledGeometryHandle nextGeometryId;
eastl::unordered_map<Rml::CompiledGeometryHandle, BgfxCompiledGeometry> geometries;
// Filter / Shader registries
Rml::CompiledFilterHandle nextFilterId;
eastl::unordered_map<Rml::CompiledFilterHandle, CompiledFilter> filters;
Rml::CompiledShaderHandle nextShaderId;
eastl::unordered_map<Rml::CompiledShaderHandle, CompiledShaderData> 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<gfx::Texture> lastBoundTexture;
int viewportWidth;
int viewportHeight;
int viewportOffsetX;
int viewportOffsetY;
bool valid;
uint32_t drawOrder;
core::ResourceManager& rsrcMgr; // resource manager
eastl::vector<eastl::shared_ptr<gfx::Texture>> generatedTextures;
};
}