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 40c19ef..5910629 100644 Binary files a/Assets/Shaders/Default.vert and b/Assets/Shaders/Default.vert differ 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