From 1fba0849713e0c9181bf937f5947bb3948530ff2 Mon Sep 17 00:00:00 2001 From: innerviewer Date: Mon, 28 Oct 2024 12:01:28 +0100 Subject: [PATCH] (Build) Fixed Linux build and removed litehtml. --- inc/Graphics/Pass/HTMLDrawerPass.h | 2 + inc/Graphics/Render/HTMLRenderer.h | 107 ++-- src/Graphics/Pass/HTMLDrawerPass.cpp | 160 +++++- .../Render/HTML/HTMLDrawableElement.cpp | 118 +++++ src/Graphics/Render/HTMLRenderer.cpp | 493 +++++++++++++----- 5 files changed, 669 insertions(+), 211 deletions(-) create mode 100644 src/Graphics/Render/HTML/HTMLDrawableElement.cpp diff --git a/inc/Graphics/Pass/HTMLDrawerPass.h b/inc/Graphics/Pass/HTMLDrawerPass.h index 5850dc7e..da3f373f 100644 --- a/inc/Graphics/Pass/HTMLDrawerPass.h +++ b/inc/Graphics/Pass/HTMLDrawerPass.h @@ -9,6 +9,7 @@ #include namespace SR_GRAPH_NS { +#ifdef SR_COMMON_LITEHTML class HTMLDrawerPass : public BasePass { SR_REGISTER_LOGICAL_NODE(HTMLDrawerPass, HTML Drawer Pass, { "Passes" }) using Super = BasePass; @@ -28,6 +29,7 @@ namespace SR_GRAPH_NS { SR_UTILS_NS::Web::HTMLPage::Ptr m_pPage = nullptr; }; +#endif } #endif //SR_ENGINE_HTML_DRAWER_PASS_H \ No newline at end of file diff --git a/inc/Graphics/Render/HTMLRenderer.h b/inc/Graphics/Render/HTMLRenderer.h index a53c404c..a2e7324d 100644 --- a/inc/Graphics/Render/HTMLRenderer.h +++ b/inc/Graphics/Render/HTMLRenderer.h @@ -5,74 +5,93 @@ #ifndef SR_ENGINE_GRAPHICS_HTML_RENDERER_H #define SR_ENGINE_GRAPHICS_HTML_RENDERER_H -#include -#include -#include +#include namespace SR_GRAPH_NS { - struct HTMLRendererUpdateContext { - SR_MATH_NS::FVector2 resolution; - SR_MATH_NS::FVector2 size; - SR_MATH_NS::FVector2 offset; - }; - - class HTMLDrawableElement : public SR_UTILS_NS::NonCopyable { + class TextBuilder; +#ifdef SR_COMMON_LITEHTML + class HTMLRenderContainer : public SR_UTILS_NS::Web::HTMLContainerInterface { + using Super = SR_UTILS_NS::Web::HTMLContainerInterface; + struct ShaderInfo { + SR_GTYPES_NS::Shader::Ptr pShader; + + uint32_t index = 0; + + struct MemInfo { + Memory::UBOManager::VirtualUBO virtualUBO; + DescriptorManager::VirtualDescriptorSet virtualDescriptor; + }; + + std::vector UBOs; + }; + struct TextAtlas { + int32_t id; + std::string text; + SR_GRAPH_NS::TextBuilder* pTextBuilderRef; + }; public: - ~HTMLDrawableElement() override; + using Ptr = SR_HTYPES_NS::SharedPtr; - void SetShader(SR_GTYPES_NS::Shader::Ptr pShader); + public: + HTMLRenderContainer(); + ~HTMLRenderContainer() override; - void SetPage(SR_UTILS_NS::Web::HTMLPage* pPage) { m_pPage = pPage; } - void SetNodeId(uint64_t id) { m_nodeId = id; } - void SetPipeline(Pipeline* pPipeline) { m_pipeline = pPipeline; } + virtual bool Init(); + virtual void DeInit(); - SR_NODISCARD SR_GTYPES_NS::Shader::Ptr GetShader() const { return m_pShader; } + virtual void Draw(); + virtual void Update(); - void Draw(); - void Update(HTMLRendererUpdateContext& context); + void SetCamera(const SR_GTYPES_NS::Camera::Ptr& pCamera) { m_pCamera = pCamera; } + void SetPipeline(Pipeline* pipeline) { m_pipeline = pipeline; } private: - SR_GTYPES_NS::Shader::Ptr m_pShader = nullptr; + void get_media_features(litehtml::media_features& media) const override; + void get_client_rect(litehtml::position& client) const override; - uint64_t m_nodeId = SR_ID_INVALID; - SR_UTILS_NS::Web::HTMLPage* m_pPage = nullptr; + litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) override; + void delete_font(litehtml::uint_ptr hFont) override; - bool m_dirtyMaterial = true; - int32_t m_virtualUBO = SR_ID_INVALID; - int32_t m_virtualDescriptor = SR_ID_INVALID; - Pipeline* m_pipeline = nullptr; + SR_NODISCARD int32_t text_width(const char* text, litehtml::uint_ptr hFont) override; - }; + void draw_solid_fill(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::web_color& color) override; + void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) override; - class HTMLRenderer : public SR_HTYPES_NS::SharedPtr { - using Super = SR_HTYPES_NS::SharedPtr; - public: - HTMLRenderer(Pipeline* pPipeline, SR_UTILS_NS::Web::HTMLPage::Ptr pPage); - ~HTMLRenderer(); + litehtml::element::ptr create_element(const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr& doc) override; - bool Init(); - void DeInit(); + private: + bool BeginElement(ShaderInfo& shaderInfo); + void DrawElement(ShaderInfo& shaderInfo); + void UpdateElement(ShaderInfo& shaderInfo); + void EndElement(ShaderInfo& shaderInfo); - void Draw(); - void Update(); + SR_NODISCARD TextAtlas* GetTextAtlas(const char* text, TextBuilder* pTextBuilder); - void SetScreenSize(const SR_MATH_NS::UVector2& size); - void SetCamera(const SR_GTYPES_NS::Camera::Ptr& pCamera) { m_pCamera = pCamera; } + void ClearTextAtlases(); private: - void PrepareNode(SR_UTILS_NS::Web::HTMLNode* pNode); - void DrawNode(const SR_UTILS_NS::Web::HTMLNode* pNode); - void UpdateNode(const SR_UTILS_NS::Web::HTMLNode* pNode, HTMLRendererUpdateContext& context); + SR_MATH_NS::IVector2 m_scroll; - private: - SR_UTILS_NS::Web::HTMLPage::Ptr m_pPage; + SR_GRAPH_NS::Memory::UBOManager& m_uboManager; + SR_GRAPH_NS::DescriptorManager& m_descriptorManager; + + SR_MATH_NS::FVector2 m_viewSize; + + bool m_isRendered = false; + bool m_updateMode = false; Pipeline* m_pipeline = nullptr; SR_GTYPES_NS::Camera::Ptr m_pCamera = nullptr; - std::vector m_drawableElements; - std::unordered_map m_shaders; + struct TextBuilderInfo { + TextBuilder* pTextBuilder; + SR_UTILS_NS::StringAtom fontName; + }; + std::vector m_textBuilders; + std::map m_shaders; + std::vector m_textAtlases; }; +#endif // SR_COMMON_LITEHTML } #endif //SR_ENGINE_GRAPHICS_HTML_RENDERER_H diff --git a/src/Graphics/Pass/HTMLDrawerPass.cpp b/src/Graphics/Pass/HTMLDrawerPass.cpp index fc6a69fe..fbf883ec 100644 --- a/src/Graphics/Pass/HTMLDrawerPass.cpp +++ b/src/Graphics/Pass/HTMLDrawerPass.cpp @@ -10,56 +10,168 @@ #include namespace SR_GRAPH_NS { +#ifdef SR_COMMON_LITEHTML SR_REGISTER_RENDER_PASS(HTMLDrawerPass) + HTMLDrawerPass::~HTMLDrawerPass() { + SRAssert2(m_fileWatchers.empty(), "Watchers are not empty!"); + SRAssert2(!m_pPage, "Page is not empty!"); + } bool HTMLDrawerPass::Load(const SR_XML_NS::Node& passNode) { SR_TRACY_ZONE; - auto&& path = SR_UTILS_NS::ResourceManager::Instance().GetResPath().Concat(passNode.GetAttribute("Path").ToString()); - m_pPage = SR_UTILS_NS::Web::HTMLParser::Instance().Parse(path); - return BasePass::Load(passNode); + LoadPage(passNode.GetAttribute("Path").ToString()); + return Super::Load(passNode); } - bool HTMLDrawerPass::Render() { - if (m_pRenderer) { - m_pRenderer->SetCamera(m_camera); - m_pRenderer->Draw(); + void HTMLDrawerPass::Prepare() { + if (m_needReloadPage) { + SR_INFO("HTMLDrawerPass::Prepare() : reloading page \"{}\"...,", m_pagePath.c_str()); + --m_needReloadPage; + ReloadPage(); + InitRenderer(); + GetPassPipeline()->SetDirty(true); } - return true; + Super::Prepare(); } - void HTMLDrawerPass::Update() { - if (m_pRenderer) { - m_pRenderer->SetCamera(m_camera); - m_pRenderer->Update(); + bool HTMLDrawerPass::Render() { + if (!m_pPage) { + return false; } - BasePass::Update(); - } - bool HTMLDrawerPass::Init() { - const bool result = BasePass::Init(); + if (const auto pRenderer = m_pPage->GetContainer().PolymorphicCast()) { + pRenderer->SetCamera(m_camera); - if (m_pPage) { const SR_MATH_NS::UVector2 size = m_camera ? m_camera->GetSize() : GetContext()->GetWindowSize(); + m_pPage->GetDocument()->render(static_cast(size.x)); + + pRenderer->Draw(); + return true; + } + return false; + } + + void HTMLDrawerPass::Update() { + if (!m_pPage) { + return; + } - m_pRenderer = HTMLRenderer::MakeShared(GetPassPipeline().Get(), m_pPage); - m_pRenderer->SetScreenSize(size); - m_pRenderer->Init(); + if (const auto pRenderer = m_pPage->GetContainer().PolymorphicCast()) { + pRenderer->SetCamera(m_camera); + pRenderer->Update(); } + Super::Update(); + } + bool HTMLDrawerPass::Init() { + const bool result = Super::Init(); + InitRenderer(); return result; } void HTMLDrawerPass::DeInit() { - if (m_pRenderer) { - m_pRenderer->DeInit(); + UnloadPage(); + + for (auto&& pWatcher : m_fileWatchers) { + pWatcher->Stop(); } + m_fileWatchers.clear(); + Super::DeInit(); } void HTMLDrawerPass::OnResize(const SR_MATH_NS::UVector2& size) { - if (m_pRenderer) { - m_pRenderer->SetScreenSize(size); + if (!m_pPage) { + return; } + + m_pPage->GetDocument()->media_changed(); + m_pPage->GetDocument()->render(static_cast(size.x)); + Super::OnResize(size); } + + bool HTMLDrawerPass::LoadPage(const SR_UTILS_NS::Path& pagePath) { + if (pagePath.IsEmpty()) { + SR_ERROR("HTMLDrawerPass::LoadPage() : path is empty!"); + return false; + } + + UnloadPage(); + + m_pagePath = pagePath; + + HTMLRenderContainer::Ptr pContainer = new HTMLRenderContainer(); + pContainer->SetCamera(m_camera); + pContainer->SetPipeline(GetPassPipeline().Get()); + + m_pPage = SR_UTILS_NS::Web::HTMLPage::Load(m_pagePath, + pContainer.PolymorphicCast()); + + if (!m_pPage) { + SR_ERROR("HTMLDrawerPass::LoadPage() : failed to parse page: {}", m_pagePath.c_str()); + } + + if (m_pPage) { + for (auto&& pWatcher : m_fileWatchers) { + pWatcher->Stop(); + } + m_fileWatchers.clear(); + for (const SR_UTILS_NS::Path& path : m_pPage->GetPaths()) { + AddWatcher(path); + } + } + else if (m_fileWatchers.empty()) { + AddWatcher(SR_UTILS_NS::ResourceManager::Instance().GetResPath().Concat(m_pagePath)); + } + + return m_pPage; + } + + void HTMLDrawerPass::UnloadPage() { + if (m_pPage) { + if (auto&& pContainer = m_pPage->GetContainer().PolymorphicCast()) { + pContainer->DeInit(); + } + m_pPage.AutoFree(); + m_pPage = nullptr; + } + } + + bool HTMLDrawerPass::ReloadPage() { + SR_INFO("HTMLDrawerPass::ReloadPage() : {}", m_pagePath.c_str()); + if (!m_pagePath.IsEmpty()) { + return LoadPage(m_pagePath); + } + return false; + } + + void HTMLDrawerPass::AddWatcher(const SR_UTILS_NS::Path& path) { + for (auto&& pWatcher : m_fileWatchers) { + if (pWatcher->GetPath() == path) { + return; + } + } + if (auto&& pWatcher = SR_UTILS_NS::ResourceManager::Instance().StartWatch(path)) { + pWatcher->SetCallBack([this](SR_UTILS_NS::FileWatcher*) { + ++m_needReloadPage; + }); + m_fileWatchers.emplace_back(pWatcher); + } + } + + void HTMLDrawerPass::InitRenderer() { + if (!m_pPage) { + return; + } + + if (const auto pRenderer = m_pPage->GetContainer().PolymorphicCast()) { + pRenderer->Init(); + } + + const SR_MATH_NS::UVector2 size = m_camera ? m_camera->GetSize() : GetContext()->GetWindowSize(); + + m_pPage->GetDocument()->render(static_cast(size.x)); + } +#endif //SR_COMMON_LITEHTML } diff --git a/src/Graphics/Render/HTML/HTMLDrawableElement.cpp b/src/Graphics/Render/HTML/HTMLDrawableElement.cpp new file mode 100644 index 00000000..8f5b4cd9 --- /dev/null +++ b/src/Graphics/Render/HTML/HTMLDrawableElement.cpp @@ -0,0 +1,118 @@ +// +// Created by Monika on 02.10.2024. +// + +#include + +namespace SR_GRAPH_NS { + /*HTMLDrawableElement::~HTMLDrawableElement() { + SetShader(nullptr); + SetTexture(nullptr); + + auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); + auto&& descriptorManager = SR_GRAPH_NS::DescriptorManager::Instance(); + + if (m_virtualUBO != SR_ID_INVALID && !uboManager.FreeUBO(&m_virtualUBO)) { + SR_ERROR("HTMLDrawableElement::~HTMLDrawableElement() : failed to free virtual uniform buffer object!"); + } + + if (m_virtualDescriptor != SR_ID_INVALID) { + descriptorManager.FreeDescriptorSet(&m_virtualDescriptor); + } + } + + void HTMLDrawableElement::SetShader(SR_GTYPES_NS::Shader::Ptr pShader) { + m_dirtyMaterial |= m_pShader != pShader; + m_pShader = pShader; + } + + void HTMLDrawableElement::SetTexture(SR_GTYPES_NS::Texture::Ptr pTexture) { + if (m_pTexture == pTexture) { + return; + } + if (m_pTexture) { + m_pTexture->RemoveUsePoint(); + } + if ((m_pTexture = pTexture)) { + pTexture->AddUsePoint(); + } + m_dirtyMaterial = true; + } + + HTMLRendererUpdateResult HTMLDrawableElement::Update(const HTMLRendererUpdateContext& context) { + HTMLRendererUpdateResult result; + + if (m_virtualUBO == SR_ID_INVALID) SR_UNLIKELY_ATTRIBUTE { + SRHalt("HTMLDrawableElement::Update() : virtual UBO is invalid!"); + return result; + } + + m_pipeline->SetCurrentShader(m_pShader); + + auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); + if (uboManager.BindNoDublicateUBO(m_virtualUBO) != Memory::UBOManager::BindResult::Success) SR_UNLIKELY_ATTRIBUTE { + return result; + } + + auto&& pNode = m_pPage->GetNodeById(m_nodeId); + auto&& style = pNode->GetStyle(); + + SR_MATH_NS::FVector2 position = context.offset; + position.x = position.x + context.size.x; + position.y = -position.y - context.size.y; + position = SR_MATH_NS::FVector2(-1, 1) + (position / context.resolution); + + m_pShader->SetVec2("position"_atom_hash, position); + m_pShader->SetVec2("size"_atom_hash, context.size / context.resolution); + + if (style.backgroundColor.colorType == SR_UTILS_NS::Web::CSSColor::ColorType::RGBA) { + m_pShader->SetVec4("backgroundColor"_atom_hash, style.backgroundColor.color.ToFColor()); + //m_pShader->SetVec4("backgroundColor"_atom_hash, SR_MATH_NS::FColor::Cyan()); + } + + SR_MAYBE_UNUSED_VAR m_pShader->Flush(); + + return result; + } + + const SR_UTILS_NS::Web::CSSStyle& HTMLDrawableElement::GetStyle() const { + return m_pPage->GetNodeById(m_nodeId)->GetStyle(); + } + + void HTMLDrawableElement::Draw() { + SR_TRACY_ZONE; + + auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); + auto&& descriptorManager = SR_GRAPH_NS::DescriptorManager::Instance(); + + if (m_dirtyMaterial) SR_UNLIKELY_ATTRIBUTE { + m_virtualUBO = uboManager.AllocateUBO(m_virtualUBO); + if (m_virtualUBO == SR_ID_INVALID) SR_UNLIKELY_ATTRIBUTE { + return; + } + + m_virtualDescriptor = descriptorManager.AllocateDescriptorSet(m_virtualDescriptor); + } + + uboManager.BindUBO(m_virtualUBO); + + const auto result = descriptorManager.Bind(m_virtualDescriptor); + + if (m_pipeline->GetCurrentBuildIteration() == 0) { + if (result == DescriptorManager::BindResult::Duplicated || m_dirtyMaterial) SR_UNLIKELY_ATTRIBUTE { + if (m_pTexture) { + m_pShader->SetSampler2D("image"_atom, m_pTexture); + } + m_pShader->FlushSamplers(); + descriptorManager.Flush(); + } + m_pipeline->GetCurrentShader()->FlushConstants(); + } + + if (result != DescriptorManager::BindResult::Failed) SR_UNLIKELY_ATTRIBUTE { + m_pipeline->Draw(4); + } + + m_dirtyMaterial = false; + }*/ +} \ No newline at end of file diff --git a/src/Graphics/Render/HTMLRenderer.cpp b/src/Graphics/Render/HTMLRenderer.cpp index 5225fe01..45f6d9e5 100644 --- a/src/Graphics/Render/HTMLRenderer.cpp +++ b/src/Graphics/Render/HTMLRenderer.cpp @@ -2,248 +2,455 @@ // Created by Monika on 14.08.2024. // -#include #include #include #include +#include +#include #include namespace SR_GRAPH_NS { - HTMLDrawableElement::~HTMLDrawableElement() { - SetShader(nullptr); + SR_UTILS_NS::StringAtom SR_SOLID_FILL_SHADER = "solid-fill"_atom; + SR_UTILS_NS::StringAtom SR_TEXT_SHADER = "text"_atom; + + std::vector SR_HTML_SHADERS = { + SR_SOLID_FILL_SHADER, SR_TEXT_SHADER + }; + +#ifdef SR_COMMON_LITEHTML + HTMLRenderContainer::HTMLRenderContainer() + : Super() + , m_uboManager(SR_GRAPH_NS::Memory::UBOManager::Instance()) + , m_descriptorManager(SR_GRAPH_NS::DescriptorManager::Instance()) + { } - auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); - auto&& descriptorManager = SR_GRAPH_NS::DescriptorManager::Instance(); + HTMLRenderContainer::~HTMLRenderContainer() { + SRAssert2(m_textAtlases.empty(), "Text atlases are not empty!"); + SRAssert2(m_textBuilders.empty(), "Text builders are not empty!"); + SRAssert2(m_shaders.empty(), "Shaders are not empty!"); + } - if (m_virtualUBO != SR_ID_INVALID && !uboManager.FreeUBO(&m_virtualUBO)) { - SR_ERROR("HTMLDrawableElement::~HTMLDrawableElement() : failed to free virtual uniform buffer object!"); + bool HTMLRenderContainer::Init() { + for (auto&& shaderId : SR_HTML_SHADERS) { + if (auto&& pShader = SR_GTYPES_NS::Shader::Load("Engine/Shaders/Web/" + shaderId.ToString() + ".srsl")) { + pShader->AddUsePoint(); + ShaderInfo shaderInfo; + shaderInfo.pShader = pShader; + m_shaders[shaderId] = shaderInfo; + } + else { + SR_ERROR("HTMLRenderContainer::Init() : failed to load shader \"{}\"!", shaderId.c_str()); + return false; + } } - if (m_virtualDescriptor != SR_ID_INVALID) { - descriptorManager.FreeDescriptorSet(&m_virtualDescriptor); - } + return true; } - void HTMLDrawableElement::SetShader(SR_GTYPES_NS::Shader::Ptr pShader) { - m_dirtyMaterial |= m_pShader != pShader; - m_pShader = pShader; + void HTMLRenderContainer::DeInit() { + ClearTextAtlases(); + + for (auto& shaderInfo : m_shaders | std::views::values) { + shaderInfo.pShader->RemoveUsePoint(); + + for (auto&& memInfo : shaderInfo.UBOs) { + if (memInfo.virtualUBO != SR_ID_INVALID && !m_uboManager.FreeUBO(&memInfo.virtualUBO)) { + SR_ERROR("HTMLRenderContainer::DeInit() : failed to free uniform buffer object!"); + } + + if (memInfo.virtualDescriptor != SR_ID_INVALID && !m_descriptorManager.FreeDescriptorSet(&memInfo.virtualDescriptor)) { + SR_ERROR("HTMLRenderContainer::DeInit() : failed to free descriptor set!"); + } + } + } + m_shaders.clear(); + + /// INFO: чистит сам документ через delete_font + /// for (auto& textBuilderInfo : m_textBuilders) { + /// delete textBuilderInfo.pTextBuilder; + /// } + /// m_textBuilders.clear(); } - void HTMLDrawableElement::Draw() { + void HTMLRenderContainer::Draw() { SR_TRACY_ZONE; - auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); - auto&& descriptorManager = SR_GRAPH_NS::DescriptorManager::Instance(); + m_isRendered = true; + + ClearTextAtlases(); - if (m_dirtyMaterial) SR_UNLIKELY_ATTRIBUTE { - m_virtualUBO = uboManager.AllocateUBO(m_virtualUBO); - if (m_virtualUBO == SR_ID_INVALID) SR_UNLIKELY_ATTRIBUTE { + for (auto&& [id, shaderInfo] : m_shaders) { + if (shaderInfo.pShader->HasErrors()) { + SR_ERROR("HTMLRenderContainer::Draw() : shader \"{}\" has errors!", id.c_str()); return; } + shaderInfo.index = 0; + } - m_virtualDescriptor = descriptorManager.AllocateDescriptorSet(m_virtualDescriptor); + if (SRVerify2(GetPage(), "HTMLRenderContainer::Draw() : page is not set!")) { + litehtml::position clip; + get_client_rect(clip); + m_viewSize = SR_MATH_NS::FVector2(clip.width, clip.height); + GetPage()->GetDocument()->draw(reinterpret_cast(this), m_scroll.x, m_scroll.y, &clip); } + } - uboManager.BindUBO(m_virtualUBO); + void HTMLRenderContainer::Update() { + SR_TRACY_ZONE; - const auto result = descriptorManager.Bind(m_virtualDescriptor); + if (!m_isRendered) { + return; + } - if (m_pipeline->GetCurrentBuildIteration() == 0) { - if (result == DescriptorManager::BindResult::Duplicated || m_dirtyMaterial) SR_UNLIKELY_ATTRIBUTE { - descriptorManager.Flush(); - } - m_pipeline->GetCurrentShader()->FlushConstants(); + if (SR_UTILS_NS::Input::Instance().GetKeyDown(SR_UTILS_NS::KeyCode::Tilde)) { + m_scroll = SR_MATH_NS::IVector2(0, 0); + m_pipeline->SetDirty(true); } - if (result != DescriptorManager::BindResult::Failed) SR_UNLIKELY_ATTRIBUTE { - m_pipeline->Draw(4); + if (SR_UTILS_NS::Input::Instance().GetKey(SR_UTILS_NS::KeyCode::DownArrow)) { + m_scroll.y -= 10; + m_pipeline->SetDirty(true); } - m_dirtyMaterial = false; - } + if (SR_UTILS_NS::Input::Instance().GetKey(SR_UTILS_NS::KeyCode::UpArrow)) { + m_scroll.y += 10; + m_pipeline->SetDirty(true); + } - void HTMLDrawableElement::Update(HTMLRendererUpdateContext& context) { - m_pipeline->SetCurrentShader(m_pShader); + if (SR_UTILS_NS::Input::Instance().GetKey(SR_UTILS_NS::KeyCode::LeftArrow)) { + m_scroll.x += 10; + m_pipeline->SetDirty(true); + } + + if (SR_UTILS_NS::Input::Instance().GetKey(SR_UTILS_NS::KeyCode::RightArrow)) { + m_scroll.x -= 10; + m_pipeline->SetDirty(true); + } - auto&& pNode = m_pPage->GetNodeById(m_nodeId); - auto&& style = pNode->GetStyle(); + for (auto&& [id, shaderInfo] : m_shaders) { + if (!shaderInfo.pShader->Ready()) { + continue; + } - auto&& uboManager = SR_GRAPH_NS::Memory::UBOManager::Instance(); - if (uboManager.BindNoDublicateUBO(m_virtualUBO) == Memory::UBOManager::BindResult::Success) SR_UNLIKELY_ATTRIBUTE { - const auto size = SR_MATH_NS::FVector2( - style.width.CalculateValue(context.size.x) / context.resolution.x, - style.height.CalculateValue(context.size.y) / context.resolution.y - ); - m_pShader->SetVec2("size"_atom_hash, size); + if (shaderInfo.pShader->BeginSharedUBO()) { + if (m_pCamera) { + shaderInfo.pShader->SetMat4(SHADER_ORTHOGONAL_MATRIX, m_pCamera->GetOrthogonal()); + } + else { + SR_ERROR("HTMLRenderContainer::Update() : no camera!"); + } - SR_MAYBE_UNUSED_VAR m_pShader->Flush(); + shaderInfo.pShader->SetVec2(SHADER_RESOLUTION, m_viewSize); + shaderInfo.pShader->EndSharedUBO(); + } } } - /// ---------------------------------------------------------------------------------------------------------------- - - HTMLRenderer::HTMLRenderer(Pipeline* pPipeline, SR_UTILS_NS::Web::HTMLPage::Ptr pPage) - : Super(this, SR_UTILS_NS::SharedPtrPolicy::Automatic) - , m_pPage(std::move(pPage)) - , m_pipeline(pPipeline) - { } + void HTMLRenderContainer::get_media_features(litehtml::media_features& media) const { + SR_TRACY_ZONE; - HTMLRenderer::~HTMLRenderer() { - SRAssert2(m_drawableElements.empty(), "HTMLRenderer::~HTMLRenderer() : not all elements were deleted!"); - SRAssert2(m_shaders.empty(), "HTMLRenderer::~HTMLRenderer() : not all shaders were deleted!"); + auto&& resolution = SR_PLATFORM_NS::GetScreenResolution().Cast(); + + litehtml::position client; + get_client_rect(client); + media.type = litehtml::media_type_screen; + media.width = client.width; + media.height = client.height; + media.device_width = resolution.x; + media.device_height = resolution.y; + media.color = 8; + media.monochrome = 0; + media.color_index = 256; + media.resolution = 96; } - bool HTMLRenderer::Init() { - if (!m_shaders.empty()) { - SR_ERROR("HTMLRenderer::Init() : shaders are already initialized!"); - return false; - } + void HTMLRenderContainer::get_client_rect(litehtml::position& client) const { + SR_TRACY_ZONE; + + client.x = 0; + client.y = 0; - if (auto&& pShader = SR_GTYPES_NS::Shader::Load("Engine/Shaders/Web/html.srsl")) { - pShader->AddUsePoint(); - m_shaders["common"] = pShader; + if (m_pCamera) { + client.width = m_pCamera->GetSize().Cast().x; + client.height = m_pCamera->GetSize().Cast().y; + } + else if (m_pipeline) { + client.width = m_pipeline->GetWindow()->GetSize().Cast().x; + client.height = m_pipeline->GetWindow()->GetSize().Cast().y; } else { - return false; + SRHalt("HTMLRenderContainer::get_client_rect() : no camera or pipeline!"); } + } - SRAssert2(m_drawableElements.empty(), "HTMLRenderer::Init() : drawable elements are not empty!"); + litehtml::uint_ptr HTMLRenderContainer::create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) { + auto&& pIt = std::find_if(m_textBuilders.begin(), m_textBuilders.end(), [faceName, size](const TextBuilderInfo& info) { + return info.fontName == faceName && info.pTextBuilder->GetFontSize() == size; + }); + if (pIt != m_textBuilders.end()) { + return reinterpret_cast(pIt->pTextBuilder); + } - m_pPage->RemoveUserDataRecursively(); - PrepareNode(m_pPage->GetBody()); + std::vector fonts = SR_UTILS_NS::StringUtils::SplitView(faceName, ","); - return true; - } + std::vector candidates; - void HTMLRenderer::DeInit() { - for (auto&& pElement : m_drawableElements) { - delete pElement; + for (auto&& font : fonts) { + candidates.emplace_back(SR_UTILS_NS::ResourceManager::Instance().GetResPath().Concat(font).ConcatExt(".ttf")); + candidates.emplace_back(SR_UTILS_NS::ResourceManager::Instance().GetResPath().Concat("Engine/Fonts").Concat(font).ConcatExt(".ttf")); + candidates.emplace_back(SR_UTILS_NS::ResourceManager::Instance().GetResPath().Concat(font).ConcatExt(".ttf")); } - m_drawableElements.clear(); - for (auto&& pShader: m_shaders | std::views::values) { - pShader->RemoveUsePoint(); + SR_UTILS_NS::Path fullPath; + for (auto&& candidate : candidates) { + if (candidate.IsFile()) { + fullPath = candidate; + break; + } } - m_shaders.clear(); - } - - void HTMLRenderer::Draw() { - SR_TRACY_ZONE; - - m_pipeline->SetCurrentShader(nullptr); - DrawNode(m_pPage->GetBody()); + if (!fullPath.IsFile()) { + SR_ERROR("HTMLRenderContainer::create_font() : font \"{}\" not found!", faceName); + return 0; + } - if (auto&& pShader = m_pipeline->GetCurrentShader()) { - pShader->UnUse(); + auto&& pFont = SR_GTYPES_NS::Font::Load(fullPath); + if (!pFont) { + SR_ERROR("HTMLRenderContainer::create_font() : failed to load font \"{}\"!", fullPath.c_str()); + return 0; } - } - void HTMLRenderer::Update() { - SR_TRACY_ZONE; + auto&& pTextBuilder = new TextBuilder(pFont); - for (auto&& pShader : m_shaders | std::views::values) { - m_pipeline->SetCurrentShader(pShader); + pTextBuilder->SetFontSize(size); + pTextBuilder->SetKerning(true); - if (!pShader || !pShader->Ready() || !m_pCamera) SR_UNLIKELY_ATTRIBUTE { - return; - } + TextBuilderInfo textBuilderInfo; + textBuilderInfo.pTextBuilder = pTextBuilder; + textBuilderInfo.fontName = SR_UTILS_NS::StringAtom(faceName); - if (pShader->BeginSharedUBO()) SR_LIKELY_ATTRIBUTE { - pShader->SetMat4(SHADER_ORTHOGONAL_MATRIX, m_pCamera->GetOrthogonal()); - pShader->SetVec2(SHADER_RESOLUTION, m_pPage->GetSize().Cast()); + m_textBuilders.emplace_back(textBuilderInfo); - const float_t aspect = m_pPage->GetSize().Aspect(); - const SR_MATH_NS::FVector2 aspectVec = aspect > 1.f ? SR_MATH_NS::FVector2(1.f / aspect, 1.f) : SR_MATH_NS::FVector2(1.f, aspect); - pShader->SetVec2(SHADER_ASPECT, aspectVec); + return reinterpret_cast(pTextBuilder); + } - pShader->EndSharedUBO(); - } - else { - SR_ERROR("HTMLDrawerPass::Update() : failed to bind shared UBO!"); + void HTMLRenderContainer::delete_font(litehtml::uint_ptr hFont) { + auto&& pTextBuilder = reinterpret_cast(hFont); + if (!pTextBuilder) { + return; + } + + for (auto&& pIt = m_textBuilders.begin(); pIt != m_textBuilders.end(); ++pIt) { + if (pIt->pTextBuilder == pTextBuilder) { + delete pTextBuilder; + m_textBuilders.erase(pIt); return; } } - HTMLRendererUpdateContext context; - context.size = m_pPage->GetSize().Cast(); - context.resolution = m_pPage->GetSize().Cast(); - UpdateNode(m_pPage->GetBody(), context); + SR_ERROR("HTMLRenderContainer::delete_font() : font not found!"); } - void HTMLRenderer::SetScreenSize(const SR_MATH_NS::UVector2& size) { - if (m_pPage) { - m_pPage->SetSize(size); + int32_t HTMLRenderContainer::text_width(const char* text, litehtml::uint_ptr hFont) { + TextBuilder* pTextBuilder = reinterpret_cast(hFont); + if (!pTextBuilder) { + return 0; } + + return pTextBuilder->CalculateTextWidth(text); } - void HTMLRenderer::PrepareNode(SR_UTILS_NS::Web::HTMLNode* pNode) { + void HTMLRenderContainer::draw_solid_fill(litehtml::uint_ptr, const litehtml::background_layer& layer, const litehtml::web_color& color) { SR_TRACY_ZONE; - if (!SRVerify(pNode)) { + if (color == litehtml::web_color::transparent) { return; } - auto&& pDrawableElement = m_drawableElements.emplace_back(new HTMLDrawableElement()); - pDrawableElement->SetShader(m_shaders["common"]); - pDrawableElement->SetNodeId(pNode->GetId()); - pDrawableElement->SetPipeline(m_pipeline); - pDrawableElement->SetPage(m_pPage.Get()); - - pNode->SetUserData(pDrawableElement); + auto&& pIt = m_shaders.find(SR_SOLID_FILL_SHADER); + if (pIt == m_shaders.end()) SR_UNLIKELY_ATTRIBUTE { + SR_ERROR("HTMLRenderContainer::DrawElement() : shader \"{}\" not found!", SR_SOLID_FILL_SHADER.c_str()); + return; + } + ShaderInfo& shaderInfo = pIt->second; - for (const auto& childId : pNode->GetChildren()) { - auto&& pChild = m_pPage->GetNodeById(childId); - PrepareNode(pChild); + if (!BeginElement(shaderInfo)) { + return; } + + auto&& pShader = m_pipeline->GetCurrentShader(); + const auto& box = layer.clip_box; + + SR_MATH_NS::FVector2 position = SR_MATH_NS::FVector2(box.x, box.y) * 2.f; + position.x = position.x + box.width; + position.y = -position.y - box.height; + + pShader->SetVec2("position"_atom_hash, SR_MATH_NS::FVector2(-1, 1) + position / m_viewSize); + pShader->SetVec2("size"_atom_hash, SR_MATH_NS::FVector2(box.width, box.height) / m_viewSize); + pShader->SetVec4("color"_atom_hash, SR_MATH_NS::FColor(color.red, color.green, color.blue, color.alpha) / 255.f); + DrawElement(shaderInfo); + UpdateElement(shaderInfo); + EndElement(shaderInfo); } - void HTMLRenderer::DrawNode(const SR_UTILS_NS::Web::HTMLNode* pNode) { + void HTMLRenderContainer::draw_text(litehtml::uint_ptr, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) { SR_TRACY_ZONE; - if (!SRVerify(pNode)) { + if (color == litehtml::web_color::transparent) { return; } - auto&& pDrawableElement = static_cast(pNode->GetUserData()); - if (!SRVerify(pDrawableElement)) { + auto&& pIt = m_shaders.find(SR_TEXT_SHADER); + if (pIt == m_shaders.end()) SR_UNLIKELY_ATTRIBUTE { + SR_ERROR("HTMLRenderContainer::DrawElement() : shader \"{}\" not found!", SR_TEXT_SHADER.c_str()); return; } + ShaderInfo& shaderInfo = pIt->second; + + auto&& pTextAtlas = GetTextAtlas(text, reinterpret_cast(hFont)); + if (!pTextAtlas) { + return; + } + + if (!BeginElement(shaderInfo)) { + return; + } + + auto&& pShader = m_pipeline->GetCurrentShader(); - auto&& pShader = pDrawableElement->GetShader(); - if (pShader != m_pipeline->GetCurrentShader()) { - if (m_pipeline->GetCurrentShader()) { - m_pipeline->GetCurrentShader()->UnUse(); + litehtml::position box = pos; + box.height += pTextAtlas->pTextBuilderRef->GetHeight(); + + SR_MATH_NS::FVector2 position = SR_MATH_NS::FVector2(box.x, box.y) * 2.f; + position.x = position.x + box.width; + position.y = -position.y - box.height; + + pShader->SetVec2("position"_atom_hash, SR_MATH_NS::FVector2(-1, 1) + position / m_viewSize); + pShader->SetVec2("size"_atom_hash, SR_MATH_NS::FVector2(box.width, box.height) / m_viewSize); + pShader->SetVec4("color"_atom_hash, SR_MATH_NS::FColor(color.red, color.green, color.blue, color.alpha) / 255.f); + pShader->SetSampler2D("textAtlas", pTextAtlas->id); + + DrawElement(shaderInfo); + UpdateElement(shaderInfo); + EndElement(shaderInfo); + } + + litehtml::element::ptr HTMLRenderContainer::create_element(const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr& doc) { + SR_TRACY_ZONE; + return nullptr; + } + + bool HTMLRenderContainer::BeginElement(ShaderInfo& shaderInfo) { + if (m_pipeline->GetCurrentShader() != shaderInfo.pShader) { + const auto result = shaderInfo.pShader->Use(); + if (result == ShaderBindResult::Failed) SR_UNLIKELY_ATTRIBUTE { + SR_ERROR("HTMLRenderContainer::BeginElement() : failed to use shader \"{}\"!", shaderInfo.pShader->GetResourceId().c_str()); + return false; } - if (pShader->Use() == ShaderBindResult::Failed) { - return; + } + + return true; + } + + void HTMLRenderContainer::UpdateElement(ShaderInfo& shaderInfo) { + auto&& pShader = m_pipeline->GetCurrentShader(); + if (!pShader->Flush()) { + SR_ERROR("HTMLRenderContainer::UpdateElement() : failed to flush shader \"{}\"!", pShader->GetResourceId().c_str()); + } + } + + void HTMLRenderContainer::EndElement(ShaderInfo& shaderInfo) { + ++shaderInfo.index; + } + + HTMLRenderContainer::TextAtlas* HTMLRenderContainer::GetTextAtlas(const char* text, TextBuilder* pTextBuilder) { + SR_TRACY_ZONE; + + for (auto&& textAtlas : m_textAtlases) { + if (textAtlas.text == text && textAtlas.pTextBuilderRef == pTextBuilder) { + return &textAtlas; } } - pDrawableElement->Draw(); + if (!pTextBuilder->Build(text)) { + //SR_ERROR("HTMLRenderContainer::GetTextAtlas() : failed to build text!"); + return nullptr; + } + + SR_GRAPH_NS::SRTextureCreateInfo textureCreateInfo; - for (const auto& childId : pNode->GetChildren()) { - auto&& pChild = m_pPage->GetNodeById(childId); - DrawNode(pChild); + textureCreateInfo.pData = pTextBuilder->GetData(); + textureCreateInfo.format = pTextBuilder->GetColorFormat(); + textureCreateInfo.width = pTextBuilder->GetWidth(); + textureCreateInfo.height = pTextBuilder->GetHeight(); + textureCreateInfo.compression = TextureCompression::None; + textureCreateInfo.filter = TextureFilter::NEAREST; + textureCreateInfo.mipLevels = 1; + textureCreateInfo.cpuUsage = false; + textureCreateInfo.alpha = true; + + EVK_PUSH_LOG_LEVEL(EvoVulkan::Tools::LogLevel::ErrorsOnly); + + const int32_t id = m_pipeline->AllocateTexture(textureCreateInfo); + + EVK_POP_LOG_LEVEL(); + + if (id == SR_ID_INVALID) { + SR_ERROR("HTMLRenderContainer::GetTextAtlas() : failed to allocate texture!"); + return nullptr; } + + TextAtlas textAtlas; + textAtlas.id = id; + textAtlas.text = text; + textAtlas.pTextBuilderRef = pTextBuilder; + m_textAtlases.emplace_back(textAtlas); + return &m_textAtlases.back(); } - void HTMLRenderer::UpdateNode(const SR_UTILS_NS::Web::HTMLNode* pNode, HTMLRendererUpdateContext& context) { + void HTMLRenderContainer::ClearTextAtlases() { SR_TRACY_ZONE; - if (!SRVerify(pNode)) { - return; + for (auto&& textAtlas : m_textAtlases) { + if (!m_pipeline->FreeTexture(&textAtlas.id)) { + SR_ERROR("HTMLRenderContainer::ClearTextAtlases() : failed to free texture!"); + } } + m_textAtlases.clear(); + } + + void HTMLRenderContainer::DrawElement(ShaderInfo &shaderInfo) { + if (shaderInfo.index >= shaderInfo.UBOs.size()) SR_UNLIKELY_ATTRIBUTE { + ShaderInfo::MemInfo memInfo; + + memInfo.virtualUBO = m_uboManager.AllocateUBO(SR_ID_INVALID); + if (memInfo.virtualUBO == SR_ID_INVALID) SR_UNLIKELY_ATTRIBUTE { + SR_ERROR("HTMLRenderContainer::DrawElement() : failed to allocate uniform buffer object!"); + return; + } - auto&& pDrawableElement = static_cast(pNode->GetUserData()); - if (SRVerify(pDrawableElement)) { - pDrawableElement->Update(context); + memInfo.virtualDescriptor = m_descriptorManager.AllocateDescriptorSet(SR_ID_INVALID); + if (memInfo.virtualDescriptor == SR_ID_INVALID) SR_UNLIKELY_ATTRIBUTE { + SR_ERROR("HTMLRenderContainer::DrawElement() : failed to allocate descriptor set!"); + return; + } + shaderInfo.UBOs.emplace_back(memInfo); } - for (const auto& childId : pNode->GetChildren()) { - auto&& pChild = m_pPage->GetNodeById(childId); - UpdateNode(pChild, context); + m_uboManager.BindUBO(shaderInfo.UBOs[shaderInfo.index].virtualUBO); + + const auto result = m_descriptorManager.Bind(shaderInfo.UBOs[shaderInfo.index].virtualDescriptor); + + if (m_pipeline->GetCurrentBuildIteration() == 0) { + shaderInfo.pShader->FlushSamplers(); + m_descriptorManager.Flush(); + m_pipeline->GetCurrentShader()->FlushConstants(); + } + + if (result != DescriptorManager::BindResult::Failed) SR_UNLIKELY_ATTRIBUTE { + m_pipeline->Draw(4); } } +#endif //SR_COMMON_LITEHTML }