diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index 7e53fdf..3f6af0d 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -9,4 +9,46 @@ namespace gz::vlk { vk.destroyBuffer(indexBuffer, indexBufferMemory); vk.destroyBuffer(vertexBuffer, vertexBufferMemory); } + + + + void Renderer::createDescriptorSetLayoutSampler(uint32_t binding) { + // combined image sampler + std::vector sampleFromRenderImageDSLB; + sampleFromRenderImageDSLB.emplace_back(vk::DescriptorSetLayoutBinding { + .binding = binding, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + }); + vk.createDescriptorSetLayout(sampleFromRenderImageDSLB, sampleFromRenderImageDSL); + } + + + void Renderer::createDescriptorSetSampler() { + std::vector layouts2(vk.getScImages().size(), sampleFromRenderImageDSL); + vk.createDescriptorSets(layouts2, descriptorPool, sampleFromRenderImageDS); + + assert(renderImageViews.size() == vk.getScImages().size()); + assert(sampleFromRenderImageDS.size() == vk.getScImages().size()); + for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) { + vk::DescriptorImageInfo imageI { + .sampler = imageSampler, + .imageView = renderImageViews[i], + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + + vk::WriteDescriptorSet descriptorW{ + .dstSet = sampleFromRenderImageDS[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &imageI, + }; + // write 1, copy 0 + vk.getDevice().updateDescriptorSets(descriptorW, nullptr); + } // for + /* rLog.log0("createDescriptoSetSampler: Created descriptor layout and sets for sampling from renderImages"); */ + } } diff --git a/src/renderer/renderer.hpp b/src/renderer/renderer.hpp index 930415c..0d61898 100644 --- a/src/renderer/renderer.hpp +++ b/src/renderer/renderer.hpp @@ -6,6 +6,8 @@ // includes for child classes #include "vulkan_util.hpp" + +#define LOG_SUBLOGS #include #include "texture_manager.hpp" @@ -42,6 +44,7 @@ namespace gz::vlk { * The images are used as render targets, `#ifdef GZ_RENDERER_OWN_RENDER_IMAGES` * * After rendering, the current image gets blitted onto the current swap chain image. + * These resources need to be created and cleaned up by the subclass * @todo */ /// @{ @@ -51,6 +54,17 @@ namespace gz::vlk { vk::Sampler imageSampler; /// @} + /** + * @name Descriptors + * @details + * The descriptor pool has to be created by the subclass. + */ + vk::DescriptorPool descriptorPool; + void createDescriptorSetLayoutSampler(uint32_t binding); + void createDescriptorSetSampler(); + vk::DescriptorSetLayout sampleFromRenderImageDSL; + std::vector sampleFromRenderImageDS; + std::vector commandBuffers; /// On device local memory vk::Buffer vertexBuffer; diff --git a/src/renderer/renderer2D.cpp b/src/renderer/renderer2D.cpp index 9095ce5..61a91ba 100644 --- a/src/renderer/renderer2D.cpp +++ b/src/renderer/renderer2D.cpp @@ -1,6 +1,7 @@ #include "renderer2D.hpp" #include "exceptions.hpp" +#include "shape.hpp" #include "vulkan_allocator.hpp" #include "vulkan_instance.hpp" #include "texture_manager.hpp" @@ -12,12 +13,32 @@ #include namespace gz::vlk { +#define RENDERER_2D_RENDER_TO_OWN_IMAGES +#define RENDERER_2D_COPY_TO_SWAP_CHAIN + +#ifdef RENDERER_2D_RENDER_TO_SWAP_CHAIN + #ifdef RENDERER_2D_RENDER_TO_OWN_IMAGES + static_assert(false, "RENDERER_2D_RENDER_TO_SWAP_CHAIN and RENDERER_2D_RENDER_TO_OWN_IMAGES defined at the same time"); + #endif + #ifdef RENDERER_2D_COPY_TO_SWAP_CHAIN + static_assert(false, "RENDERER_2D_RENDER_TO_SWAP_CHAIN and RENDERER_2D_COPY_TO_SWAP_CHAIN defined at the same time"); + #endif + #ifdef RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + static_assert(false, "RENDERER_2D_RENDER_TO_SWAP_CHAIN and RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES defined at the same time"); + #endif +#elif defined RENDERER_2D_RENDER_TO_OWN_IMAGES +/* #ifndef RENDERER_2D_COPY_TO_SWAP_CHAIN */ +/* #define RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES */ +/* #endif */ +#else + static_assert(false, "Either RENDERER_2D_RENDER_TO_OWN_IMAGES or RENDERER_2D_RENDER_TO_SWAP_CHAIN must be defined"); +#endif // // INIT & CLEANUP // - Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) : - Renderer(instance, textureManager) + Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) + : Renderer(instance, textureManager), shapeBufferManager(instance) { LogCreateInfo logCI { .logfile = "renderer2D.log", @@ -35,10 +56,16 @@ namespace gz::vlk { const size_t indexCount = 1000; vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); +#ifdef RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + createDescriptorResources(); +#endif initSwapChainDependantResources(); VulkanInstance::registerObjectUsingVulkan("Renderer2D", - &pipelines[R2Render].pipeline, &renderPass, &vertexBuffer, &vertexBufferMemory.memory, &indexBuffer, &indexBufferMemory.memory, - &framebuffers, &renderImages, &renderImageViews, &commandBuffers); // &renderImageMemory, + &vertexBuffer, &vertexBufferMemory.memory, + &indexBuffer, &indexBufferMemory.memory, + &renderPass, &pipelines[R2Render].pipeline, //&pipelines[blendToSC].pipeline, + &imageSampler, &framebuffers, &renderImages, &renderImageViews, // missing: &renderImageMemory, + &commandBuffers); rLog("Created Renderer2D"); } @@ -48,6 +75,10 @@ namespace gz::vlk { // UPDATE DOC ON CHANGES! /* vk.destroyCommandBuffers(commandBuffers); */ +#ifdef RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + vk.getDevice().destroyDescriptorPool(descriptorPool); + vk.getDevice().destroyDescriptorSetLayout(sampleFromRenderImageDSL); +#endif cleanupSwapChainDependantResources(); cleanup_(); } @@ -59,6 +90,9 @@ namespace gz::vlk { void Renderer2D::initSwapChainDependantResources() { createRenderPass(); createImageResources(); +#ifdef RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + createDescriptorSetSampler(); +#endif vk.createFramebuffers(framebuffers, renderImageViews, renderPass); std::vector descriptorSetLayouts = { textureManager.getDescriptorSetLayout() }; @@ -92,7 +126,7 @@ namespace gz::vlk { .stage = vk::ShaderStageFlagBits::eFragment, .module = fragShaderModule, .pName = "main", - .pSpecializationInfo = nullptr, + .pSpecializationInfo = &specI, }); vk::GraphicsPipelineCreateInfo pipelineCI { @@ -101,7 +135,12 @@ namespace gz::vlk { }; pipelineCI.setStages(shaderStages); - vk.createGraphicsPipeline(std::move(pipelineCI), &descriptorSetLayouts, true, pipelines[R2Render]); + std::vector pushConstantRanges; + pushConstantRanges.emplace_back(vk::PushConstantRange{ + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .size = sizeof(uint32_t), + }); + vk.createGraphicsPipeline(std::move(pipelineCI), descriptorSetLayouts, pushConstantRanges, false, pipelines[R2Render]); vk.getDevice().destroyShaderModule(vertShaderModule, nullptr); vk.getDevice().destroyShaderModule(fragShaderModule, nullptr); @@ -114,7 +153,7 @@ namespace gz::vlk { vk.destroyFramebuffers(framebuffers); -#ifndef RENDERER_2D_RENDER_TO_SWAP_CHAIN +#ifdef RENDERER_2D_RENDER_TO_OWN_IMAGES for (size_t i = 0; i < renderImages.size(); i++) { vk.destroyImageView(renderImageViews[i]); vk.destroyImage(renderImages[i], renderImageMemory[i]); @@ -135,22 +174,34 @@ namespace gz::vlk { // void Renderer2D::createImageResources() { renderImageViews.resize(vk.getScImages().size()); -#ifndef RENDERER_2D_RENDER_TO_SWAP_CHAIN +#ifdef RENDERER_2D_RENDER_TO_OWN_IMAGES renderImages.resize(vk.getScImages().size()); renderImageMemory.resize(vk.getScImages().size()); - vk::ImageUsageFlags usage= vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc; + vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment; + #ifdef RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES + usage |= vk::ImageUsageFlagBits::eSampled; + #elif defined RENDERER_2D_COPY_TO_SWAP_CHAIN + usage |= vk::ImageUsageFlagBits::eTransferSrc; + #endif + rLog.log("Image usage flags:", usage); for (size_t i = 0; i < renderImages.size(); i++) { vk.createImage(vk.getScExtent().width, vk.getScExtent().height, vk.getScImageFormat(), vk::ImageTiling::eOptimal, usage, vk::MemoryPropertyFlagBits::eDeviceLocal, renderImages[i], renderImageMemory[i]); + } + #ifdef RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES + vk.createTextureSampler(imageSampler); + #endif + for (size_t i = 0; i < renderImages.size(); i++) { vk.createImageView(vk.getScImageFormat(), renderImages[i], renderImageViews[i], vk::ImageAspectFlagBits::eColor); } - vk.createTextureSampler(imageSampler); rLog.log0("createImageResources: created images, views and sampler"); -#else - for (size_t i = 0; i < renderImages.size(); i++) { +#elif defined RENDERER_2D_RENDER_TO_SWAP_CHAIN + // views to swap chain images + for (size_t i = 0; i < renderImageViews.size(); i++) { vk.createImageView(vk.getScImageFormat(), vk.getScImages()[i], renderImageViews[i], vk::ImageAspectFlagBits::eColor); } rLog.log0("createImageResources: created image views"); #endif + assert(renderImageViews.size() == vk.getScImages().size()); } // @@ -165,7 +216,13 @@ namespace gz::vlk { .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, .initialLayout = vk::ImageLayout::eUndefined, +#ifdef RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + .finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal, +#elif defined RENDERER_2D_COPY_TO_SWAP_CHAIN .finalLayout = vk::ImageLayout::eTransferSrcOptimal, +#else + .finalLayout = vk::ImageLayout::eColorAttachmentOptimal, +#endif }; vk::AttachmentReference2 colorAttachmentRef { @@ -173,21 +230,7 @@ namespace gz::vlk { .layout = vk::ImageLayout::eColorAttachmentOptimal, }; - /* vk::AttachmentDescription depthAttachment { */ - /* depthAttachment.format = findDepthFormat(); */ - /* depthAttachment.samples = vk::SampleCountFlagBits::e1; */ - /* depthAttachment.loadOp = vk::AttachmentLoadOp::eClear; */ - /* depthAttachment.storeOp = vk::AttachmentStoreOp::eDontCare; */ - /* depthAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare; */ - /* depthAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare; */ - /* depthAttachment.initialLayout = vk::ImageLayout::eUndefined; */ - /* depthAttachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal; */ - - /* vk::AttachmentReference depthAttachmentRef { */ - /* depthAttachmentRef.attachment = 1; */ - /* depthAttachmentRef.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; */ - - // SUBPASS 1: DRAW STUFF + // SUBPASS: Rendering vk::SubpassDescription2 drawSubpass { .pipelineBindPoint = vk::PipelineBindPoint::eGraphics, .colorAttachmentCount = 1, @@ -205,36 +248,35 @@ namespace gz::vlk { }; // dependecy for the image layout transition to transfer dst - /* vk::SubpassDependency2 dsLayoutTransitionSD { */ + vk::SubpassDependency2 dsLayoutTransitionSD { + .srcSubpass = 0, + .dstSubpass = VK_SUBPASS_EXTERNAL, + .srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput, + .dstStageMask = vk::PipelineStageFlagBits::eTransfer, + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + }; + + // SUBPASS 2: BLIT ONTO SWAP CHAIN IMAGE + /* vk::SubpassDescription2 blitSubpass { */ + /* .pipelineBindPoint = vk::PipelineBindPoint::eGraphics, */ + /* .colorAttachmentCount = 1, */ + /* .pColorAttachments = &colorAttachmentRef, */ + /* }; */ + + /* vk::SubpassDependency2 bsColorAttachmentSD { */ /* .srcSubpass = 0, */ /* .dstSubpass = 1, */ /* .srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput, */ - /* .dstStageMask = vk::PipelineStageFlagBits::eTransfer, */ + /* .dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput, */ /* .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, */ - /* .dstAccessMask = vk::AccessFlagBits::eTransferRead, */ - /* .dependencyFlags = vk::DependencyFlagBits::eByRegion, */ + /* .dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead, */ /* }; */ - // SUBPASS 2: BLIT ONTO SWAP CHAIN IMAGE - vk::SubpassDescription2 blitSubpass { - .pipelineBindPoint = vk::PipelineBindPoint::eGraphics, - .colorAttachmentCount = 1, - .pColorAttachments = &colorAttachmentRef, - }; - - vk::SubpassDependency2 bsColorAttachmentSD { - .srcSubpass = 0, - .dstSubpass = 1, - .srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput, - .dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput, - .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead, - }; - - /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ std::vector attachments = { colorBlendAttachment }; - std::vector subpasses { drawSubpass, blitSubpass }; - std::vector dependencies = { dsColorAttachmentSD, bsColorAttachmentSD }; //dsLayoutTransitionSD }; + std::vector subpasses { drawSubpass }; + std::vector dependencies = { dsColorAttachmentSD, dsLayoutTransitionSD }; //, bsColorAttachmentSD }; //dsLayoutTransitionSD }; vk::RenderPassCreateInfo2 renderPassCI; renderPassCI.setAttachments(attachments); renderPassCI.setSubpasses(subpasses); @@ -247,18 +289,36 @@ namespace gz::vlk { rLog("createRenderPass: Created render pass."); } +// +// DESCRIPTORS +// + void Renderer2D::createDescriptorResources() { + createDescriptorSetLayoutSampler(0); + // POOL + std::vector poolSizes; + poolSizes = { + { .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = static_cast(vk.getScImages().size()) }, + }; + uint32_t maxSets = 0; + for (auto& size : poolSizes) { + maxSets += size.descriptorCount; + } + vk.createDescriptorPool(poolSizes, maxSets, descriptorPool); + rLog("createDescriptorResources: pool", reinterpret_cast(static_cast(descriptorPool)), "vs hpp:", reinterpret_cast(&(*descriptorPool))); + } // // RENDERING // - void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) { + void Renderer2D::beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame) { + commandBuffers[currentFrame].reset(); vk::CommandBufferBeginInfo commandBufferBI { /* .flags = 0, */ /* .pInheritanceInfo = nullptr, */ }; vk::Result result = commandBuffers[currentFrame].begin(&commandBufferBI); if (result != vk::Result::eSuccess) { - throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer"); + throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::beginFrameDraw"); } // clear @@ -275,15 +335,90 @@ namespace gz::vlk { renderPassBI.setClearValues(clearValues); commandBuffers[currentFrame].beginRenderPass(&renderPassBI, vk::SubpassContents::eInline); + } + + void Renderer2D::endFrameDraw(uint32_t imageIndex, uint32_t currentFrame) { + commandBuffers[currentFrame].endRenderPass(); + +#ifdef RENDERER_2D_COPY_TO_SWAP_CHAIN + vk.copyImageToImage(commandBuffers[currentFrame], renderImages[imageIndex], vk.getScImages()[imageIndex], vk.getScExtent()); +#endif + vk::Result result = commandBuffers[currentFrame].end(); + if (result != vk::Result::eSuccess) { + rLog.error("Failed to record 2D - command buffer", "VkResult:", result); + throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::endFrameDraw"); + } + vk.submitThisFrame(commandBuffers[currentFrame]); + } + + + /* void Renderer2D::fillVertexBufferWithShapes() { */ + /* rLog("fillVertexBufferWithShapes"); */ + /* if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { */ + /* throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */ + /* } */ + + /* // create staging buffer */ + /* vk::Buffer stagingBuffer; */ + /* MemoryInfo stagingBufferMemory; */ + /* vk.createBuffer(vertexBufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); */ + + /* // fill staging buffer */ + /* void* data; */ + /* vk::Result result = vk.getDevice().mapMemory(stagingBufferMemory.memory, stagingBufferMemory.offset, vertexBufferSize, NO_MEM_FLAGS, &data); */ + /* if (result != vk::Result::eSuccess) { */ + /* throw getVkException(result, "Failed to map staging buffer", "Renderer2D::fillVertexBufferWithShapes"); */ + /* } */ + /* Vertex2D* vdata = reinterpret_cast(data); */ + /* size_t offset = 0; */ + /* for (auto it = shapes.begin(); it != shapes.end(); it++) { */ + /* rLog.log0("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); */ + /* memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); */ + /* offset += it->getVertices().size(); */ + /* } */ + /* vk.getDevice().unmapMemory(stagingBufferMemory.memory); */ + /* // fill vertex buffer */ + /* vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); */ + /* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */ + /* } */ + /* void Renderer2D::fillIndexBufferWithShapes() { */ + /* rLog("fillIndexBufferWithShapes"); */ + /* if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { */ + /* throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */ + /* } */ + + /* // create staging buffer */ + /* vk::Buffer stagingBuffer; */ + /* MemoryInfo stagingBufferMemory; */ + /* vk.createBuffer(indexBufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); */ + + /* // fill staging buffer */ + /* void* data; */ + /* vk::Result result = vk.getDevice().mapMemory(stagingBufferMemory.memory, stagingBufferMemory.offset, indexBufferSize, NO_MEM_FLAGS, &data); */ + /* if (result != vk::Result::eSuccess) { */ + /* throw getVkException(result, "Failed to map staging buffer", "Renderer2D::fillIndexBufferWithShapes"); */ + /* } */ + /* uint32_t* idata = reinterpret_cast(data); */ + /* size_t offset = 0; */ + /* for (auto it = shapes.begin(); it != shapes.end(); it++) { */ + /* rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); */ + /* memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); */ + /* offset += it->getIndices().size(); */ + /* } */ + /* rLog.log0("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); */ + /* vk.getDevice().unmapMemory(stagingBufferMemory.memory); */ + + /* // fill index buffer */ + /* vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); */ + /* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */ + + /* } */ + + + void Renderer2D::drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame) { + /* rLog.log0("drawShape:", shape->getVertexBufferInfo(), shape->getIndexBufferInfo()); */ commandBuffers[currentFrame].bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline); - vk::Buffer vertexBuffers[] = { vertexBuffer }; - vk::DeviceSize offsets[] = {0}; - uint32_t bindingCount = 1; - uint32_t binding = 0; - commandBuffers[currentFrame].bindVertexBuffers(binding, bindingCount, vertexBuffers, offsets); - // TODO use correct index type! - commandBuffers[currentFrame].bindIndexBuffer(indexBuffer, NO_OFFSET, vk::IndexType::eUint32); uint32_t descriptorCount = 1; uint32_t firstSet = 0; @@ -291,103 +426,36 @@ namespace gz::vlk { uint32_t* dynamicOffsets = nullptr; commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); - int instanceCount = 1; - int firstIndex = 0; - int firstInstance = 0; - commandBuffers[currentFrame].drawIndexed(static_cast(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); - commandBuffers[currentFrame].endRenderPass(); - /* vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.getScImages()[imageIndex], vk.getScExtent()); */ - result = commandBuffers[currentFrame].end(); - if (result != vk::Result::eSuccess) { - rLog.error("Failed to record 2D - command buffer", "VkResult:", result); - throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBufferWithTexture"); - } - vk.submitThisFrame(commandBuffers[currentFrame]); + vk::Buffer vertexBuffers[] = { shapeBufferManager.getVertexBuffer(shape->getVertexBufferInfo()) }; + vk::DeviceSize vertexOffsets[] = { shape->getVertexBufferInfo().offset }; + uint32_t bindingCount = 1; + uint32_t binding = 0; + + commandBuffers[currentFrame].bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets); + + vk::Buffer indexBuffers = { shapeBufferManager.getIndexBuffer(shape->getIndexBufferInfo()) }; + vk::DeviceSize indexOffset = shape->getIndexBufferInfo().offset; + // TODO use correct index type! + commandBuffers[currentFrame].bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32); + + uint32_t pushConstant = shape->getTexureAtlasIndex(); + vk::ShaderStageFlags flags(vk::ShaderStageFlagBits::eFragment); + commandBuffers[currentFrame].pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant); + + uint32_t indexCount = shape->getIndexBufferInfo().count; + uint32_t instanceCount = 1; + uint32_t firstIndex = 0; + uint32_t firstInstance = 0; + commandBuffers[currentFrame].drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance); } - - void Renderer2D::fillVertexBufferWithShapes() { - rLog("fillVertexBufferWithShapes"); - if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { - throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); - } - - // create staging buffer - vk::Buffer stagingBuffer; - MemoryInfo stagingBufferMemory; - vk.createBuffer(vertexBufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); - - // fill staging buffer - void* data; - vk::Result result = vk.getDevice().mapMemory(stagingBufferMemory.memory, stagingBufferMemory.offset, vertexBufferSize, NO_MEM_FLAGS, &data); - if (result != vk::Result::eSuccess) { - throw getVkException(result, "Failed to map staging buffer", "Renderer3D::fillVertexBufferWithShapes"); - } - Vertex2D* vdata = reinterpret_cast(data); - size_t offset = 0; - for (auto it = shapes.begin(); it != shapes.end(); it++) { - rLog.log0("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); - memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); - offset += it->getVertices().size(); - } - vk.getDevice().unmapMemory(stagingBufferMemory.memory); - // fill vertex buffer - vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); - vk.destroyBuffer(stagingBuffer, stagingBufferMemory); - } - void Renderer2D::fillIndexBufferWithShapes() { - rLog("fillIndexBufferWithShapes"); - if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { - throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); - } - - // create staging buffer - vk::Buffer stagingBuffer; - MemoryInfo stagingBufferMemory; - vk.createBuffer(indexBufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); - - // fill staging buffer - void* data; - vk::Result result = vk.getDevice().mapMemory(stagingBufferMemory.memory, stagingBufferMemory.offset, indexBufferSize, NO_MEM_FLAGS, &data); - if (result != vk::Result::eSuccess) { - throw getVkException(result, "Failed to map staging buffer", "Renderer2D::fillIndexBufferWithShapes"); - } - uint32_t* idata = reinterpret_cast(data); - size_t offset = 0; - for (auto it = shapes.begin(); it != shapes.end(); it++) { - rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); - memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); - offset += it->getIndices().size(); - } - rLog.log0("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); - vk.getDevice().unmapMemory(stagingBufferMemory.memory); - - // fill index buffer - vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); - vk.destroyBuffer(stagingBuffer, stagingBufferMemory); - - } - - - void Renderer2D::drawShape(Shape* shape) { + void Renderer2D::initializeShape(Shape* shape) { // make indices valid - shape->setIndexOffset(shapesVerticesCount); + /* shape->setIndexOffset(shapesVerticesCount); */ shape->normalizeVertices(vk.getScExtent().width, vk.getScExtent().height); shape->setTextureCoordinates(textureManager); - // object slicing here, need to call virtual setTextureCoordinates before this! - shapes.push_back(*shape); - shapesVerticesCount += shape->getVertices().size(); - shapesIndicesCount += shape->getIndices().size(); + shape->initalizeBufferInfos(shapeBufferManager); } - - void Renderer2D::drawFrame(uint32_t imageIndex) { - commandBuffers[vk.getCurrentFrame()].reset(); - /* recordCommandBuffer(imageIndex, vk.getCurrentFrame()); */ - recordCommandBuffer(imageIndex, vk.getCurrentFrame()); - - } - - } // namespace gz::vlk diff --git a/src/renderer/renderer2D.hpp b/src/renderer/renderer2D.hpp index daa267d..55fc3ab 100644 --- a/src/renderer/renderer2D.hpp +++ b/src/renderer/renderer2D.hpp @@ -11,6 +11,15 @@ namespace gz::vlk { R2Render, }; + /** + * @brief 2D Renderer + * @details + * @section r3d_options Options + * - `RENDERER_2D_RENDER_TO_OWN_IMAGES`: render to own images + * - `RENDERER_2D_COPY_TO_SWAP_CHAIN`: copy the image to the swap chain after rendering it to the own image (swap chain images have to be created with TRANSFER_DST_BIT) + * - `RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES`: enable sampling from the rendered images + * - `RENDERER_2D_RENDER_TO_SWAP_CHAIN`: render directly onto the swap chain + */ class Renderer2D : public Renderer { public: /** @@ -20,6 +29,7 @@ namespace gz::vlk { * -# @ref VulkanInstance::registerSwapChainRecreateCallback "register" @ref swapChainRecreateCallback "swapChain recreation callback" * -# create command buffers * -# create vertex & index buffers + * -# call createDescriptorResources (if `RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES`) * -# call initSwapChainDependantResources */ Renderer2D(VulkanInstance& instance, TextureManager& textureManager); @@ -27,13 +37,30 @@ namespace gz::vlk { * @name Rendering */ /// @{ - void drawShape(Shape* shape); + /** + * @brief Begin rendering a new frame + * @details + * As of now, this does (in the same command buffer from graphicsPool) + * -# begin render pass + * - image layout: vk::ImageLayout::eColorAttachmentOptimal + * - clear image + */ + void beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame); + /** + * @brief End rendering a frame + * -# end render pass + * - image layout: vk::ImageLayout::eTransferSrcOptimal + * -# copy image to swapChain image with same imageIndex (if RENDERER_2D_COPY_TO_SWAP_CHAIN) + */ + void endFrameDraw(uint32_t imageIndex, uint32_t currentFrame); + + void drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame); + void initializeShape(Shape* shape); /** * @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer */ - void fillVertexBufferWithShapes(); - void fillIndexBufferWithShapes(); - void drawFrame(uint32_t imageIndex); + /* void fillVertexBufferWithShapes(); */ + /* void fillIndexBufferWithShapes(); */ /// @} private: @@ -41,28 +68,18 @@ namespace gz::vlk { * @brief Destroy all vulkan objects owned by this object * @details: * Does: + * -# destroy descriptor layout and pool (if `RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES`) * -# call cleanupSwapChainDependantResources() * -# call Renderer::cleanup_() */ void cleanup(); - std::vector shapes; - size_t shapesVerticesCount = 0; - size_t shapesIndicesCount = 0; - /** - * @brief Render everything - * @details - * As of now, this does (in the same command buffer from graphicsPool) - * -# begin render pass - * - image layout: vk::ImageLayout::eColorAttachmentOptimal - * - clear image - * -# bind 2d pipeline, vertex and index buffer - * -# bind texture sampler - * -# draw indexed: draw the shapes from shapes vector - * -# end render pass - * - image layout: vk::ImageLayout::eTransferSrcOptimal - * -# copy image to swapChain image with same imageIndex - */ - void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); + + /* std::vector shapes; */ + /* size_t shapesVerticesCount = 0; */ + /* size_t shapesIndicesCount = 0; */ + BufferManager shapeBufferManager; + + /* void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); */ /** * @brief Create the render image resources @@ -108,6 +125,23 @@ namespace gz::vlk { // PIPELINE PipelineContainer pipelines; + /** + * @name Desciptors + * @details + * + * @section r2d_desc Descriptors + * Renderer2D has one descriptor set. + * @subsection r2d_desc1 Set 1: For sampling from the rendered image + * If `RENDERER_2D_ENABLE_SAMPLE_FROM_RENDER_IMAGES`: + * -0. Image sampler for current render image (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + */ + /// @{ + /** + * @brief Create descriptor pool and layout + */ + void createDescriptorResources(); + /// @} + // SWAPCHAIN RECREATION /** * @brief Swapchain recreation @@ -120,9 +154,10 @@ namespace gz::vlk { /** * @brief Sets up resources that depend on the swap chain or its attributes * @details - * Initializes up: + * Initializes: * - render pass * - images, imageMemory, imageViews + * - image sampling descriptor set (if `RENDERER_2D_SAMPLE_FROM_RENDER_IMAGES`) * - framebuffers * - pipeline */ diff --git a/src/renderer/renderer3D.cpp b/src/renderer/renderer3D.cpp index b833ff9..c6a7de6 100644 --- a/src/renderer/renderer3D.cpp +++ b/src/renderer/renderer3D.cpp @@ -12,8 +12,34 @@ #include #include +#include #include +constexpr uint32_t DS_MVP_BINDING = 0; + +/* #define RENDERER_3D_RENDER_TO_SWAP_CHAIN */ +#define RENDERER_3D_RENDER_TO_OWN_IMAGES +#define RENDERER_3D_COPY_TO_SWAP_CHAIN + +// options sanity checks +#ifdef RENDERER_3D_RENDER_TO_SWAP_CHAIN + #ifdef RENDERER_3D_RENDER_TO_OWN_IMAGES + static_assert(false, "RENDERER_3D_RENDER_TO_SWAP_CHAIN and RENDERER_3D_RENDER_TO_OWN_IMAGES defined at the same time"); + #endif + #ifdef RENDERER_3D_COPY_TO_SWAP_CHAIN + static_assert(false, "RENDERER_3D_RENDER_TO_SWAP_CHAIN and RENDERER_3D_COPY_TO_SWAP_CHAIN defined at the same time"); + #endif + #ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + static_assert(false, "RENDERER_3D_RENDER_TO_SWAP_CHAIN and RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES defined at the same time"); + #endif +#elif defined RENDERER_3D_RENDER_TO_OWN_IMAGES +/* #ifndef RENDERER_3D_COPY_TO_SWAP_CHAIN */ +/* #define RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES */ +/* #endif */ +#else + static_assert(false, "Either RENDERER_3D_RENDER_TO_OWN_IMAGES or RENDERER_3D_RENDER_TO_SWAP_CHAIN must be defined"); +#endif + namespace gz::vlk { // // INIT & CLEANUP @@ -33,8 +59,8 @@ namespace gz::vlk { vk.registerSwapChainRecreateCallback(std::bind(&Renderer3D::swapChainRecreateCallback, this)); vk.createCommandBuffers(commandBuffers); - const size_t vertexCount = 20000; - const size_t indexCount = 10000; + const size_t vertexCount = 500000; + const size_t indexCount = 500000; vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); @@ -43,13 +69,17 @@ namespace gz::vlk { createUniformBuffers(); createDescriptorResources(); + createDescriptorSetMvp(); initSwapChainDependantResources(); VulkanInstance::registerObjectUsingVulkan("Renderer3D", - &renderPass, &imageSampler, &vertexBuffer, &vertexBufferMemory.memory, &indexBuffer, &indexBufferMemory.memory, - &pipelines[R3Render].pipeline, //&pipelines[blendToSC].pipeline, - &descriptorPool, &uboDSL, &sampleFromRenderImageDSL, - &framebuffers, &renderImages, &renderImageViews, &commandBuffers, - &uboDS, &sampleFromRenderImageDS); // missing: &renderImageMemory, + &vertexBuffer, &vertexBufferMemory.memory, + &indexBuffer, &indexBufferMemory.memory, + &renderPass, &pipelines[R3Render].pipeline, //&pipelines[blendToSC].pipeline, + &descriptorPool, + &mvpDSL, &sampleFromRenderImageDSL, + &mvpDS, &sampleFromRenderImageDS, + &imageSampler, &framebuffers, &renderImages, &renderImageViews, // missing: &renderImageMemory, + &commandBuffers); rLog.log1("Created Renderer3D"); } @@ -59,14 +89,16 @@ namespace gz::vlk { for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) { vk.destroyBuffer(uniformBuffers[i], uniformBuffersMemory[i]); } - vk.getDevice().destroyDescriptorSetLayout(uboDSL, NO_ALLOC); + vk.getDevice().destroyDescriptorSetLayout(mvpDSL, NO_ALLOC); vk.getDevice().destroyDescriptorPool(descriptorPool, NO_ALLOC); +#ifdef RENDERER_3D_SAMPLE_FROM_RENDER_IMAGES + vk.getDevice().destroySampler(imageSampler); +#endif + cleanupSwapChainDependantResources(); cleanup_(); } - - // // SWAPCHAIN DEPENDANT // @@ -75,64 +107,12 @@ namespace gz::vlk { createRenderPass(); createDepthImage(); createImageResources(); +#ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + createDescriptorSetSampler(); +#endif vk.createFramebuffers(framebuffers, renderImageViews, renderPass, depthImageView); + createPipelines(); - // VERTEX 3D PIPELINE - std::vector descriptorSetLayouts = { uboDSL, textureManager.getDescriptorSetLayout() }; - auto vertShaderCode = readBinaryFile("shaders/shader.vert.spv"); - auto fragShaderCode = readBinaryFile("shaders/shader.frag.spv"); - - vk::ShaderModule vertShaderModule = vk.createShaderModule(vertShaderCode); - vk::ShaderModule fragShaderModule = vk.createShaderModule(fragShaderCode); - - std::vector shaderStages; - - shaderStages.emplace_back(vk::PipelineShaderStageCreateInfo { - .stage = vk::ShaderStageFlagBits::eVertex, - .module = vertShaderModule, - .pName = "main", - .pSpecializationInfo = nullptr, - }); - shaderStages.emplace_back(vk::PipelineShaderStageCreateInfo { - .stage = vk::ShaderStageFlagBits::eFragment, - .module = fragShaderModule, - .pName = "main", - .pSpecializationInfo = nullptr, - }); - - vk::GraphicsPipelineCreateInfo pipelineCI { - .renderPass = renderPass, - .subpass = 0, - }; - pipelineCI.setStages(shaderStages); - - vk.createGraphicsPipeline(std::move(pipelineCI), &descriptorSetLayouts, true, pipelines[R3Render]); - - vk.getDevice().destroyShaderModule(vertShaderModule, nullptr); - vk.getDevice().destroyShaderModule(fragShaderModule, nullptr); - - - // BLIT TO SWAP CHAIN PIPELINE - /* auto fragShaderCode2 = readBinaryFile("shaders/blend.frag.spv"); */ - /* vk::ShaderModule fragShaderModule2 = vk.createShaderModule(fragShaderCode); */ - - /* std::vector shaderStages2; */ - /* shaderStages2.emplace_back(vk::PipelineShaderStageCreateInfo { */ - /* .stage = vk::ShaderStageFlagBits::eFragment, */ - /* .module = fragShaderModule2, */ - /* .pName = "main", */ - /* .pSpecializationInfo = nullptr, */ - /* }); */ - - /* vk::GraphicsPipelineCreateInfo pipelineCI2 { */ - /* .renderPass = renderPass, */ - /* .subpass = 1, */ - /* }; */ - /* pipelineCI2.setStages(shaderStages2); */ - - /* vk.createGraphicsPipeline(std::move(pipelineCI2), nullptr, false, pipelines[blendToSC]); */ - - /* vk.getDevice().destroyShaderModule(fragShaderModule2, nullptr); */ } @@ -145,7 +125,7 @@ namespace gz::vlk { vk.destroyImageView(depthImageView); vk.destroyImage(depthImage, depthImageMemory); -#ifndef RENDERER_3D_RENDER_TO_SWAP_CHAIN +#ifdef RENDERER_3D_RENDER_TO_OWN_IMAGES for (size_t i = 0; i < renderImages.size(); i++) { vk.destroyImageView(renderImageViews[i]); vk.destroyImage(renderImages[i], renderImageMemory[i]); @@ -166,22 +146,30 @@ namespace gz::vlk { // void Renderer3D::createImageResources() { renderImageViews.resize(vk.getScImages().size()); -#ifndef RENDERER_3D_RENDER_TO_SWAP_CHAIN +#ifdef RENDERER_3D_RENDER_TO_OWN_IMAGES renderImages.resize(vk.getScImages().size()); renderImageMemory.resize(vk.getScImages().size()); - vk::ImageUsageFlags usage= vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc; + vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment; + #ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + usage |= vk::ImageUsageFlagBits::eSampled; + #elif defined RENDERER_3D_COPY_TO_SWAP_CHAIN + usage |= vk::ImageUsageFlagBits::eTransferSrc; + #endif for (size_t i = 0; i < renderImages.size(); i++) { vk.createImage(vk.getScExtent().width, vk.getScExtent().height, vk.getScImageFormat(), vk::ImageTiling::eOptimal, usage, vk::MemoryPropertyFlagBits::eDeviceLocal, renderImages[i], renderImageMemory[i]); vk.createImageView(vk.getScImageFormat(), renderImages[i], renderImageViews[i], vk::ImageAspectFlagBits::eColor); } + #ifdef RENDERER_3D_SAMPLE_FROM_RENDER_IMAGES vk.createTextureSampler(imageSampler); + #endif rLog.log0("createImageResources: created images, views and sampler"); -#else - for (size_t i = 0; i < renderImages.size(); i++) { +#elif defined RENDERER_3D_RENDER_TO_SWAP_CHAIN + for (size_t i = 0; i < renderImageViews.size(); i++) { vk.createImageView(vk.getScImageFormat(), vk.getScImages()[i], renderImageViews[i], vk::ImageAspectFlagBits::eColor); } rLog.log0("createImageResources: created image views"); #endif + assert(renderImageViews.size() == vk.getScImages().size()); } @@ -212,7 +200,13 @@ namespace gz::vlk { .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, .initialLayout = vk::ImageLayout::eUndefined, +#ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + .finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal, +#elif defined RENDERER_3D_COPY_TO_SWAP_CHAIN .finalLayout = vk::ImageLayout::eTransferSrcOptimal, +#else + .finalLayout = vk::ImageLayout::eColorAttachmentOptimal, +#endif }; vk::AttachmentReference2 colorBlendAR { @@ -253,6 +247,7 @@ namespace gz::vlk { .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite, }; +#ifdef RENDERER_3D_COPY_TO_SWAP_CHAIN // image layout transition to transfer dst vk::SubpassDependency2 layoutTransitionSD { .srcSubpass = 0, @@ -263,29 +258,25 @@ namespace gz::vlk { .dstAccessMask = vk::AccessFlagBits::eTransferRead, .dependencyFlags = vk::DependencyFlagBits::eByRegion, }; +#endif /* vk::SubpassDependency2 dependency { */ - /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ - /* dependency.dstSubpass = 0; */ - /* dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests; */ - /* dependency.srcAccessMask = 0; */ - /* dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests; */ - /* dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite; */ + /* .srcSubpass = VK_SUBPASS_EXTERNAL, */ + /* .dstSubpass = 0, */ + /* .srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests, */ + /* .dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests, */ + /* .srcAccessMask = NO_ACC_FLAGS, */ + /* .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite, */ + /* }; */ std::vector attachments = { colorBlendAD, depthAD, }; - std::vector dependencies = { colorAttachmentSD, layoutTransitionSD }; - vk::RenderPassCreateInfo2 renderPassCI { - /* .attachmentCount = static_cast(attachments.size()), */ - /* .pAttachments = attachments.data(), */ - /* .subpassCount = 1, */ - /* .pSubpasses = &subpass, */ - /* .dependencyCount = static_cast(dependencies.size()), */ - /* .pDependencies = dependencies.data(), */ - /* .dependencyCount = 0, */ - /* .pDependencies = nullptr, */ - /* .correlatedViewMaskCount = 0, */ - /* .pCorrelatedViewMasks = nullptr, */ + std::vector dependencies = { colorAttachmentSD +#ifdef RENDERER_3D_COPY_TO_SWAP_CHAIN + , layoutTransitionSD +#endif }; + vk::RenderPassCreateInfo2 renderPassCI; + /* renderPassCI.setCorrelatedViewMasks() */ renderPassCI.setAttachments(attachments); renderPassCI.setDependencies(dependencies); renderPassCI.setSubpasses(subpass); @@ -297,71 +288,153 @@ namespace gz::vlk { } + void Renderer3D::createPipelines() { + // VERTEX 3D PIPELINE + std::vector descriptorSetLayouts = { textureManager.getDescriptorSetLayout(), mvpDSL }; + /* std::vector descriptorSetLayouts = { mvpDSL }; */ + + // specialization constant for sampler2D array length = atlas count + vk::SpecializationMapEntry specME { + .constantID = 0, + .offset = 0, + .size = sizeof(uint32_t), + }; + uint32_t atlasCount = textureManager.getAtlasCount(); + vk::SpecializationInfo specI; + specI.setData(atlasCount); + specI.setMapEntries(specME); + + auto vertShaderCode = readBinaryFile("shaders/shader.vert.spv"); + auto fragShaderCode = readBinaryFile("shaders/shader.frag.spv"); + + vk::ShaderModule vertShaderModule = vk.createShaderModule(vertShaderCode); + vk::ShaderModule fragShaderModule = vk.createShaderModule(fragShaderCode); + + std::vector shaderStages; + + shaderStages.emplace_back(vk::PipelineShaderStageCreateInfo { + .stage = vk::ShaderStageFlagBits::eVertex, + .module = vertShaderModule, + .pName = "main", + .pSpecializationInfo = nullptr, + }); + shaderStages.emplace_back(vk::PipelineShaderStageCreateInfo { + .stage = vk::ShaderStageFlagBits::eFragment, + .module = fragShaderModule, + .pName = "main", + .pSpecializationInfo = &specI, + }); + + // custom rasterizationState with clockwise front face + vk::PipelineRasterizationStateCreateInfo rasterizationStateCI { + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eClockwise, + .depthBiasEnable = VK_FALSE, + .lineWidth = 1.0f, + /* .depthBiasConstantFactor = 0.0f, */ + /* .depthBiasClamp = 0.0f, */ + /* .depthBiasSlopeFactor = 0.0f, */ + }; + vk::GraphicsPipelineCreateInfo pipelineCI { + .pRasterizationState = &rasterizationStateCI, + .renderPass = renderPass, + .subpass = 0, + }; + pipelineCI.setStages(shaderStages); + + std::vector pushConstantRanges; + pushConstantRanges.emplace_back(vk::PushConstantRange{ + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .size = sizeof(uint32_t), + }); + vk.createGraphicsPipeline(std::move(pipelineCI), descriptorSetLayouts, pushConstantRanges, true, pipelines[R3Render]); + + vk.getDevice().destroyShaderModule(vertShaderModule, nullptr); + vk.getDevice().destroyShaderModule(fragShaderModule, nullptr); + + + // BLIT TO SWAP CHAIN PIPELINE + /* auto fragShaderCode2 = readBinaryFile("shaders/blend.frag.spv"); */ + /* vk::ShaderModule fragShaderModule2 = vk.createShaderModule(fragShaderCode); */ + + /* std::vector shaderStages2; */ + /* shaderStages2.emplace_back(vk::PipelineShaderStageCreateInfo { */ + /* .stage = vk::ShaderStageFlagBits::eFragment, */ + /* .module = fragShaderModule2, */ + /* .pName = "main", */ + /* .pSpecializationInfo = nullptr, */ + /* }); */ + + /* vk::GraphicsPipelineCreateInfo pipelineCI2 { */ + /* .renderPass = renderPass, */ + /* .subpass = 1, */ + /* }; */ + /* pipelineCI2.setStages(shaderStages2); */ + + /* vk.createGraphicsPipeline(std::move(pipelineCI2), nullptr, false, pipelines[blendToSC]); */ + + /* vk.getDevice().destroyShaderModule(fragShaderModule2, nullptr); */ + + } + + // // DESCRIPTORS // void Renderer3D::createDescriptorResources() { - rLog("createDescriptorResources: pool", reinterpret_cast(static_cast(descriptorPool)), "vs hpp:", reinterpret_cast(&(*descriptorPool))); // LAYOUTS - std::vector uboDSLB; + std::vector mvpDSLB; // SET 1: uniform buffer object - uboDSLB.emplace_back(vk::DescriptorSetLayoutBinding { - .binding = 0, + mvpDSLB.emplace_back(vk::DescriptorSetLayoutBinding { + .binding = DS_MVP_BINDING, .descriptorType = vk::DescriptorType::eUniformBuffer, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eVertex, - /* .pImmutableSamplers = nullptr, */ }); - /* // SET 1: combined image sampler */ - /* set1bindings.emplace_back(vk::DescriptorSetLayoutBinding { */ - /* .binding = 1, */ - /* .descriptorType = vk::DescriptorType::eCombinedImageSampler, */ - /* .descriptorCount = 1, */ - /* .stageFlags = vk::ShaderStageFlagBits::eFragment, */ - /* /1* .pImmutableSamplers = nullptr, *1/ */ - /* }); */ - vk.createDescriptorSetLayout(uboDSLB, uboDSL); + vk.createDescriptorSetLayout(mvpDSLB, mvpDSL); -#ifndef RENDERER_3D_RENDER_TO_SWAP_CHAIN - // SET 2: combined image sampler - std::vector sampleFromRenderImageDSLB; - sampleFromRenderImageDSLB.emplace_back(vk::DescriptorSetLayoutBinding { - .binding = 0, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eFragment, - }); - vk.createDescriptorSetLayout(sampleFromRenderImageDSLB, sampleFromRenderImageDSL); +#ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + createDescriptorSetLayoutSampler(0); #endif - - // POOL std::vector poolSizes; poolSizes = { { .type = vk::DescriptorType::eUniformBuffer, .descriptorCount = vk.getMaxFramesInFlight() }, -#ifndef RENDERER_3D_RENDER_TO_SWAP_CHAIN - { .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = vk.getMaxFramesInFlight() }, +#ifdef RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES + { .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = static_cast(vk.getScImages().size()) }, #endif }; - vk.createDescriptorPool(poolSizes, vk.getMaxFramesInFlight(), descriptorPool); + uint32_t maxSets = 0; + for (auto& size : poolSizes) { + maxSets += size.descriptorCount; + } + vk.createDescriptorPool(poolSizes, maxSets, descriptorPool); - // SETS - // SET 1 - std::vector layouts1(vk.getMaxFramesInFlight(), uboDSL); - vk.createDescriptorSets(layouts1, descriptorPool, uboDS); + rLog.log0("createDescriptorResources: Created descriptor layouts and pool"); + } + + void Renderer3D::createDescriptorSetMvp() { + std::vector layouts1(vk.getMaxFramesInFlight(), mvpDSL); + vk.createDescriptorSets(layouts1, descriptorPool, mvpDS); + + assert(uniformBuffers.size() == vk.getMaxFramesInFlight()); + assert(mvpDS.size() == vk.getMaxFramesInFlight()); for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) { vk::DescriptorBufferInfo bufferI { .buffer = uniformBuffers[i], .offset = 0, - .range = VK_WHOLE_SIZE, // sizeof(UniformBufferObject), + .range = VK_WHOLE_SIZE, // sizeof(ModelViewProjection), }; vk::WriteDescriptorSet descriptorW{ - .dstSet = uboDS[i], - .dstBinding = 0, + .dstSet = mvpDS[i], + .dstBinding = DS_MVP_BINDING, .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, @@ -372,48 +445,23 @@ namespace gz::vlk { // write 1, copy 0 vk.getDevice().updateDescriptorSets(descriptorW, nullptr); } // for - rLog.log0("createDescriptorResources: Created descriptor layout and sets for uniform buffer"); - -#ifndef RENDERER_3D_RENDER_TO_SWAP_CHAIN - // SET 2: - std::vector layouts2(vk.getMaxFramesInFlight(), sampleFromRenderImageDSL); - vk.createDescriptorSets(layouts2, descriptorPool, sampleFromRenderImageDS); - - for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) { - vk::DescriptorImageInfo imageI { - .sampler = imageSampler, - .imageView = renderImageViews[i], - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - }; - - vk::WriteDescriptorSet descriptorW{ - .dstSet = sampleFromRenderImageDS[i], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &imageI, - }; - // write 1, copy 0 - vk.getDevice().updateDescriptorSets(descriptorW, nullptr); - } // for - rLog.log0("createDescriptorResources: Created descriptor layout and sets for sampling from renderImages"); -#endif + rLog.log0("createDescriptorSetMvp: Created", mvpDS.size(), "descriptor sets for model view projection"); } + // // MODEL // void Renderer3D::loadModel() { // load model into VerticesAndIndices struct rLog.log1("Renderer3D: loadModel: loading model"); - vk.loadModel(settings::MODEL_PATH, model); + /* vk.loadModel(settings::MODEL_PATH, model); */ // TODO use correct type vk::DeviceSize requiredVertexBufferSize = model.vertices.size() * sizeof(Vertex3D); vk::DeviceSize requiredIndexBufferSize = model.indices.size() * sizeof(uint32_t); - if (requiredVertexBufferSize > vertexBufferSize) { throw VkException("Renderer3D::loadModel: vertex buffer too small"); } - if (requiredIndexBufferSize > indexBufferSize) { throw VkException("Renderer3D::loadModel: index buffer too small"); } + if (requiredVertexBufferSize > vertexBufferSize) { throw VkException("Renderer3D::loadModel: vertex buffer too small, required size = " + gz::toString(requiredVertexBufferSize)); } + if (requiredIndexBufferSize > indexBufferSize) { throw VkException("Renderer3D::loadModel: index buffer too small = " + gz::toString(requiredIndexBufferSize)); } rLog.log0("Renderer3D: loadModel: filling vertex buffer"); // copy to vertexBuffer @@ -486,8 +534,11 @@ namespace gz::vlk { commandBuffers[currentFrame].bindIndexBuffer(indexBuffer, NO_OFFSET, vk::IndexType::eUint32); uint32_t firstSet = 0; - std::vector descriptorSets = { uboDS[currentFrame], textureManager.getDescriptorSet() }; - commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R3Render].layout, firstSet, descriptorSets, {}); + std::vector descriptorSets = { textureManager.getDescriptorSet(), mvpDS[currentFrame], }; + std::vector dynamicOffsets; + commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R3Render].layout, firstSet, descriptorSets, dynamicOffsets); + /* commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R3Render].layout, firstSet, mvpDS[currentFrame], dynamicOffsets); */ + /* commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R3Render].layout, firstSet, descriptorSets, dynamicOffsets); */ int instanceCount = 1; int firstIndex = 0; @@ -500,7 +551,9 @@ namespace gz::vlk { /* commandBuffers[currentFrame].draw(6, 1, 0, 0); */ commandBuffers[currentFrame].endRenderPass(); +#ifdef RENDERER_3D_COPY_TO_SWAP_CHAIN vk.copyImageToImage(commandBuffers[currentFrame], renderImages[imageIndex], vk.getScImages()[imageIndex], vk.getScExtent()); +#endif result = commandBuffers[currentFrame].end(); if (result != vk::Result::eSuccess) { rLog.error("Failed to record 3D - command buffer", "VkResult:", result); @@ -520,7 +573,7 @@ namespace gz::vlk { void Renderer3D::createUniformBuffers() { - vk::DeviceSize bufferSize = sizeof(UniformBufferObject); + vk::DeviceSize bufferSize = sizeof(ModelViewProjection); uniformBuffers.resize(vk.getMaxFramesInFlight()); uniformBuffersMemory.resize(vk.getMaxFramesInFlight()); @@ -537,21 +590,21 @@ namespace gz::vlk { float time = std::chrono::duration(currentTime - startTime).count(); // TODO use push constant instead of ubo - UniformBufferObject ubo{}; - ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v / 2, glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.projection = glm::perspective(glm::radians(45.0f), static_cast(vk.getScExtent().width) / vk.getScExtent().height, 1.0f, 10.0f); + ModelViewProjection mvp{}; + mvp.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v / 2, glm::vec3(0.0f, 0.0f, 1.0f)); + mvp.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + mvp.projection = glm::perspective(glm::radians(45.0f), static_cast(vk.getScExtent().width) / vk.getScExtent().height, 1.0f, 10.0f); /* ubo.model = glm::mat4(1); */ /* ubo.view = glm::mat4(1); */ /* ubo.projection = glm::mat4(1); */ /* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */ MemoryInfo& uniformBufferMI = uniformBuffersMemory[vk.getCurrentFrame()]; void* data; - vk::Result result = vk.getDevice().mapMemory(uniformBufferMI.memory, uniformBufferMI.offset, sizeof(ubo), NO_MEM_FLAGS, &data); + vk::Result result = vk.getDevice().mapMemory(uniformBufferMI.memory, uniformBufferMI.offset, sizeof(mvp), NO_MEM_FLAGS, &data); if (result != vk::Result::eSuccess) { throw getVkException(result, "Failed to map staging buffer", "Renderer3D::updateUniformBuffer"); } - memcpy(data, &ubo, sizeof(ubo)); + memcpy(data, &mvp, sizeof(mvp)); vk.getDevice().unmapMemory(uniformBufferMI.memory); } diff --git a/src/renderer/renderer3D.hpp b/src/renderer/renderer3D.hpp index c86b04e..36fd12e 100644 --- a/src/renderer/renderer3D.hpp +++ b/src/renderer/renderer3D.hpp @@ -6,7 +6,7 @@ #include "vulkan_util.hpp" namespace gz::vlk { - struct UniformBufferObject { + struct ModelViewProjection { alignas(16) glm::mat4 model; alignas(16) glm::mat4 view; alignas(16) glm::mat4 projection; @@ -16,6 +16,15 @@ namespace gz::vlk { R3Render, }; + /** + * @brief 3D Renderer + * @details + * @section r3d_options Options + * - `RENDERER_3D_RENDER_TO_OWN_IMAGES`: render to own images + * - `RENDERER_3D_COPY_TO_SWAP_CHAIN`: copy the image to the swap chain after rendering it to the own image (swap chain images have to be created with TRANSFER_DST_BIT) + * - `RENDERER_3D_SAMPLE_FROM_RENDER_IMAGES`: enable sampling from the rendered images + * - `RENDERER_3D_RENDER_TO_SWAP_CHAIN`: render directly onto the swap chain + */ class Renderer3D : public Renderer { public: /** @@ -55,7 +64,8 @@ namespace gz::vlk { * Else: * - renderImages are images on renderImageMemory * - renderImageViews are views to renderImages - * - renderImageSampler is a combined image sampler + * - If `RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES` + * - renderImageSampler is a combined image sampler */ void createImageResources(); @@ -87,7 +97,9 @@ namespace gz::vlk { * - initialLayout = vk::ImageLayout::eColorAttachmentOptimal * - finalLayout = vk::ImageLayout::eColorAttachmentOptimal * - stencil load/store = dont care + * * @subsection r3p_subpass2 Subpass 2: Blitting render image onto swap chain image + * @todo remove * Attributes: * - Pipeline: `Pipelines3D::blendToSC` * - Descriptor layouts: `@ref r3p_desc2 "descriptorSet2Layout"` @@ -110,6 +122,7 @@ namespace gz::vlk { std::vector framebuffers; // PIPELINE PipelineContainer pipelines; + void createPipelines(); /** * @name Desciptors @@ -119,22 +132,28 @@ namespace gz::vlk { * Renderer3D has two descriptor sets, both allocated from the same pool. * @subsection r3d_desc1 Set 1: For rendering * Bindings: - * -0. UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER) - * @subsection r3d_desc2 Set 2: For sampling from the rendered image (only if not `RENDERER_3D_RENDER_TO_SWAP_CHAIN) + * -0. ModelViewProjection (DESCRIPTOR_TYPE_UNIFORM_BUFFER) + * @subsection r3d_desc2 Set 2: For sampling from the rendered image + * If `RENDERER_3D_ENABLE_SAMPLE_FROM_RENDER_IMAGES`: * -0. Image sampler for current render image (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) */ /// @{ - vk::DescriptorPool descriptorPool; - - vk::DescriptorSetLayout uboDSL; - std::vector uboDS; - - vk::DescriptorSetLayout sampleFromRenderImageDSL; - std::vector sampleFromRenderImageDS; + vk::DescriptorSetLayout mvpDSL; + std::vector mvpDS; /** - * @brief Create @ref r3d_desc "all descritptor resources" + * @brief Create descriptor set layouts and pool */ void createDescriptorResources(); + /** + * @brief Create @ref r3d_desc1 "model view projection descriptor sets" + */ + void createDescriptorSetMvp(); + /** + * @brief Create @ref r3d_desc2 "sampler from render image descriptor sets" + * @details + * Swap chain dependant + */ + void createDescriptorSetSampler(); /// @} /** @@ -150,8 +169,9 @@ namespace gz::vlk { * @details * Initializes: * - @ref createRenderPass() "render pass" - * - @ref createImages() "images and imageViews" * - @ref createDepthImage() "depth image and view" + * - @ref createImageResources() "images and imageViews" + * - @ref createDescriptorSetSampler() "descriptors for sampling from render image" * - @ref VulkanInstance::createFramebuffers() "framebuffers" * - pipelines: * - R3Render @@ -175,11 +195,12 @@ namespace gz::vlk { * -# destroy descriptor set layout and pool * -# call cleanupSwapChainDependantResources() * -# call Renderer::cleanup_() + * -# destroy image sampler (if `RENDERER_3D_SAMPLE_FROM_RENDER_IMAGES`) */ void cleanup(); void loadModel(); - VerticesAndIndices model; + VerticesAndIndices model; Log rLog; };