Add ImguiPipelinePass
This commit is contained in:
parent
c5bffa9c98
commit
741b593033
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue