diff --git a/src/calculator/renderer/VulkanBuffer.cpp b/src/calculator/renderer/VulkanBuffer.cpp index 6378336..95ce5ca 100644 --- a/src/calculator/renderer/VulkanBuffer.cpp +++ b/src/calculator/renderer/VulkanBuffer.cpp @@ -7,13 +7,14 @@ using namespace ui; -VulkanBuffer::VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t size) : window_(window) +VulkanBuffer::VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t element_size, std::size_t count) : + window_(window), type_(type), element_size_(element_size), count_(count) { this->dev_ = this->window_->vulkanInstance()->deviceFunctions(this->window_->device()); VkBufferCreateInfo buffer_info{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = size, + .size = this->GetSize(), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; @@ -37,9 +38,12 @@ VulkanBuffer::VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t s throw std::runtime_error("failed to create camera buffer"); } + VkMemoryRequirements memory_requirements; + this->dev_->vkGetBufferMemoryRequirements(this->window_->device(), this->buffer_, &memory_requirements); + VkMemoryAllocateInfo memory_allocate_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = buffer_info.size, + .allocationSize = memory_requirements.size, .memoryTypeIndex = this->window_->hostVisibleMemoryIndex(), }; @@ -52,6 +56,38 @@ VulkanBuffer::VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t s this->dev_->vkMapMemory(this->window_->device(), this->memory_, 0, buffer_info.size, 0, (void **)&this->data_); } +VkBuffer VulkanBuffer::GetNativeHandle() const noexcept +{ + return this->buffer_; +} + +std::size_t VulkanBuffer::GetSize() const noexcept +{ + return this->element_size_ * this->count_; +} + +std::size_t VulkanBuffer::GetElementSize() const noexcept +{ + return this->element_size_; +} + +std::size_t VulkanBuffer::GetCount() const noexcept +{ + return this->count_; +} + +void VulkanBuffer::Bind(VkCommandBuffer cmd) +{ + if (this->type_ != BufferType::Vertex) + { + throw std::runtime_error("can only bind vertex buffers"); + } + + std::array buffers = {this->buffer_}; + constexpr std::array offsets = {0}; + this->dev_->vkCmdBindVertexBuffers(cmd, 0, 1, buffers.data(), offsets.data()); +} + VulkanBuffer::~VulkanBuffer() { this->dev_->vkUnmapMemory(this->window_->device(), this->memory_); diff --git a/src/calculator/renderer/VulkanBuffer.h b/src/calculator/renderer/VulkanBuffer.h index 0a4575a..2219ec2 100644 --- a/src/calculator/renderer/VulkanBuffer.h +++ b/src/calculator/renderer/VulkanBuffer.h @@ -23,23 +23,23 @@ namespace ui VkBuffer buffer_ = nullptr; VkDeviceMemory memory_ = nullptr; - std::size_t size_ = 0; - void *data_ = nullptr; + BufferType type_; + std::size_t element_size_ = 0; + std::size_t count_ = 0; - protected: - VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t size); + void *data_ = nullptr; public: template - static VulkanBuffer Create(QVulkanWindow *window, BufferType type, std::size_t count = 1) + static std::unique_ptr Create(QVulkanWindow *window, BufferType type, std::size_t count = 1) { - return {window, type, sizeof(T) * count}; + return std::make_unique(window, type, sizeof(T), count); } template void Write(T const *value, std::size_t count = 1) { - if (this->size_ != sizeof(T) * count) + if (this->GetSize() != sizeof(T) * count) { throw std::runtime_error("attempting to write to buffer of incorrect size!"); } @@ -47,9 +47,17 @@ namespace ui memcpy(this->data_, &value, sizeof(T) * count); } + [[nodiscard]] VkBuffer GetNativeHandle() const noexcept; + [[nodiscard]] std::size_t GetSize() const noexcept; + [[nodiscard]] std::size_t GetElementSize() const noexcept; + [[nodiscard]] std::size_t GetCount() const noexcept; + + void Bind(VkCommandBuffer cmd); + VulkanBuffer() = delete; VulkanBuffer(VulkanBuffer const &) = delete; - VulkanBuffer(VulkanBuffer &&) = delete; + VulkanBuffer(QVulkanWindow *window, BufferType type, std::size_t element_size, std::size_t count); + ~VulkanBuffer(); }; } // namespace ui diff --git a/src/calculator/renderer/VulkanPipeline.cpp b/src/calculator/renderer/VulkanPipeline.cpp index 29b1f7b..b9b28d9 100644 --- a/src/calculator/renderer/VulkanPipeline.cpp +++ b/src/calculator/renderer/VulkanPipeline.cpp @@ -43,6 +43,11 @@ VkShaderModule ui::VulkanPipeline::LoadShader(char const *path) return module; } +VkPipelineLayout ui::VulkanPipeline::GetLayout() const noexcept +{ + return this->pipeline_layout_; +} + void ui::VulkanPipeline::Bind(VkCommandBuffer cmd) { this->dev_->vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, this->pipeline_); @@ -174,29 +179,14 @@ ui::VulkanPipeline::VulkanPipeline(CreateVulkanPipelineInfo const &info) : windo .blendConstants = {0.f, 0.f, 0.f, 0.f}, }; - VkDescriptorSetLayoutBinding descriptor_layout_binding{ - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, - .pImmutableSamplers = nullptr, + VkPipelineDepthStencilStateCreateInfo depth_stencil_state_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, }; - VkDescriptorSetLayoutCreateInfo descriptor_set_layout_info{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = 1, - .pBindings = &descriptor_layout_binding, - }; - - if (this->dev_->vkCreateDescriptorSetLayout(this->window_->device(), &descriptor_set_layout_info, nullptr, &this->descriptor_set_layout_) != VK_SUCCESS) - { - throw std::runtime_error("failed to create descriptor set layout"); - } - VkPipelineLayoutCreateInfo pipeline_layout_info{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, - .pSetLayouts = &this->descriptor_set_layout_, + .pSetLayouts = &info.descriptor_set_layout_, .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; @@ -215,7 +205,7 @@ ui::VulkanPipeline::VulkanPipeline(CreateVulkanPipelineInfo const &info) : windo .pViewportState = &viewport_state_info, .pRasterizationState = &rasterization_state_info, .pMultisampleState = &multisample_state_info, - .pDepthStencilState = nullptr, + .pDepthStencilState = &depth_stencil_state_info, .pColorBlendState = &color_blend_state_info, .pDynamicState = &dynamic_state_info, .layout = this->pipeline_layout_, @@ -236,7 +226,6 @@ ui::VulkanPipeline::VulkanPipeline(CreateVulkanPipelineInfo const &info) : windo void ui::VulkanPipeline::Destroy() { - this->dev_->vkDestroyDescriptorSetLayout(this->window_->device(), this->descriptor_set_layout_, nullptr); this->dev_->vkDestroyPipeline(this->window_->device(), this->pipeline_, nullptr); this->dev_->vkDestroyPipelineLayout(this->window_->device(), this->pipeline_layout_, nullptr); } @@ -252,54 +241,6 @@ this->dev_->vkDestroyBuffer(this->window_->device(), this->ubo_buffer_, nullptr) this->dev_->vkDestroyDescriptorPool(this->window_->device(), this->descriptor_pool_, nullptr); - - - VkDescriptorPoolSize descriptor_pool_size{ - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - }; - - VkDescriptorPoolCreateInfo descriptor_pool_info{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = 1, - .poolSizeCount = 1, - .pPoolSizes = &descriptor_pool_size, - }; - - if (this->dev_->vkCreateDescriptorPool(this->window_->device(), &descriptor_pool_info, nullptr, &this->descriptor_pool_) != VK_SUCCESS) - { - throw std::runtime_error("failed to create descriptor pool"); - } - - VkDescriptorSetAllocateInfo descriptor_set_allocate_info{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = this->descriptor_pool_, - .descriptorSetCount = 1, - .pSetLayouts = &this->descriptor_set_layout_, - }; - - if (this->dev_->vkAllocateDescriptorSets(this->window_->device(), &descriptor_set_allocate_info, &this->descriptor_set_) != VK_SUCCESS) - { - throw std::runtime_error("failed to allocate descriptor set"); - } - - VkDescriptorBufferInfo descriptor_buffer_info{ - .buffer = this->ubo_buffer_, - .offset = 0, - .range = sizeof(unlogic::Camera), - }; - - VkWriteDescriptorSet write_descriptor_set{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = this->descriptor_set_, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = &descriptor_buffer_info, - }; - - this->dev_->vkUpdateDescriptorSets(this->window_->device(), 1, &write_descriptor_set, 0, nullptr); */ /* diff --git a/src/calculator/renderer/VulkanPipeline.h b/src/calculator/renderer/VulkanPipeline.h index 6c295a6..058d68b 100644 --- a/src/calculator/renderer/VulkanPipeline.h +++ b/src/calculator/renderer/VulkanPipeline.h @@ -10,6 +10,9 @@ namespace ui struct CreateVulkanPipelineInfo { QVulkanWindow *window = nullptr; + + VkDescriptorSetLayout descriptor_set_layout_ = nullptr; + VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; char const *vert_shader = nullptr; char const *frag_shader = nullptr; @@ -20,7 +23,6 @@ namespace ui QVulkanWindow *window_; QVulkanDeviceFunctions *dev_; - VkDescriptorSetLayout descriptor_set_layout_ = nullptr; VkPipelineLayout pipeline_layout_ = nullptr; VkPipeline pipeline_ = nullptr; @@ -28,6 +30,8 @@ namespace ui VkShaderModule LoadShader(char const *path); public: + [[nodiscard]] VkPipelineLayout GetLayout() const noexcept; + void Bind(VkCommandBuffer cmd); void BindDescriptorSets(VkCommandBuffer cmd, VkDescriptorSet const *descriptor_set, std::size_t count); void Destroy(); diff --git a/src/calculator/renderer/VulkanRenderer.cpp b/src/calculator/renderer/VulkanRenderer.cpp index bbbdc6c..ea5b124 100644 --- a/src/calculator/renderer/VulkanRenderer.cpp +++ b/src/calculator/renderer/VulkanRenderer.cpp @@ -5,37 +5,125 @@ using namespace ui; +void VulkanRenderer::invalidateFrameContexts() noexcept +{ + for (auto &ctx: this->contexts_) + { + ctx.valid = false; + } +} + void VulkanRenderer::initResources() { this->dev_ = this->window_->vulkanInstance()->deviceFunctions(this->window_->device()); + VkDescriptorSetLayoutBinding descriptor_layout_binding{ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, + .pImmutableSamplers = nullptr, + }; + + VkDescriptorSetLayoutCreateInfo descriptor_set_layout_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = &descriptor_layout_binding, + }; + + if (this->dev_->vkCreateDescriptorSetLayout(this->window_->device(), &descriptor_set_layout_info, nullptr, &this->descriptor_set_layout_) != VK_SUCCESS) + { + throw std::runtime_error("failed to create descriptor set layout"); + } + + VkDescriptorPoolSize descriptor_pool_size{ + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = this->contexts_.size(), + }; + + VkDescriptorPoolCreateInfo descriptor_pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = this->contexts_.size(), + .poolSizeCount = 1, + .pPoolSizes = &descriptor_pool_size, + }; + + if (this->dev_->vkCreateDescriptorPool(this->window_->device(), &descriptor_pool_info, nullptr, &this->descriptor_pool_) != VK_SUCCESS) + { + throw std::runtime_error("failed to create descriptor pool"); + } + CreateVulkanPipelineInfo grid_info{ .window = this->window_, + .descriptor_set_layout_ = this->descriptor_set_layout_, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .vert_shader = ":/shaders/grid.vert.qsb", .frag_shader = ":/shaders/grid.frag.qsb", - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, }; this->grid_pipeline_ = std::make_unique(grid_info); CreateVulkanPipelineInfo plot_info{ .window = this->window_, + .descriptor_set_layout_ = this->descriptor_set_layout_, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, .vert_shader = ":/shaders/plot.vert.qsb", .frag_shader = ":/shaders/plot.frag.qsb", - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, }; this->plot_pipeline_ = std::make_unique(plot_info); for (int i = 0; i < this->window_->concurrentFrameCount(); i++) { - this->contexts_.push_back({ - .camera_buffer = VulkanBuffer::Create(this->window_, BufferType::Uniform), - }); + this->contexts_[i].valid = false; + this->contexts_[i].scene = this->window_->scene; + this->contexts_[i].camera_buffer = VulkanBuffer::Create(this->window_, BufferType::Uniform); + this->contexts_[i].plot_buffers.clear(); + + VkDescriptorSetAllocateInfo descriptor_set_allocate_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = this->descriptor_pool_, + .descriptorSetCount = 1, + .pSetLayouts = &this->descriptor_set_layout_, + }; + + if (this->dev_->vkAllocateDescriptorSets(this->window_->device(), &descriptor_set_allocate_info, &this->contexts_[i].descriptor_set) != VK_SUCCESS) + { + throw std::runtime_error("failed to allocate descriptor set"); + } + + VkDescriptorBufferInfo descriptor_buffer_info{ + .buffer = this->contexts_[i].camera_buffer->GetNativeHandle(), + .offset = 0, + .range = this->contexts_[i].camera_buffer->GetSize(), + }; + + VkWriteDescriptorSet write_descriptor_set{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = this->contexts_[i].descriptor_set, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &descriptor_buffer_info, + }; + + this->dev_->vkUpdateDescriptorSets(this->window_->device(), 1, &write_descriptor_set, 0, nullptr); } } void VulkanRenderer::releaseResources() { - this->contexts_.clear(); + for (auto &ctx: this->contexts_) + { + ctx.valid = false; + ctx.scene.reset(); + ctx.camera_buffer.reset(); + ctx.grid_buffer.reset(); + ctx.plot_buffers.clear(); + } + + this->dev_->vkDestroyDescriptorPool(this->window_->device(), this->descriptor_pool_, nullptr); + this->dev_->vkDestroyDescriptorSetLayout(this->window_->device(), this->descriptor_set_layout_, nullptr); + this->grid_pipeline_.reset(); this->plot_pipeline_.reset(); } @@ -51,7 +139,7 @@ void VulkanRenderer::startNextFrame() // Begin render pass VkCommandBuffer cmd = this->window_->currentCommandBuffer(); - VkClearColorValue clearColor = {0.f, 0.f, 0.f, 1.f}; + VkClearColorValue clearColor = {0.f, 0.f, 0.f, 0.f}; VkClearDepthStencilValue clearDS = {1.0f, 0}; VkClearValue clearValues[2]; memset(clearValues, 0, sizeof(clearValues)); @@ -92,12 +180,41 @@ void VulkanRenderer::startNextFrame() this->dev_->vkCmdSetScissor(cmd, 0, 1, &scissor); auto &ctx = this->contexts_[this->window_->currentFrame()]; + ctx.camera_buffer->Write(&this->window_->camera); - ctx.camera_buffer.Write(&this->window_->camera); + if (!ctx.valid) + { + ctx.plot_buffers.clear(); + ctx.scene = this->window_->scene; + + auto const &grid_vertices = this->window_->grid.GetVertices(); + ctx.grid_buffer = VulkanBuffer::Create(this->window_, BufferType::Vertex, grid_vertices.size()); + ctx.grid_buffer->Write(grid_vertices.data(), grid_vertices.size()); + + for (auto const &plot: ctx.scene->plots) + { + auto const &plot_vertices = plot.GetVertices(); + auto plot_buffer = VulkanBuffer::Create(this->window_, BufferType::Vertex, plot_vertices.size()); + plot_buffer->Write(plot_vertices.data(), plot_vertices.size()); + } + + ctx.valid = true; + } this->grid_pipeline_->Bind(cmd); + this->dev_->vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, this->grid_pipeline_->GetLayout(), 0, 1, &ctx.descriptor_set, 0, nullptr); + + ctx.grid_buffer->Bind(cmd); + this->dev_->vkCmdDraw(cmd, ctx.grid_buffer->GetCount(), 1, 0, 0); this->plot_pipeline_->Bind(cmd); + this->dev_->vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, this->plot_pipeline_->GetLayout(), 0, 1, &ctx.descriptor_set, 0, nullptr); + + for (auto &plot_buffer: ctx.plot_buffers) + { + plot_buffer->Bind(cmd); + this->dev_->vkCmdDraw(cmd, plot_buffer->GetCount(), 1, 0, 0); + } this->dev_->vkCmdEndRenderPass(cmd); diff --git a/src/calculator/renderer/VulkanRenderer.h b/src/calculator/renderer/VulkanRenderer.h index 5c414c5..d4d32a5 100644 --- a/src/calculator/renderer/VulkanRenderer.h +++ b/src/calculator/renderer/VulkanRenderer.h @@ -14,9 +14,14 @@ namespace ui struct VulkanFrameContext { - std::chrono::time_point first_flight; - VulkanBuffer camera_buffer; - std::vector plot_buffers; + std::atomic valid = false; + + VkDescriptorSet descriptor_set = nullptr; + + std::shared_ptr scene; + std::unique_ptr camera_buffer; + std::unique_ptr grid_buffer; + std::vector> plot_buffers; }; class VulkanRenderer : public QVulkanWindowRenderer @@ -25,12 +30,17 @@ namespace ui QVulkanDeviceFunctions *dev_ = nullptr; + VkDescriptorSetLayout descriptor_set_layout_; + VkDescriptorPool descriptor_pool_; + std::unique_ptr grid_pipeline_; std::unique_ptr plot_pipeline_; - std::vector contexts_; + std::array contexts_; public: + void invalidateFrameContexts() noexcept; + void initResources() override; void releaseResources() override; void startNextFrame() override; diff --git a/src/calculator/renderer/VulkanWindow.cpp b/src/calculator/renderer/VulkanWindow.cpp index 1770349..5e9b2fd 100644 --- a/src/calculator/renderer/VulkanWindow.cpp +++ b/src/calculator/renderer/VulkanWindow.cpp @@ -5,6 +5,11 @@ using namespace ui; void VulkanWindow::setScene(std::shared_ptr scene) { this->scene = scene; + + if (this->renderer) + { + this->renderer->invalidateFrameContexts(); + } } void VulkanWindow::mousePressEvent(QMouseEvent *ev) diff --git a/src/graphic/Scene.cpp b/src/graphic/Scene.cpp index 0258ce1..c28341f 100644 --- a/src/graphic/Scene.cpp +++ b/src/graphic/Scene.cpp @@ -3,6 +3,23 @@ // #include "Scene.h" +#include +#include "Color.h" -namespace unlogic { -} // unlogic \ No newline at end of file +using namespace unlogic; + +void Scene::AddPlot(char const *name, Plot2dFunctionType function) +{ + std::array colors = { + Color::Red, + Color::Green, + Color::Blue, + Color::Orange, + Color::Cyan, + Color::Pink, + Color::Purple, + }; + Color color = colors[this->plots.size() % colors.size()]; + + this->plots.emplace_back(name, function, color); +} diff --git a/src/graphic/Scene.h b/src/graphic/Scene.h index 6c6c61f..a8c04a5 100644 --- a/src/graphic/Scene.h +++ b/src/graphic/Scene.h @@ -5,9 +5,7 @@ #ifndef SCENE_H #define SCENE_H -#include #include -#include "Color.h" #include "shape/Plot.h" namespace unlogic @@ -17,22 +15,7 @@ namespace unlogic bool draw_grid = true; std::vector plots; - void AddPlot(char const *name, Plot2dFunctionType function) - { - std::array colors = { - Color::Red, - Color::Green, - Color::Blue, - Color::Orange, - Color::Yellow, - Color::Cyan, - Color::Pink, - Color::Purple, - }; - Color color = colors[std::rand() % colors.size()]; - - this->plots.emplace_back(name, function, color); - } + void AddPlot(char const *name, Plot2dFunctionType function); }; } // namespace unlogic diff --git a/src/graphic/shape/Rect.cpp b/src/graphic/shape/Rect.cpp index 0835923..248ba0e 100644 --- a/src/graphic/shape/Rect.cpp +++ b/src/graphic/shape/Rect.cpp @@ -1,4 +1,5 @@ #include "Rect.h" +#include using namespace unlogic;