Add ImguiPipelinePass

This commit is contained in:
SweetId 2023-11-16 17:23:55 +05:30
parent c5bffa9c98
commit 741b593033
6 changed files with 485 additions and 347 deletions

View File

@ -0,0 +1,65 @@
#pragma once
#include <NazaraImgui/Config.hpp>
#include <Nazara/Renderer/ShaderBinding.hpp>
#include <imgui.h>
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<ImDrawCmd> cmdBuffer;
};
std::vector<DrawCall> m_drawCalls;
struct
{
std::shared_ptr<RenderPipeline> pipeline;
std::unordered_map<Texture*, ShaderBindingPtr> textureShaderBindings;
Nz::ShaderBindingPtr uboShaderBinding;
std::shared_ptr<TextureSampler> textureSampler;
} m_texturedPipeline;
struct
{
std::shared_ptr<RenderPipeline> pipeline;
Nz::ShaderBindingPtr uboShaderBinding;
} m_untexturedPipeline;
std::shared_ptr<RenderBuffer> m_vertexBuffer;
std::shared_ptr<RenderBuffer> m_indexBuffer;
std::shared_ptr<RenderBuffer> m_uboBuffer;
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <NazaraImgui/Config.hpp>
#include <Nazara/Core/ParameterList.hpp>
#include <Nazara/Graphics/FramePipelinePass.hpp>
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;
};
}

View File

@ -4,6 +4,7 @@
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Math/Rect.hpp>
#include <NazaraImgui/Config.hpp>
#include <NazaraImgui/ImguiDrawer.hpp>
#include <imgui.h>
#include <unordered_set>
@ -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<Nz::Cursor> 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<Nz::RenderPipeline> pipeline;
std::unordered_map<Nz::Texture*, Nz::ShaderBindingPtr> textureShaderBindings;
Nz::ShaderBindingPtr uboShaderBinding;
std::shared_ptr<Nz::TextureSampler> textureSampler;
} m_texturedPipeline;
struct
{
std::shared_ptr<Nz::RenderPipeline> pipeline;
Nz::ShaderBindingPtr uboShaderBinding;
} m_untexturedPipeline;
std::shared_ptr<Nz::RenderBuffer> m_uboBuffer;
ImguiDrawer m_imguiDrawer;
std::shared_ptr<Nz::Texture> m_fontTexture;
std::unordered_set<ImguiHandler*> m_handlers;
static Imgui* s_instance;

View File

@ -0,0 +1,319 @@
#include <NazaraImgui/ImguiDrawer.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <NZSL/Parser.hpp>
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<int>(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = static_cast<int>(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<Nz::VertexStruct_XYZ_Color_UV> vertices;
std::vector<uint16_t> 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<int>(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = static_cast<int>(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<Nz::Texture*>(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<Nz::RenderPipelineLayout> 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<Nz::RenderPipelineLayout> 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;
}
}

View File

@ -0,0 +1,47 @@
#include <NazaraImgui/ImguiPipelinePass.hpp>
#include <NazaraImgui/NazaraImgui.hpp>
#include <NazaraImgui/ImguiDrawer.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
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;
}
}

View File

@ -1,4 +1,6 @@
#include <NazaraImgui/NazaraImgui.hpp>
#include <NazaraImgui/ImguiPipelinePass.hpp>
#include <NazaraImgui/ImguiDrawer.hpp>
#include <Nazara/Core/DynLib.hpp>
#include <Nazara/Core/Log.hpp>
@ -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<ImguiPipelinePass>("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<int>(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = static_cast<int>(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<Nz::RenderPipelineLayout> 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<Nz::RenderPipelineLayout> 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<int>(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = static_cast<int>(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<ImDrawCmd> cmdBuffer;
};
std::vector<DrawCall> drawCalls;
// first pass over cmd lists to prepare buffers
std::vector<Nz::VertexStruct_XYZ_Color_UV> vertices;
std::vector<uint16_t> 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<Nz::Texture*>(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