1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-29 15:07:45 +00:00
novus/renderer/src/imguipass.cpp
Joshua Goins e34daadbcd Split up Renderer's source files and a lot of refactoring
This now splits up the rendering system into sensible parts, and makes
it easier to switch between the simple renderer and the new experimental
one. Lots of refactors I needed to do for a while are now done, too.
2024-04-21 17:35:51 -04:00

339 lines
15 KiB
C++

// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "imguipass.h"
#include <QDebug>
#include <array>
#include <glm/glm.hpp>
#include <imgui.h>
#include "rendermanager.h"
ImGuiPass::ImGuiPass(RenderManager &renderer)
: renderer_(renderer)
{
createDescriptorSetLayout();
createPipeline();
createFontImage();
}
ImGuiPass::~ImGuiPass()
{
vkDestroySampler(renderer_.device().device, fontSampler_, nullptr);
vkDestroyImageView(renderer_.device().device, fontImageView_, nullptr);
vkFreeMemory(renderer_.device().device, fontMemory_, nullptr);
vkDestroyImage(renderer_.device().device, fontImage_, nullptr);
vkDestroyPipeline(renderer_.device().device, pipeline_, nullptr);
vkDestroyPipelineLayout(renderer_.device().device, pipelineLayout_, nullptr);
vkDestroyDescriptorSetLayout(renderer_.device().device, setLayout_, nullptr);
}
void ImGuiPass::render(VkCommandBuffer commandBuffer)
{
ImDrawData *drawData = ImGui::GetDrawData();
if (drawData == nullptr) {
return;
}
const size_t newVertexSize = drawData->TotalVtxCount * sizeof(ImDrawVert);
if (newVertexSize > vertexSize) {
createBuffer(vertexBuffer, vertexMemory, newVertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
vertexSize = newVertexSize;
}
const size_t newIndexSize = drawData->TotalIdxCount * sizeof(ImDrawIdx);
if (newIndexSize > indexSize) {
createBuffer(indexBuffer, indexMemory, newIndexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
indexSize = newIndexSize;
}
if (vertexSize == 0 || indexSize == 0)
return;
ImDrawVert *vertexData = nullptr;
ImDrawIdx *indexData = nullptr;
vkMapMemory(renderer_.device().device, vertexMemory, 0, vertexSize, 0, reinterpret_cast<void **>(&vertexData));
vkMapMemory(renderer_.device().device, indexMemory, 0, indexSize, 0, reinterpret_cast<void **>(&indexData));
for (int i = 0; i < drawData->CmdListsCount; i++) {
const ImDrawList *cmd_list = drawData->CmdLists[i];
memcpy(vertexData, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
vertexData += cmd_list->VtxBuffer.Size;
memcpy(indexData, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
indexData += cmd_list->IdxBuffer.Size;
}
vkUnmapMemory(renderer_.device().device, vertexMemory);
vkUnmapMemory(renderer_.device().device, indexMemory);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, &offset);
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);
float scale[2];
scale[0] = 2.0f / drawData->DisplaySize.x;
scale[1] = 2.0f / drawData->DisplaySize.y;
float translate[2];
translate[0] = -1.0f - drawData->DisplayPos.x * scale[0];
translate[1] = -1.0f - drawData->DisplayPos.y * scale[1];
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale);
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate);
int vertexOffset = 0, indexOffset = 0;
const ImVec2 displayPos = drawData->DisplayPos;
for (int n = 0; n < drawData->CmdListsCount; n++) {
const ImDrawList *cmd_list = drawData->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
if (descriptorSets_.count((VkImageView)pcmd->TextureId)) {
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout_,
0,
1,
&descriptorSets_[(VkImageView)pcmd->TextureId],
0,
nullptr);
} else {
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = renderer_.device().descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &setLayout_;
VkDescriptorSet set = nullptr;
vkAllocateDescriptorSets(renderer_.device().device, &allocInfo, &set);
VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = (VkImageView)pcmd->TextureId;
imageInfo.sampler = fontSampler_;
VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.descriptorCount = 1;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.dstSet = set;
descriptorWrite.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(renderer_.device().device, 1, &descriptorWrite, 0, nullptr);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &set, 0, nullptr);
descriptorSets_[(VkImageView)pcmd->TextureId] = set;
}
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
VkRect2D scissor;
scissor.offset.x = (int32_t)(pcmd->ClipRect.x - displayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - displayPos.x) : 0;
scissor.offset.y = (int32_t)(pcmd->ClipRect.y - displayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - displayPos.y) : 0;
scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1);
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDrawIndexed(commandBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0);
}
indexOffset += pcmd->ElemCount;
}
vertexOffset += cmd_list->VtxBuffer.Size;
}
}
void ImGuiPass::createDescriptorSetLayout()
{
VkDescriptorSetLayoutBinding samplerBinding = {};
samplerBinding.descriptorCount = 1;
samplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.bindingCount = 1;
createInfo.pBindings = &samplerBinding;
vkCreateDescriptorSetLayout(renderer_.device().device, &createInfo, nullptr, &setLayout_);
}
void ImGuiPass::createPipeline()
{
VkShaderModule vertShaderModule = renderer_.device().loadShaderFromDisk(":/shaders/imgui.vert.spv");
VkShaderModule fragShaderModule = renderer_.device().loadShaderFromDisk(":/shaders/imgui.frag.spv");
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages = {vertShaderStageInfo, fragShaderStageInfo};
VkVertexInputBindingDescription vertexBindingDescription = {};
vertexBindingDescription.stride = sizeof(ImDrawVert);
vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription positionAttribute = {};
positionAttribute.format = VK_FORMAT_R32G32_SFLOAT;
positionAttribute.offset = offsetof(ImDrawVert, pos);
VkVertexInputAttributeDescription uvAttribute = {};
uvAttribute.location = 1;
uvAttribute.format = VK_FORMAT_R32G32_SFLOAT;
uvAttribute.offset = offsetof(ImDrawVert, uv);
VkVertexInputAttributeDescription colorAttribute = {};
colorAttribute.location = 2;
colorAttribute.format = VK_FORMAT_R8G8B8A8_UNORM;
colorAttribute.offset = offsetof(ImDrawVert, col);
const std::array<VkVertexInputAttributeDescription, 3> attributes = {positionAttribute, uvAttribute, colorAttribute};
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = attributes.size();
vertexInputInfo.pVertexAttributeDescriptions = attributes.data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
const std::array<VkDynamicState, 2> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = dynamicStates.size();
dynamicState.pDynamicStates = dynamicStates.data();
VkPushConstantRange pushConstant = {};
pushConstant.size = sizeof(glm::vec4);
pushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &setLayout_;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstant;
vkCreatePipelineLayout(renderer_.device().device, &pipelineLayoutInfo, nullptr, &pipelineLayout_);
VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {};
depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = shaderStages.size();
pipelineInfo.pStages = shaderStages.data();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = pipelineLayout_;
pipelineInfo.pDepthStencilState = &depthStencilStateCreateInfo;
pipelineInfo.renderPass = renderer_.presentationRenderPass();
vkCreateGraphicsPipelines(renderer_.device().device, nullptr, 1, &pipelineInfo, nullptr, &pipeline_);
vkDestroyShaderModule(renderer_.device().device, fragShaderModule, nullptr);
vkDestroyShaderModule(renderer_.device().device, vertShaderModule, nullptr);
}
void ImGuiPass::createFontImage()
{
ImGuiIO &io = ImGui::GetIO();
unsigned char *pixels = nullptr;
int width = 0, height = 0;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
auto texture = renderer_.addTexture(width, height, pixels, width * height * 4);
fontImageView_ = texture.view;
fontSampler_ = texture.sampler;
io.Fonts->SetTexID(static_cast<ImTextureID>(fontImageView_));
}
void ImGuiPass::createBuffer(VkBuffer &buffer, VkDeviceMemory &memory, VkDeviceSize size, VkBufferUsageFlagBits bufferUsage)
{
if (buffer != nullptr)
vkDestroyBuffer(renderer_.device().device, buffer, nullptr);
if (memory != nullptr)
vkFreeMemory(renderer_.device().device, memory, nullptr);
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = bufferUsage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(renderer_.device().device, &bufferInfo, nullptr, &buffer);
VkMemoryRequirements memRequirements = {};
vkGetBufferMemoryRequirements(renderer_.device().device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex =
renderer_.device().findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(renderer_.device().device, &allocInfo, nullptr, &memory);
vkBindBufferMemory(renderer_.device().device, buffer, memory, 0);
}