From 105f32ef35b68f99113108fe38afc88cc0ef9631 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 16 Feb 2025 10:14:57 -0800 Subject: [PATCH] Add `Graphics::RenderInstance`, bind instance SSBO during draw --- App/Src/Main.cpp | 27 +++++++++++++--- Assets/Shaders/Default.vert | Bin 1372 -> 2352 bytes Lib/Include/Tkge/Graphics/RenderInstance.hpp | 12 +++++++ Lib/Include/Tkge/Graphics/Renderer.hpp | 13 ++++++-- Lib/Src/Detail/PipelinePool.hpp | 6 ++-- Lib/Src/Glsl/Default.vert | 19 +++++++++-- Lib/Src/Graphics/Renderer.cpp | 32 ++++++++++++++----- 7 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 Lib/Include/Tkge/Graphics/RenderInstance.hpp diff --git a/App/Src/Main.cpp b/App/Src/Main.cpp index 1df0c09..abdd9e6 100644 --- a/App/Src/Main.cpp +++ b/App/Src/Main.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -51,10 +53,10 @@ namespace if (!shader.Load(renderDevice.get_device(), vertexSpirV, fragmentSpirV)) { throw std::runtime_error{"Failed to load shaders"}; } static constexpr auto Vertices = std::array{ - Tkge::Graphics::Vertex{.position = {-200.0f, -200.0f}, .colour = kvf::red_v.to_vec4()}, - Tkge::Graphics::Vertex{.position = {200.0f, -200.0f}, .colour = kvf::green_v.to_vec4()}, - Tkge::Graphics::Vertex{.position = {200.0f, 200.0f}, .colour = kvf::blue_v.to_vec4()}, - Tkge::Graphics::Vertex{.position = {-200.0f, 200.0f}, .colour = kvf::yellow_v.to_vec4()}, + Tkge::Graphics::Vertex{.position = {-200.0f, -200.0f}}, + Tkge::Graphics::Vertex{.position = {200.0f, -200.0f}}, + Tkge::Graphics::Vertex{.position = {200.0f, 200.0f}}, + Tkge::Graphics::Vertex{.position = {-200.0f, 200.0f}}, }; static constexpr auto Indices = std::array{ @@ -66,13 +68,28 @@ namespace .indices = Indices, }; + auto instances = std::array{}; + instances[0].transform.position.x = -250.0f; + instances[0].tint = kvf::cyan_v; + instances[1].transform.position.x = 250.0f; + instances[1].tint = kvf::yellow_v; + auto wireframe = false; auto lineWidth = 3.0f; + auto deltaTime = kvf::DeltaTime{}; + auto elapsed = kvf::Seconds{}; + while (engine.IsRunning()) { engine.NextFrame(); + const auto dt = deltaTime.tick(); + elapsed += dt; + + instances[0].tint.w = kvf::Color::to_u8((0.5f * std::sin(elapsed.count())) + 0.5f); + instances[1].tint.w = kvf::Color::to_u8((0.5f * std::sin(-elapsed.count())) + 0.5f); + if (ImGui::Begin("Misc")) { ImGui::Checkbox("wireframe", &wireframe); @@ -85,7 +102,7 @@ namespace renderer.BindShader(shader); renderer.SetLineWidth(lineWidth); renderer.SetWireframe(wireframe); - renderer.Draw(Primitive); + renderer.Draw(Primitive, instances); } engine.Present(); diff --git a/Assets/Shaders/Default.vert b/Assets/Shaders/Default.vert index 40c19ef1e86a87f25f1ab8a94468e2f198202e93..59106298db39ebcf3fa8f47fbaf6a9c31f383144 100644 GIT binary patch literal 2352 zcmZ9MT~8BH5QZ1p1w;W6`A`A3AcCS56%a+f1X4(vP($RlY1)n1Wa*N23+k0O{s8}y zzsegE-)Hxnvf+^F%)Ik;&de;i-uZsV>2Yp5L(ZX-o>8Y)h$E~o;id9vx!8^y#nrVH zJcgZI;)u_Pyf}H$c`axn-xn>2iXs9&hz>+K{q>6fQDEK~bV}uF>ExtTKCIR+E~@SL zd+1lgpy^i|LCdel!KIJiK*ld<*27LC>)^^GTibP9=ay<5H`{ToSyu)}uShZE^Qhs6 z8H_i?K{LiiEUyVNv0!(E-OLBZ$>syQnQvH_jcrTr(;pbe)&Y0bo4W~$+@JLDYgo0R z$IXWS4Kw%g#IK@O*r-H=3dtw^u61=c>3!6R_oFcCw2Tk4s2Rl7JvOAPM#XQP`K>t3 z&;7tT*rIk22T@aKe}ePA1Xs^agCC~9sg2pnwjTx;2Z3q7jbmf5b;7V~9bEH^14_DZcJgu(fPWqa~=1mTZ@g}fcQAxRm z#QQ|lEU6~*;{MdJvztnEx2xNn_%lcB4r{w!ap;?nOSC zSAjqs-oyN|?`MX*k2+)eg%?ztyC6?UViulgQt{M7=Z;hQ9S{dYpVcqlzQsK0gT@Yy zoxQO38RcfyV6&2$Ejn={;?xJ*G&-{c8<##R?umLt%SOK^nM3@TI5zxPw0CbBFkslIc15 ziAQJmz_7m(CzrLOvx{K#V@=!>ambIpF2X)7{hc^>0GpM>u96E38$_M=;?%^>J<$0d z0mh8jF?xjWM{(W{hA%q%WqY!1Wc+PU4kUA@b!{?!>{uf~TtnA$7fx*NS>n5{*B$;}|ZtFJa_z}Y$rbXo8 z?!<7P$0B0b6JpTuvpEZrZBFh&?Ku%R`n>p45qm-%X7S8mVqNUH!Khu)ZMe^}2w!ZA zq9xI9bJrJ=se=#q^2EWYN$hJ8yJO$+MzVc}^?54{{FlkODw#NNa|1vwr+8X-Pr4_v_xvPzR|HNTc79*PzS%jTX2th3 ziBg*-(b!ItQDLtW@>XkkHXEh$aXleWM!jxe0#`eq7iW2z&!?(K4qD=h3DnbV;V)@4 zNT$PNS|r~D#g;sKV)@J#HqXSaxO{qbyv>RW`$OtDQ^x2^v9r`pKG<0iXQN~hV^YNC zX}X97ngz3Ap>J1yXp^ttmKZSWDlUctdGhxiQ|2?%SPG$tG2i&|+Jw4`*%N1h=^c#z zP`UJu-cLHoo<$DJbL7I|J;xb1`tcllIKBaQqr1Gj>`72n%6#9G9-FJn9iHovsD)no z>^k%xAJ#Cv)cd1%Fm<=}kMBwg+Su=E*OKqZ_H;k>!D#O2e^s7b_}3L?hH%7r-Aghb z2m9IVD8BCU^r!W-!W-Ii=0JE;o_cU??U=dOKn$bKmOM3yqXWzvfuqj0d`HH;)B)d> zvG#>``DlJ1g9oGW zkqmvGbp|bm6(_ggdKHJJfvQsXv5Z_|9T`3R(jCN(WYi%CExem!8Q%ykXa$q^SDn6+ F{R6$vVUYj; diff --git a/Lib/Include/Tkge/Graphics/RenderInstance.hpp b/Lib/Include/Tkge/Graphics/RenderInstance.hpp new file mode 100644 index 0000000..88152ad --- /dev/null +++ b/Lib/Include/Tkge/Graphics/RenderInstance.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +namespace Tkge::Graphics +{ + struct RenderInstance + { + Transform transform{}; + kvf::Color tint{kvf::white_v}; + }; +} // namespace Tkge::Graphics diff --git a/Lib/Include/Tkge/Graphics/Renderer.hpp b/Lib/Include/Tkge/Graphics/Renderer.hpp index 7419527..5a1841d 100644 --- a/Lib/Include/Tkge/Graphics/Renderer.hpp +++ b/Lib/Include/Tkge/Graphics/Renderer.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -26,15 +27,22 @@ namespace Tkge::Graphics void SetLineWidth(float width); void SetWireframe(bool wireframe); - void Draw(const Primitive& primitive); + void Draw(const Primitive& primitive, std::span instances); explicit operator bool() const { return IsRendering(); } Transform view{}; private: + struct Std430Instance + { + glm::mat4 model; + glm::vec4 tint; + }; + + void UpdateInstances(std::span instances); [[nodiscard]] bool WriteSets() const; - void BindVboAndDraw(const Primitive& primitive) const; + void BindVboAndDraw(const Primitive& primitive, std::uint32_t instances) const; kvf::RenderPass* _renderPass{}; IResourcePool* _resourcePool{}; @@ -45,5 +53,6 @@ namespace Tkge::Graphics vk::Pipeline _pipeline{}; vk::PolygonMode _polygonMode{vk::PolygonMode::eFill}; float _lineWidth{1.0f}; + std::vector _instances{}; }; } // namespace Tkge::Graphics diff --git a/Lib/Src/Detail/PipelinePool.hpp b/Lib/Src/Detail/PipelinePool.hpp index 98ec78c..6908fff 100644 --- a/Lib/Src/Detail/PipelinePool.hpp +++ b/Lib/Src/Detail/PipelinePool.hpp @@ -75,10 +75,12 @@ namespace Tkge::Detail { static constexpr auto StageFlags = vk::ShaderStageFlagBits::eAllGraphics; // set 0: builtin - auto set0 = std::array{}; + auto set0 = std::array{}; // set 0, binding 0: view set0[0].setBinding(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eUniformBuffer).setStageFlags(StageFlags); - // TODO: instances, texture bindings + // set 0, binding 1: instances + set0[1].setBinding(1).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eStorageBuffer).setStageFlags(StageFlags); + // TODO: texture bindings // TODO: set 1: user data diff --git a/Lib/Src/Glsl/Default.vert b/Lib/Src/Glsl/Default.vert index c98f64d..fce546a 100644 --- a/Lib/Src/Glsl/Default.vert +++ b/Lib/Src/Glsl/Default.vert @@ -1,5 +1,11 @@ #version 450 core +struct Instance +{ + mat4 model; + vec4 tint; +}; + layout (location = 0) in vec2 aPos; layout (location = 1) in vec4 aColour; layout (location = 2) in vec2 aUv; @@ -9,11 +15,20 @@ layout (set = 0, binding = 0) uniform View mat4 matVP; }; +layout (set = 0, binding = 1) readonly buffer Instances +{ + Instance instances[]; +}; + layout (location = 0) out vec4 outColour; void main() { - outColour = aColour; + const Instance instance = instances[gl_InstanceIndex]; + + const vec4 worldPos = instance.model * vec4(aPos, 0.0, 1.0); + + outColour = aColour * instance.tint; - gl_Position = matVP * vec4(aPos, 0.0, 1.0); + gl_Position = matVP * worldPos; } diff --git a/Lib/Src/Graphics/Renderer.cpp b/Lib/Src/Graphics/Renderer.cpp index f2e0873..08f11e3 100644 --- a/Lib/Src/Graphics/Renderer.cpp +++ b/Lib/Src/Graphics/Renderer.cpp @@ -29,8 +29,6 @@ namespace Tkge::Graphics return true; } - void Renderer::SetWireframe(const bool wireframe) { _polygonMode = wireframe ? vk::PolygonMode::eLine : vk::PolygonMode::eFill; } - void Renderer::SetLineWidth(const float width) { if (_renderPass == nullptr) { return; } @@ -38,9 +36,11 @@ namespace Tkge::Graphics _lineWidth = std::clamp(width, limits[0], limits[1]); } - void Renderer::Draw(const Primitive& primitive) + void Renderer::SetWireframe(const bool wireframe) { _polygonMode = wireframe ? vk::PolygonMode::eLine : vk::PolygonMode::eFill; } + + void Renderer::Draw(const Primitive& primitive, std::span instances) { - if (!IsRendering() || _shader == nullptr || primitive.vertices.empty()) { return; } + if (!IsRendering() || _shader == nullptr || primitive.vertices.empty() || instances.empty()) { return; } const auto fixedState = PipelineFixedState{ .colourFormat = _renderPass->get_color_format(), @@ -56,11 +56,22 @@ namespace Tkge::Graphics _renderPass->bind_pipeline(_pipeline); } + UpdateInstances(instances); if (!WriteSets()) { return; } _renderPass->get_command_buffer().setViewport(0, _viewport); - BindVboAndDraw(primitive); + BindVboAndDraw(primitive, std::uint32_t(instances.size())); + } + + void Renderer::UpdateInstances(std::span instances) + { + _instances.clear(); + _instances.reserve(instances.size()); + for (const auto& instance : instances) + { + _instances.push_back(Std430Instance{.model = instance.transform.ToModel(), .tint = instance.tint.to_linear()}); + } } bool Renderer::WriteSets() const @@ -91,6 +102,11 @@ namespace Tkge::Graphics kvf::util::overwrite(ubo00, matVP); pushBufferWrite(descriptorSets[0], 0, ubo00, vk::DescriptorType::eUniformBuffer); + const auto instanceSpan = std::span{_instances}; + auto& ssbo01 = _resourcePool->AllocateBuffer(vk::BufferUsageFlagBits::eStorageBuffer, instanceSpan.size_bytes()); + kvf::util::overwrite(ssbo01, instanceSpan); + pushBufferWrite(descriptorSets[0], 1, ssbo01, vk::DescriptorType::eStorageBuffer); + const auto writeSpan = std::span{descriptorWrites.data(), descriptorWrites.size()}; renderDevice.get_device().updateDescriptorSets(writeSpan, {}); @@ -100,7 +116,7 @@ namespace Tkge::Graphics return true; } - void Renderer::BindVboAndDraw(const Primitive& primitive) const + void Renderer::BindVboAndDraw(const Primitive& primitive, const std::uint32_t instances) const { const auto vertSize = primitive.vertices.size_bytes(); const auto vboSize = vertSize + primitive.indices.size_bytes(); @@ -112,11 +128,11 @@ namespace Tkge::Graphics commandBuffer.setLineWidth(_lineWidth); commandBuffer.bindVertexBuffers(0, vertexBuffer.get_buffer(), vk::DeviceSize{}); - if (primitive.indices.empty()) { commandBuffer.draw(std::uint32_t(primitive.vertices.size()), 1, 0, 0); } + if (primitive.indices.empty()) { commandBuffer.draw(std::uint32_t(primitive.vertices.size()), instances, 0, 0); } else { commandBuffer.bindIndexBuffer(vertexBuffer.get_buffer(), vertSize, vk::IndexType::eUint32); - commandBuffer.drawIndexed(std::uint32_t(primitive.indices.size()), 1, 0, 0, 0); + commandBuffer.drawIndexed(std::uint32_t(primitive.indices.size()), instances, 0, 0, 0); } } } // namespace Tkge::Graphics