/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/EnginePlugins/RmlUiPlugin/Implementation/RmlUiSingleton.cpp
374 строки
10 KB
C-Core
Load RmlDebugger dll dynamically (#1998)
22 июл 2026, 08:58
Не верифицирован
22 июл 2026, 08:58
bbeedd6
Код
Авторство
О чём код?
#include <RmlUiPlugin/RmlUiPluginPCH.h> #include <Foundation/Configuration/CVar.h> #include <Foundation/IO/FileSystem/FileReader.h> #include <Foundation/IO/FileSystem/FileWriter.h> #include <Foundation/IO/OpenDdlReader.h> #include <Foundation/IO/OpenDdlUtils.h> #include <Foundation/IO/OpenDdlWriter.h> #include <RendererCore/RenderWorld/RenderWorld.h> #include <RmlUiPlugin/Implementation/EventListener.h> #include <RmlUiPlugin/Implementation/FileInterface.h> #include <RmlUiPlugin/Implementation/RenderInterface.h> #include <RmlUiPlugin/Implementation/SystemInterface.h> #include <RmlUiPlugin/RmlUiContext.h> #include <RmlUiPlugin/RmlUiSingleton.h> #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) && EZ_ENABLED(EZ_PLATFORM_WINDOWS) # include <Foundation/Platform/Win/Utils/IncludeWindows.h> # include <RmlUi/Include/RmlUi/Debugger/DebuggerFunctionTable.h> static Rml_Debugger_Functions s_DebuggerFunctions; void FillDebuggerFunctionTable() { if (s_DebuggerFunctions.m_InitFunc != nullptr) return; auto hModule = LoadLibraryW(L"RmlDebugger.dll"); if (hModule == nullptr) { ezLog::Error("Could not load RmlDebugger.dll"); return; } auto func = (GetFunctionsFunc)GetProcAddress(hModule, "Rml_Debugger_GetFunctions"); if (func == nullptr) { ezLog::Error("Could not find Rml_Debugger_GetFunctions in RmlDebugger.dll"); return; } func(&s_DebuggerFunctions); }; #endif ezResult ezRmlUiConfiguration::Save(ezStringView sFile) const { EZ_LOG_BLOCK("ezRmlUiConfiguration::Save()"); ezFileWriter file; if (file.Open(sFile).Failed()) return EZ_FAILURE; ezOpenDdlWriter writer; writer.SetOutputStream(&file); writer.SetCompactMode(false); writer.SetPrimitiveTypeStringMode(ezOpenDdlWriter::TypeStringMode::Compliant); writer.BeginObject("Fonts"); for (auto& font : m_Fonts) { ezOpenDdlUtils::StoreString(writer, font); } writer.EndObject(); return EZ_SUCCESS; } ezResult ezRmlUiConfiguration::Load(ezStringView sFile) { EZ_LOG_BLOCK("ezRmlUiConfiguration::Load()"); m_Fonts.Clear(); ezFileReader file; if (file.Open(sFile).Failed()) return EZ_FAILURE; ezOpenDdlReader reader; if (reader.ParseDocument(file, 0, ezLog::GetThreadLocalLogSystem()).Failed()) { ezLog::Error("Failed to parse RmlUi config file '{0}'", sFile); return EZ_FAILURE; } const ezOpenDdlReaderElement* pTree = reader.GetRootElement(); for (const ezOpenDdlReaderElement* pChild = pTree->GetFirstChild(); pChild != nullptr; pChild = pChild->GetSibling()) { if (pChild->IsCustomType("Fonts")) { for (const ezOpenDdlReaderElement* pFont = pChild->GetFirstChild(); pFont != nullptr; pFont = pFont->GetSibling()) { m_Fonts.PushBack(pFont->GetPrimitivesString()[0]); } } } return EZ_SUCCESS; } bool ezRmlUiConfiguration::operator==(const ezRmlUiConfiguration& rhs) const { return m_Fonts == rhs.m_Fonts; } ////////////////////////////////////////////////////////////////////////// EZ_IMPLEMENT_SINGLETON(ezRmlUi); #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) ezCVarString cvar_RmlUiDebugContext("RmlUi.DebugContext", "", ezCVarFlags::Default, "Sets the name of the context that should be debugged"); bool ShouldDebugContext(const ezRmlUiContext& context) { if (cvar_RmlUiDebugContext.GetValue().IsEmpty()) return false; ezStringView sContextName = ezRmlUiConversionUtils::ToStringView(context.GetName()); return sContextName.FindSubString_NoCase(cvar_RmlUiDebugContext.GetValue()) != nullptr; } #endif struct ezRmlUi::Data { ezMutex m_ExtractionMutex; ezRmlUiInternal::RenderInterface m_RenderInterface; ezRmlUiInternal::FileInterface m_FileInterface; ezRmlUiInternal::SystemInterface m_SystemInterface; ezRmlUiInternal::ContextInstancer m_ContextInstancer; ezRmlUiInternal::EventListenerInstancer m_EventListenerInstancer; ezMutex m_ContextsMutex; ezDynamicArray<ezRmlUiContext*> m_Contexts; ezUInt64 m_uiLastClearedCacheFrame = 0; ezRmlUiConfiguration m_Config; #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) bool m_bDebuggerInitialized = false; ezEventSubscriptionID m_DebugCVarEventHandler; #endif }; ezRmlUi::ezRmlUi() : m_SingletonRegistrar(this) { m_pData = EZ_DEFAULT_NEW(Data); Rml::SetRenderInterface(&m_pData->m_RenderInterface); Rml::SetFileInterface(&m_pData->m_FileInterface); Rml::SetSystemInterface(&m_pData->m_SystemInterface); Rml::Initialise(); Rml::Factory::RegisterContextInstancer(&m_pData->m_ContextInstancer); Rml::Factory::RegisterEventListenerInstancer(&m_pData->m_EventListenerInstancer); if (m_pData->m_Config.Load().Failed()) { ezLog::Warning("No valid RmlUi configuration file available in '{}'.", ezRmlUiConfiguration::s_sConfigFile); return; } for (auto& font : m_pData->m_Config.m_Fonts) { // Treat last font as fall back bool bIsFallbackFont = (font == m_pData->m_Config.m_Fonts.PeekBack()); if (Rml::LoadFontFace(font.GetData(), bIsFallbackFont) == false) { ezLog::Warning("Failed to load font face '{0}'.", font); } } #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) m_pData->m_DebugCVarEventHandler = cvar_RmlUiDebugContext.m_CVarEvents.AddEventHandler( [this](const ezCVarEvent& e) { if (e.m_EventType != ezCVarEvent::ValueChanged) return; ezStringView sContextName = cvar_RmlUiDebugContext.GetValue(); if (sContextName.IsEmpty()) { DebugContext(nullptr); return; } EZ_LOCK(m_pData->m_ContextsMutex); for (auto pContext : m_pData->m_Contexts) { if (ShouldDebugContext(*pContext)) { DebugContext(pContext); return; } } DebugContext(nullptr); }); #endif } ezRmlUi::~ezRmlUi() { #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) cvar_RmlUiDebugContext.m_CVarEvents.RemoveEventHandler(m_pData->m_DebugCVarEventHandler); #endif Rml::Shutdown(); } ezRmlUiContext* ezRmlUi::CreateContext(const char* szName, const ezVec2U32& vInitialSize) { EZ_LOCK(m_pData->m_ContextsMutex); ezRmlUiContext* pContext = static_cast<ezRmlUiContext*>(Rml::CreateContext(szName, Rml::Vector2i(vInitialSize.x, vInitialSize.y))); EZ_ASSERT_DEV(pContext != nullptr, "RML UI context creation failed"); m_pData->m_Contexts.PushBack(pContext); return pContext; } void ezRmlUi::DeleteContext(ezRmlUiContext* pContext) { EZ_LOCK(m_pData->m_ContextsMutex); m_pData->m_Contexts.RemoveAndCopy(pContext); Rml::RemoveContext(pContext->GetName()); } bool ezRmlUi::AnyContextWantsInput() { EZ_LOCK(m_pData->m_ContextsMutex); for (auto pContext : m_pData->m_Contexts) { if (pContext->WantsInput()) return true; } return false; } ezResult ezRmlUi::LoadDocumentFromResource(ezRmlUiContext& ref_context, const ezRmlUiResourceHandle& hResource) { UnloadDocument(ref_context); if (hResource.IsValid()) { ezResourceLock<ezRmlUiResource> pResource(hResource, ezResourceAcquireMode::BlockTillLoaded); if (pResource.GetAcquireResult() == ezResourceAcquireResult::Final) { // RmlUi is not thread safe, so we need to make that we only load/unload one document at a time. EZ_LOCK(m_pData->m_ContextsMutex); ref_context.LoadDocument(pResource->GetRmlFile().GetData()); } } #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) if (ref_context.HasDocument() && ShouldDebugContext(ref_context)) { DebugContext(&ref_context); } #endif return ref_context.HasDocument() ? EZ_SUCCESS : EZ_FAILURE; } ezResult ezRmlUi::LoadDocumentFromString(ezRmlUiContext& ref_context, const ezStringView& sContent) { UnloadDocument(ref_context); if (!sContent.IsEmpty()) { Rml::String sRmlContent = Rml::String(sContent.GetStartPointer(), sContent.GetElementCount()); // RmlUi is not thread safe, so we need to make that we only load/unload one document at a time. EZ_LOCK(m_pData->m_ContextsMutex); ref_context.LoadDocumentFromMemory(sRmlContent); } #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) if (ref_context.HasDocument() && ShouldDebugContext(ref_context)) { DebugContext(&ref_context); } #endif return ref_context.HasDocument() ? EZ_SUCCESS : EZ_FAILURE; } void ezRmlUi::UnloadDocument(ezRmlUiContext& ref_context) { if (ref_context.HasDocument()) { // RmlUi is not thread safe, so we need to make that we only load/unload one document at a time. EZ_LOCK(m_pData->m_ContextsMutex); static_cast<Rml::Context&>(ref_context).UnloadDocument(ref_context.GetDocument(0)); } } void ezRmlUi::ClearCaches() { ezUInt64 uiCurrentFrame = ezRenderWorld::GetFrameCounter(); if (uiCurrentFrame == m_pData->m_uiLastClearedCacheFrame) return; m_pData->m_uiLastClearedCacheFrame = uiCurrentFrame; EZ_LOCK(m_pData->m_ContextsMutex); Rml::Factory::ClearStyleSheetCache(); Rml::Factory::ClearTemplateCache(); } void ezRmlUi::ExtractContext(ezRmlUiContext& ref_context, ezGALTextureHandle hTexture) { if (ref_context.HasDocument() == false) return; // Unfortunately we need to hold a lock for the whole extraction of a context since RmlUi is not thread safe. EZ_LOCK(m_pData->m_ExtractionMutex); ref_context.ExtractRenderData(m_pData->m_RenderInterface, hTexture); } ezMutex& ezRmlUi::GetContextMutex() { return m_pData->m_ContextsMutex; } #if EZ_ENABLED(EZ_COMPILE_FOR_DEVELOPMENT) void ezRmlUi::DebugContext(ezRmlUiContext* pContext) { # if EZ_ENABLED(EZ_PLATFORM_WINDOWS) FillDebuggerFunctionTable(); if (s_DebuggerFunctions.m_InitFunc == nullptr) return; EZ_LOCK(m_pData->m_ContextsMutex); if (m_pData->m_bDebuggerInitialized) { s_DebuggerFunctions.m_ShutdownFunc(); m_pData->m_bDebuggerInitialized = false; } if (pContext != nullptr) { s_DebuggerFunctions.m_InitFunc(pContext); s_DebuggerFunctions.m_SetVisibleFunc(true); m_pData->m_bDebuggerInitialized = true; } # else ezLog::Error("RmlUi debugger is only available on Windows."); # endif } #endif EZ_STATICLINK_FILE(RmlUiPlugin, RmlUiPlugin_Implementation_RmlUiSingleton);