diff --git a/include/NazaraImgui/ImguiDrawer.hpp b/include/NazaraImgui/ImguiDrawer.hpp new file mode 100644 index 0000000..21473cd --- /dev/null +++ b/include/NazaraImgui/ImguiDrawer.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include + +#include + +namespace Nz +{ + class CommandBufferBuilder; + class RenderBuffer; + class RenderDevice; + class RenderFrame; + class RenderPipeline; + + class NAZARA_IMGUI_API ImguiDrawer + { + public: + ImguiDrawer(RenderDevice& renderDevice); + ImguiDrawer(const ImguiDrawer&) = delete; + ImguiDrawer(ImguiDrawer&&) noexcept = default; + ~ImguiDrawer(); + + ImguiDrawer& operator=(const ImguiDrawer&) = delete; + ImguiDrawer& operator=(ImguiDrawer&&) = delete; + + void Prepare(RenderFrame& renderFrame); + + void Reset(RenderFrame& renderFrame); + + void Draw(CommandBufferBuilder& builder); + + private: + bool LoadTexturedPipeline(); + bool LoadUntexturedPipeline(); + + RenderDevice& m_renderDevice; + + struct DrawCall + { + size_t vertex_offset, indice_offset; + std::vector cmdBuffer; + }; + std::vector m_drawCalls; + + struct + { + std::shared_ptr pipeline; + std::unordered_map textureShaderBindings; + Nz::ShaderBindingPtr uboShaderBinding; + std::shared_ptr textureSampler; + } m_texturedPipeline; + + struct + { + std::shared_ptr pipeline; + Nz::ShaderBindingPtr uboShaderBinding; + } m_untexturedPipeline; + + std::shared_ptr m_vertexBuffer; + std::shared_ptr m_indexBuffer; + std::shared_ptr m_uboBuffer; + }; +} \ No newline at end of file diff --git a/include/NazaraImgui/ImguiPipelinePass.hpp b/include/NazaraImgui/ImguiPipelinePass.hpp new file mode 100644 index 0000000..818af14 --- /dev/null +++ b/include/NazaraImgui/ImguiPipelinePass.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +namespace Nz +{ + class PassData; + + class NAZARA_IMGUI_API ImguiPipelinePass + : public FramePipelinePass + { + public: + ImguiPipelinePass(PassData& passData, std::string passName, const ParameterList& parameters = {}); + ImguiPipelinePass(const ImguiPipelinePass&) = delete; + ImguiPipelinePass(ImguiPipelinePass&&) = delete; + ~ImguiPipelinePass() = default; + + void Prepare(FrameData& frameData) override; + FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, const PassInputOuputs& inputOuputs) override; + + private: + std::string m_passName; + }; +} \ No newline at end of file diff --git a/include/NazaraImgui/NazaraImgui.hpp b/include/NazaraImgui/NazaraImgui.hpp index b4e218b..5df04f8 100644 --- a/include/NazaraImgui/NazaraImgui.hpp +++ b/include/NazaraImgui/NazaraImgui.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -11,8 +12,6 @@ namespace Nz { class Cursor; - class RenderFrame; - class RenderTarget; class RenderWindow; class Texture; class Window; @@ -39,6 +38,9 @@ namespace Nz void Update(Nz::Window& window, float dt); void Render(Nz::RenderTarget* renderTarget, Nz::RenderFrame& frame); + inline ImguiDrawer& GetImguiDrawer() { return m_imguiDrawer; } + inline const ImguiDrawer& GetImguiDrawer() const { return m_imguiDrawer; } + // User-defined void AddHandler(ImguiHandler* handler); void RemoveHandler(ImguiHandler* handler); @@ -63,35 +65,16 @@ namespace Nz std::shared_ptr GetMouseCursor(ImGuiMouseCursor cursorType); void UpdateMouseCursor(Nz::Window& window); - bool LoadTexturedPipeline(); - bool LoadUntexturedPipeline(); void UpdateFontTexture(); - void RenderDrawLists(Nz::RenderTarget* renderTarget, Nz::RenderFrame& frame, ImDrawData* drawData); - ImGuiContext* m_currentContext; std::string m_clipboardText; bool m_bWindowHasFocus; bool m_bMouseMoved; - struct - { - std::shared_ptr pipeline; - std::unordered_map textureShaderBindings; - Nz::ShaderBindingPtr uboShaderBinding; - std::shared_ptr textureSampler; - } m_texturedPipeline; - - struct - { - std::shared_ptr pipeline; - Nz::ShaderBindingPtr uboShaderBinding; - } m_untexturedPipeline; - - std::shared_ptr m_uboBuffer; + ImguiDrawer m_imguiDrawer; std::shared_ptr m_fontTexture; - std::unordered_set m_handlers; static Imgui* s_instance; diff --git a/src/NazaraImgui/ImguiDrawer.cpp b/src/NazaraImgui/ImguiDrawer.cpp new file mode 100644 index 0000000..c95910b --- /dev/null +++ b/src/NazaraImgui/ImguiDrawer.cpp @@ -0,0 +1,319 @@ +#include + +#include +#include +#include +#include +#include + + +#include + +const char shaderSource_Textured[] = +#include "Textured.nzsl.h" +; + +const char shaderSource_Untextured[] = +#include "Untextured.nzsl.h" +; + +namespace +{ + inline Nz::Vector2f ToNzVec2(ImVec2 v) + { + return { v.x, v.y }; + } + + inline Nz::Vector3f ToNzVec3(ImVec2 v) + { + return { v.x, v.y, 0.f }; + } + + inline Nz::Color ToNzColor(ImU32 color) + { + auto c = ImGui::ColorConvertU32ToFloat4(color); + return { c.x, c.y, c.z, c.w }; + } +} + +namespace Nz +{ + struct ImguiUbo + { + float screenWidth; + float screenHeight; + }; + + ImguiDrawer::ImguiDrawer(RenderDevice& renderDevice) + : m_renderDevice(renderDevice) + { + LoadTexturedPipeline(); + LoadUntexturedPipeline(); + } + + ImguiDrawer::~ImguiDrawer() + { + m_untexturedPipeline.uboShaderBinding.reset(); + m_untexturedPipeline.pipeline.reset(); + + m_texturedPipeline.uboShaderBinding.reset(); + m_texturedPipeline.textureShaderBindings.clear(); + m_texturedPipeline.textureSampler.reset(); + m_texturedPipeline.pipeline.reset(); + } + + void ImguiDrawer::Prepare(RenderFrame& frame) + { + m_drawCalls.clear(); + + ImDrawData* drawData = ImGui::GetDrawData(); + if (drawData->CmdListsCount == 0) + return; + + ImGuiIO& io = ImGui::GetIO(); + assert(io.Fonts->TexID != (ImTextureID)NULL); // You forgot to create and set font texture + + // scale stuff (needed for proper handling of window resize) + int fb_width = static_cast(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = static_cast(io.DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width == 0 || fb_height == 0) + return; + + ImguiUbo ubo{ fb_width / 2.f, fb_height / 2.f }; + auto& allocation = frame.GetUploadPool().Allocate(sizeof(ImguiUbo)); + + std::memcpy(allocation.mappedPtr, &ubo, sizeof(ImguiUbo)); + + frame.Execute([&](Nz::CommandBufferBuilder& builder) + { + builder.BeginDebugRegion("Imgui UBO Update", Nz::Color::Yellow()); + { + builder.PreTransferBarrier(); + builder.CopyBuffer(allocation, m_uboBuffer.get()); + builder.PostTransferBarrier(); + } + builder.EndDebugRegion(); + }, Nz::QueueType::Transfer); + + drawData->ScaleClipRects(io.DisplayFramebufferScale); + + // first pass over cmd lists to prepare buffers + std::vector vertices; + std::vector indices; + for (int n = 0; n < drawData->CmdListsCount; ++n) { + const ImDrawList* cmd_list = drawData->CmdLists[n]; + + DrawCall drawCall; + drawCall.vertex_offset = vertices.size(); + drawCall.indice_offset = indices.size(); + + vertices.reserve(vertices.size() + cmd_list->VtxBuffer.size()); + for (auto& vertex : cmd_list->VtxBuffer) + vertices.push_back({ ToNzVec3(vertex.pos), ToNzColor(vertex.col), ToNzVec2(vertex.uv) }); + + indices.reserve(indices.size() + cmd_list->IdxBuffer.size()); + for (auto indice : cmd_list->IdxBuffer) + indices.push_back(uint16_t(drawCall.vertex_offset + indice)); + + for (auto& cmd : cmd_list->CmdBuffer) + drawCall.cmdBuffer.push_back(cmd); + + m_drawCalls.push_back(std::move(drawCall)); + } + + + // now that we have macro buffers, allocate them on gpu + size_t size = vertices.size() * sizeof(Nz::VertexStruct_XYZ_Color_UV); + m_vertexBuffer = m_renderDevice.InstantiateBuffer(Nz::BufferType::Vertex, size, Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic, vertices.data()); + size = indices.size() * sizeof(uint16_t); + m_indexBuffer = m_renderDevice.InstantiateBuffer(Nz::BufferType::Index, size, Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic, indices.data()); + + } + + void ImguiDrawer::Draw(CommandBufferBuilder& builder) + { + if (m_drawCalls.empty()) + return; + + ImGuiIO& io = ImGui::GetIO(); + int fb_width = static_cast(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = static_cast(io.DisplaySize.y * io.DisplayFramebufferScale.y); + + builder.SetViewport(Nz::Recti{ 0, 0, fb_width, fb_height }); + builder.BindIndexBuffer(*m_indexBuffer, Nz::IndexType::U16); + builder.BindVertexBuffer(0, *m_vertexBuffer); + + for (auto& drawCall : m_drawCalls) + { + Nz::UInt64 indexOffset = drawCall.indice_offset; + for (auto& cmd : drawCall.cmdBuffer) + { + if (!cmd.UserCallback) + { + auto rect = cmd.ClipRect; + auto count = cmd.ElemCount; + auto texture = static_cast(cmd.GetTexID()); + + if (nullptr != texture) + { + if (std::end(m_texturedPipeline.textureShaderBindings) == m_texturedPipeline.textureShaderBindings.find(texture)) + { + auto binding = m_texturedPipeline.pipeline->GetPipelineInfo().pipelineLayout->AllocateShaderBinding(1); + binding->Update({ + { + 0, + Nz::ShaderBinding::SampledTextureBinding { + texture, m_texturedPipeline.textureSampler.get() + } + } + }); + m_texturedPipeline.textureShaderBindings[texture] = std::move(binding); + } + + builder.BindRenderPipeline(*m_texturedPipeline.pipeline); + builder.BindRenderShaderBinding(0, *m_texturedPipeline.uboShaderBinding); + builder.BindRenderShaderBinding(1, *m_texturedPipeline.textureShaderBindings[texture]); + } + else + { + builder.BindRenderPipeline(*m_untexturedPipeline.pipeline); + builder.BindRenderShaderBinding(0, *m_untexturedPipeline.uboShaderBinding); + } + + builder.SetScissor(Nz::Recti{ int(rect.x), int(rect.y), int(rect.z - rect.x), int(rect.w - rect.y) });// Nz::Recti{ int(rect.x), int(fb_height - rect.w), int(rect.z - rect.x), int(rect.w - rect.y) }); + + builder.DrawIndexed(count, 1, Nz::UInt32(indexOffset)); + } + indexOffset += cmd.ElemCount; + } + } + } + + void ImguiDrawer::Reset(RenderFrame& renderFrame) + { + m_drawCalls.clear(); + } + + bool ImguiDrawer::LoadTexturedPipeline() + { + nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(shaderSource_Textured, sizeof(shaderSource_Textured))); + if (!shaderModule) + throw std::runtime_error("Failed to parse shader module"); + + nzsl::ShaderWriter::States states; + states.optimize = true; + + auto shader = m_renderDevice.InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, states); + if (!shader) + throw std::runtime_error("Failed to instantiate shader"); + + m_texturedPipeline.textureSampler = m_renderDevice.InstantiateTextureSampler({}); + + Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; + + auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back(); + uboBinding.setIndex = 0; + uboBinding.bindingIndex = 0; + uboBinding.shaderStageFlags = nzsl::ShaderStageType::Vertex; + uboBinding.type = Nz::ShaderBindingType::UniformBuffer; + + auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back(); + textureBinding.setIndex = 1; + textureBinding.bindingIndex = 0; + textureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; + textureBinding.type = Nz::ShaderBindingType::Texture; + + std::shared_ptr renderPipelineLayout = m_renderDevice.InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); + + Nz::RenderPipelineInfo pipelineInfo; + pipelineInfo.pipelineLayout = renderPipelineLayout; + pipelineInfo.shaderModules.emplace_back(shader); + + pipelineInfo.depthBuffer = false; + pipelineInfo.faceCulling = Nz::FaceCulling::None; + pipelineInfo.scissorTest = true; + + pipelineInfo.blending = true; + pipelineInfo.blend.modeAlpha = Nz::BlendEquation::Add; + pipelineInfo.blend.srcColor = Nz::BlendFunc::SrcAlpha; + pipelineInfo.blend.dstColor = Nz::BlendFunc::InvSrcAlpha; + pipelineInfo.blend.srcAlpha = Nz::BlendFunc::One; + pipelineInfo.blend.dstAlpha = Nz::BlendFunc::Zero; + + auto& pipelineVertexBuffer = pipelineInfo.vertexBuffers.emplace_back(); + pipelineVertexBuffer.binding = 0; + pipelineVertexBuffer.declaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color_UV); + + m_texturedPipeline.pipeline = m_renderDevice.InstantiateRenderPipeline(pipelineInfo); + + m_uboBuffer = m_renderDevice.InstantiateBuffer(Nz::BufferType::Uniform, sizeof(ImguiUbo), Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic); + + m_texturedPipeline.uboShaderBinding = renderPipelineLayout->AllocateShaderBinding(0); + m_texturedPipeline.uboShaderBinding->Update({ + { + 0, + Nz::ShaderBinding::UniformBufferBinding { + m_uboBuffer.get(), 0, sizeof(ImguiUbo) + } + } + }); + + return true; + } + + bool ImguiDrawer::LoadUntexturedPipeline() + { + nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(shaderSource_Untextured, sizeof(shaderSource_Untextured))); + if (!shaderModule) + throw std::runtime_error("Failed to parse shader module"); + + nzsl::ShaderWriter::States states; + states.optimize = true; + + auto shader = m_renderDevice.InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, states); + if (!shader) + throw std::runtime_error("Failed to instantiate shader"); + + Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; + + auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back(); + uboBinding.setIndex = 0; + uboBinding.bindingIndex = 0; + uboBinding.shaderStageFlags = nzsl::ShaderStageType::Vertex; + uboBinding.type = Nz::ShaderBindingType::UniformBuffer; + + std::shared_ptr renderPipelineLayout = m_renderDevice.InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); + + Nz::RenderPipelineInfo pipelineInfo; + pipelineInfo.pipelineLayout = renderPipelineLayout; + pipelineInfo.shaderModules.emplace_back(shader); + + pipelineInfo.depthBuffer = false; + pipelineInfo.faceCulling = Nz::FaceCulling::None; + pipelineInfo.scissorTest = true; + + pipelineInfo.blending = true; + pipelineInfo.blend.modeAlpha = Nz::BlendEquation::Add; + pipelineInfo.blend.srcColor = Nz::BlendFunc::SrcAlpha; + pipelineInfo.blend.dstColor = Nz::BlendFunc::InvSrcAlpha; + pipelineInfo.blend.srcAlpha = Nz::BlendFunc::One; + pipelineInfo.blend.dstAlpha = Nz::BlendFunc::Zero; + + auto& pipelineVertexBuffer = pipelineInfo.vertexBuffers.emplace_back(); + pipelineVertexBuffer.binding = 0; + pipelineVertexBuffer.declaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color_UV); + + m_untexturedPipeline.pipeline = m_renderDevice.InstantiateRenderPipeline(pipelineInfo); + + m_untexturedPipeline.uboShaderBinding = renderPipelineLayout->AllocateShaderBinding(0); + m_untexturedPipeline.uboShaderBinding->Update({ + { + 0, + Nz::ShaderBinding::UniformBufferBinding { + m_uboBuffer.get(), 0, sizeof(ImguiUbo) + } + } + }); + return true; + } +} \ No newline at end of file diff --git a/src/NazaraImgui/ImguiPipelinePass.cpp b/src/NazaraImgui/ImguiPipelinePass.cpp new file mode 100644 index 0000000..43b5404 --- /dev/null +++ b/src/NazaraImgui/ImguiPipelinePass.cpp @@ -0,0 +1,47 @@ +#include + +#include +#include + +#include + +namespace Nz +{ + ImguiPipelinePass::ImguiPipelinePass(PassData& passData, std::string passName, const ParameterList& /*parameters*/) : + FramePipelinePass({}) + , m_passName(std::move(passName)) + { + } + + void ImguiPipelinePass::Prepare(FrameData& frameData) + { + ImguiDrawer& imguiDrawer = Nz::Imgui::Instance()->GetImguiDrawer(); + imguiDrawer.Prepare(frameData.renderFrame); + } + + FramePass& ImguiPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, const PassInputOuputs& inputOuputs) + { + if (inputOuputs.inputCount != 1) + throw std::runtime_error("one input expected"); + + if (inputOuputs.outputCount != 1) + throw std::runtime_error("one output expected"); + + FramePass& imguiPass = frameGraph.AddPass("Imgui pass"); + imguiPass.AddInput(inputOuputs.inputAttachments[0]); + imguiPass.AddOutput(inputOuputs.outputAttachments[0]); + + imguiPass.SetExecutionCallback([&] + { + return FramePassExecution::UpdateAndExecute; + }); + + imguiPass.SetCommandCallback([this](CommandBufferBuilder& builder, const FramePassEnvironment& /*env*/) + { + ImguiDrawer& imguiDrawer = Nz::Imgui::Instance()->GetImguiDrawer(); + imguiDrawer.Draw(builder); + }); + + return imguiPass; + } +} \ No newline at end of file diff --git a/src/NazaraImgui/NazaraImgui.cpp b/src/NazaraImgui/NazaraImgui.cpp index ef29dd8..a3c8d5d 100644 --- a/src/NazaraImgui/NazaraImgui.cpp +++ b/src/NazaraImgui/NazaraImgui.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -36,14 +38,6 @@ static_assert(sizeof(void*) <= sizeof(ImTextureID), #define NazaraImguiDebugSuffix "" #endif -const char shaderSource_Textured[] = -#include "Textured.nzsl.h" -; - -const char shaderSource_Untextured[] = -#include "Untextured.nzsl.h" -; - namespace { inline Nz::SystemCursor ToNz(ImGuiMouseCursor type) @@ -65,34 +59,11 @@ namespace } } - - inline Nz::Vector2f ToNzVec2(ImVec2 v) - { - return { v.x, v.y }; - } - - inline Nz::Vector3f ToNzVec3(ImVec2 v) - { - return { v.x, v.y, 0.f }; - } - - inline Nz::Color ToNzColor(ImU32 color) - { - auto c = ImGui::ColorConvertU32ToFloat4(color); - return { c.x, c.y, c.z, c.w }; - } - } namespace Nz { - struct ImguiUbo - { - float screenWidth; - float screenHeight; - }; - Imgui* Imgui::s_instance = nullptr; Imgui::Imgui(Config /*config*/) @@ -100,6 +71,7 @@ namespace Nz , m_bMouseMoved(false) , m_bWindowHasFocus(false) , m_currentContext(nullptr) + , m_imguiDrawer(*Nz::Graphics::Instance()->GetRenderDevice()) { ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); @@ -108,14 +80,6 @@ namespace Nz Imgui::~Imgui() { - m_untexturedPipeline.uboShaderBinding.reset(); - m_untexturedPipeline.pipeline.reset(); - - m_texturedPipeline.uboShaderBinding.reset(); - m_texturedPipeline.textureShaderBindings.clear(); - m_texturedPipeline.textureSampler.reset(); - m_texturedPipeline.pipeline.reset(); - ImGui::GetIO().Fonts->TexID = nullptr; ImGui::DestroyContext(); @@ -139,11 +103,8 @@ namespace Nz // init rendering io.DisplaySize = ImVec2(window.GetSize().x * 1.f, window.GetSize().y * 1.f); - if (!LoadTexturedPipeline()) - return false; - - if (!LoadUntexturedPipeline()) - return false; + auto registry = Nz::Graphics::Instance()->GetFramePipelinePassRegistry(); + registry.RegisterPass("Imgui", { "Input" }, { "Output" }); if (bLoadDefaultFont) { @@ -320,7 +281,23 @@ namespace Nz handler->OnRenderImgui(); ImGui::Render(); - RenderDrawLists(renderTarget, frame, ImGui::GetDrawData()); + + m_imguiDrawer.Prepare(frame); + + frame.Execute([this, renderTarget, &frame](Nz::CommandBufferBuilder& builder) { + + ImGuiIO& io = ImGui::GetIO(); + int fb_width = static_cast(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = static_cast(io.DisplaySize.y * io.DisplayFramebufferScale.y); + Nz::Recti renderRect(0, 0, fb_width, fb_height); + + builder.BeginDebugRegion("ImGui", Nz::Color::Green()); + builder.BeginRenderPass(renderTarget->GetFramebuffer(frame.GetFramebufferIndex()), renderTarget->GetRenderPass(), renderRect); + m_imguiDrawer.Draw(builder); + builder.EndRenderPass(); + builder.EndDebugRegion(); + + }, Nz::QueueType::Graphics); } void Imgui::AddHandler(ImguiHandler* handler) @@ -406,286 +383,6 @@ namespace Nz } } } - - bool Imgui::LoadTexturedPipeline() - { - auto renderDevice = Nz::Graphics::Instance()->GetRenderDevice(); - - nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(shaderSource_Textured, sizeof(shaderSource_Textured))); - if (!shaderModule) - { - std::cout << "Failed to parse shader module" << std::endl; - return false; - } - - nzsl::ShaderWriter::States states; - states.optimize = true; - - auto shader = renderDevice->InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, states); - if (!shader) - { - std::cout << "Failed to instantiate shader" << std::endl; - return false; - } - - - m_texturedPipeline.textureSampler = renderDevice->InstantiateTextureSampler({}); - - Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; - - auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back(); - uboBinding.setIndex = 0; - uboBinding.bindingIndex = 0; - uboBinding.shaderStageFlags = nzsl::ShaderStageType::Vertex; - uboBinding.type = Nz::ShaderBindingType::UniformBuffer; - - auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back(); - textureBinding.setIndex = 1; - textureBinding.bindingIndex = 0; - textureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; - textureBinding.type = Nz::ShaderBindingType::Texture; - - std::shared_ptr renderPipelineLayout = renderDevice->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); - - Nz::RenderPipelineInfo pipelineInfo; - pipelineInfo.pipelineLayout = renderPipelineLayout; - pipelineInfo.shaderModules.emplace_back(shader); - - pipelineInfo.depthBuffer = false; - pipelineInfo.faceCulling = Nz::FaceCulling::None; - pipelineInfo.scissorTest = true; - - pipelineInfo.blending = true; - pipelineInfo.blend.modeAlpha = Nz::BlendEquation::Add; - pipelineInfo.blend.srcColor = Nz::BlendFunc::SrcAlpha; - pipelineInfo.blend.dstColor = Nz::BlendFunc::InvSrcAlpha; - pipelineInfo.blend.srcAlpha = Nz::BlendFunc::One; - pipelineInfo.blend.dstAlpha = Nz::BlendFunc::Zero; - - auto& pipelineVertexBuffer = pipelineInfo.vertexBuffers.emplace_back(); - pipelineVertexBuffer.binding = 0; - pipelineVertexBuffer.declaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color_UV); - - m_texturedPipeline.pipeline = renderDevice->InstantiateRenderPipeline(pipelineInfo); - - m_uboBuffer = renderDevice->InstantiateBuffer(Nz::BufferType::Uniform, sizeof(ImguiUbo), Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic); - - m_texturedPipeline.uboShaderBinding = renderPipelineLayout->AllocateShaderBinding(0); - m_texturedPipeline.uboShaderBinding->Update({ - { - 0, - Nz::ShaderBinding::UniformBufferBinding { - m_uboBuffer.get(), 0, sizeof(ImguiUbo) - } - } - }); - - return true; - } - - bool Imgui::LoadUntexturedPipeline() - { - auto renderDevice = Nz::Graphics::Instance()->GetRenderDevice(); - - nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(shaderSource_Untextured, sizeof(shaderSource_Untextured))); - if (!shaderModule) - { - std::cout << "Failed to parse shader module" << std::endl; - return false; - } - - nzsl::ShaderWriter::States states; - states.optimize = true; - - auto shader = renderDevice->InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, states); - if (!shader) - { - std::cout << "Failed to instantiate shader" << std::endl; - return false; - } - - Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; - - auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back(); - uboBinding.setIndex = 0; - uboBinding.bindingIndex = 0; - uboBinding.shaderStageFlags = nzsl::ShaderStageType::Vertex; - uboBinding.type = Nz::ShaderBindingType::UniformBuffer; - - std::shared_ptr renderPipelineLayout = renderDevice->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); - - Nz::RenderPipelineInfo pipelineInfo; - pipelineInfo.pipelineLayout = renderPipelineLayout; - pipelineInfo.shaderModules.emplace_back(shader); - - pipelineInfo.depthBuffer = false; - pipelineInfo.faceCulling = Nz::FaceCulling::None; - pipelineInfo.scissorTest = true; - - pipelineInfo.blending = true; - pipelineInfo.blend.modeAlpha = Nz::BlendEquation::Add; - pipelineInfo.blend.srcColor = Nz::BlendFunc::SrcAlpha; - pipelineInfo.blend.dstColor = Nz::BlendFunc::InvSrcAlpha; - pipelineInfo.blend.srcAlpha = Nz::BlendFunc::One; - pipelineInfo.blend.dstAlpha = Nz::BlendFunc::Zero; - - auto& pipelineVertexBuffer = pipelineInfo.vertexBuffers.emplace_back(); - pipelineVertexBuffer.binding = 0; - pipelineVertexBuffer.declaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color_UV); - - m_untexturedPipeline.pipeline = renderDevice->InstantiateRenderPipeline(pipelineInfo); - - m_untexturedPipeline.uboShaderBinding = renderPipelineLayout->AllocateShaderBinding(0); - m_untexturedPipeline.uboShaderBinding->Update({ - { - 0, - Nz::ShaderBinding::UniformBufferBinding { - m_uboBuffer.get(), 0, sizeof(ImguiUbo) - } - } - }); - return true; - } - - // Rendering callback - void Imgui::RenderDrawLists(Nz::RenderTarget* renderTarget, Nz::RenderFrame& frame, ImDrawData* drawData) - { - if (drawData->CmdListsCount == 0) - return; - - ImGuiIO& io = ImGui::GetIO(); - assert(io.Fonts->TexID != (ImTextureID)NULL); // You forgot to create and set font texture - - // scale stuff (needed for proper handling of window resize) - int fb_width = static_cast(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = static_cast(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - - ImguiUbo ubo{ fb_width / 2.f, fb_height / 2.f }; - auto& allocation = frame.GetUploadPool().Allocate(sizeof(ImguiUbo)); - - std::memcpy(allocation.mappedPtr, &ubo, sizeof(ImguiUbo)); - - frame.Execute([&](Nz::CommandBufferBuilder& builder) - { - builder.BeginDebugRegion("UBO Update", Nz::Color::Yellow()); - { - builder.PreTransferBarrier(); - builder.CopyBuffer(allocation, m_uboBuffer.get()); - builder.PostTransferBarrier(); - } - builder.EndDebugRegion(); - }, Nz::QueueType::Transfer); - - drawData->ScaleClipRects(io.DisplayFramebufferScale); - - auto renderDevice = Nz::Graphics::Instance()->GetRenderDevice(); - - struct DrawCall - { - size_t vertex_offset, indice_offset; - std::vector cmdBuffer; - }; - - std::vector drawCalls; - - // first pass over cmd lists to prepare buffers - std::vector vertices; - std::vector indices; - for (int n = 0; n < drawData->CmdListsCount; ++n) { - const ImDrawList* cmd_list = drawData->CmdLists[n]; - - DrawCall drawCall; - drawCall.vertex_offset = vertices.size(); - drawCall.indice_offset = indices.size(); - - vertices.reserve(vertices.size() + cmd_list->VtxBuffer.size()); - for (auto& vertex : cmd_list->VtxBuffer) - vertices.push_back({ ToNzVec3(vertex.pos), ToNzColor(vertex.col), ToNzVec2(vertex.uv) }); - - indices.reserve(indices.size() + cmd_list->IdxBuffer.size()); - for (auto indice : cmd_list->IdxBuffer) - indices.push_back(uint16_t(drawCall.vertex_offset + indice)); - - for (auto& cmd : cmd_list->CmdBuffer) - drawCall.cmdBuffer.push_back(cmd); - - drawCalls.push_back(std::move(drawCall)); - } - - - // now that we have macro buffers, allocate them on gpu - size_t size = vertices.size() * sizeof(Nz::VertexStruct_XYZ_Color_UV); - auto vertexBuffer = renderDevice->InstantiateBuffer(Nz::BufferType::Vertex, size, Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic, vertices.data()); - size = indices.size() * sizeof(uint16_t); - auto indexBuffer = renderDevice->InstantiateBuffer(Nz::BufferType::Index, size, Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic, indices.data()); - - // freeing memory now, no need to keep buffers on CPU - vertices.clear(); - indices.clear(); - - frame.Execute([this, renderTarget, &frame, fb_width, fb_height, drawCalls, vertexBuffer, indexBuffer](Nz::CommandBufferBuilder& builder) { - builder.BeginDebugRegion("ImGui", Nz::Color::Green()); - { - Nz::Recti renderRect(0, 0, fb_width, fb_height); - - builder.BeginRenderPass(renderTarget->GetFramebuffer(frame.GetFramebufferIndex()), renderTarget->GetRenderPass(), renderRect); - { - builder.SetViewport(Nz::Recti{ 0, 0, fb_width, fb_height }); - builder.BindIndexBuffer(*indexBuffer, Nz::IndexType::U16); - builder.BindVertexBuffer(0, *vertexBuffer); - - for (auto& drawCall : drawCalls) - { - Nz::UInt64 indexOffset = drawCall.indice_offset; - for (auto& cmd : drawCall.cmdBuffer) - { - if (!cmd.UserCallback) - { - auto rect = cmd.ClipRect; - auto count = cmd.ElemCount; - auto texture = static_cast(cmd.GetTexID()); - - if (nullptr != texture) - { - if (std::end(m_texturedPipeline.textureShaderBindings) == m_texturedPipeline.textureShaderBindings.find(texture)) - { - auto binding = m_texturedPipeline.pipeline->GetPipelineInfo().pipelineLayout->AllocateShaderBinding(1); - binding->Update({ - { - 0, - Nz::ShaderBinding::SampledTextureBinding { - texture, m_texturedPipeline.textureSampler.get() - } - } - }); - m_texturedPipeline.textureShaderBindings[texture] = std::move(binding); - } - - builder.BindRenderPipeline(*m_texturedPipeline.pipeline); - builder.BindRenderShaderBinding(0, *m_texturedPipeline.uboShaderBinding); - builder.BindRenderShaderBinding(1, *m_texturedPipeline.textureShaderBindings[texture]); - } - else - { - builder.BindRenderPipeline(*m_untexturedPipeline.pipeline); - builder.BindRenderShaderBinding(0, *m_untexturedPipeline.uboShaderBinding); - } - - builder.SetScissor(Nz::Recti{ int(rect.x), int(rect.y), int(rect.z - rect.x), int(rect.w - rect.y) });// Nz::Recti{ int(rect.x), int(fb_height - rect.w), int(rect.z - rect.x), int(rect.w - rect.y) }); - - builder.DrawIndexed(count, 1, Nz::UInt32(indexOffset)); - } - indexOffset += cmd.ElemCount; - } - } - } - builder.EndRenderPass(); - } - builder.EndDebugRegion(); - }, Nz::QueueType::Graphics); - } } namespace