diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 4f9468a2a738502d8459e4e136f566fa507d8b6e..af186fe222fc17ac7bc676e4f2c9ed8f82e04c00 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -62,9 +62,6 @@ namespace vkcv Context m_Context; SwapChain m_swapchain; - std::vector<vk::ImageView> m_swapchainImageViews; - std::vector<vk::Image> m_swapchainImages; - std::vector<vk::ImageLayout> m_swapchainImageLayouts; const Window& m_window; std::unique_ptr<PassManager> m_PassManager; @@ -81,9 +78,7 @@ namespace vkcv std::function<void(int, int)> e_resizeHandle; - static std::vector<vk::ImageView> createImageViews( Context &context, SwapChain& swapChain); - - void recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout); + static std::vector<vk::ImageView> createSwapchainImageViews( Context &context, SwapChain& swapChain); public: /** diff --git a/projects/voxelization/resources/shaders/gammaCorrection.comp b/projects/voxelization/resources/shaders/gammaCorrection.comp new file mode 100644 index 0000000000000000000000000000000000000000..b9886ef275bb37caf1089d4362d45d6d56a5a060 --- /dev/null +++ b/projects/voxelization/resources/shaders/gammaCorrection.comp @@ -0,0 +1,16 @@ +#version 440 + +layout(set=0, binding=0, r8) uniform image2D sceneImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(sceneImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(sceneImage, uv).rgb; + vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f)); + imageStore(sceneImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index fd2a0bc687c5c38eae47fc0538af309c66884ac3..f832dd3d023f1daf66b5090d30b5986e56f16688 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -357,6 +357,16 @@ int main(int argc, const char** argv) { resetVoxelWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(0, voxelImage.getHandle()) }; core.writeDescriptorSet(resetVoxelDescriptorSet, resetVoxelWrites); + // gamma correction compute shader + vkcv::ShaderProgram gammaCorrectionProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + gammaCorrectionProgram.addShader(shaderStage, path); + }); + vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram, + { core.getDescriptorSet(gammaCorrectionDescriptorSet).layout }); + // prepare descriptor sets for drawcalls std::vector<vkcv::Image> sceneImages; std::vector<vkcv::DescriptorSetHandle> descriptorSets; @@ -418,7 +428,12 @@ int main(int argc, const char** argv) { auto end = std::chrono::system_clock::now(); auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - + + // update descriptor sets which use swapchain image + vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; + gammaCorrectionDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(0, swapchainInput) }; + core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites); + start = end; cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); @@ -559,6 +574,22 @@ int main(int argc, const char** argv) { renderTargets); } + const uint32_t gammaCorrectionLocalGroupSize = 8; + const uint32_t gammaCorrectionDispatchCount[3] = { + glm::ceil(windowWidth / float(gammaCorrectionLocalGroupSize)), + glm::ceil(windowHeight / float(gammaCorrectionLocalGroupSize)), + 1 + }; + + core.prepareImageForStorage(cmdStream, swapchainInput); + + core.recordComputeDispatchToCmdStream( + cmdStream, + gammaCorrectionPipeline, + gammaCorrectionDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(nullptr, 0)); + // present and end core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index e76a4111678ee5e9d506708a845c2a134d668d3a..1531bc2b77fb24db407e1f31d3bac200fe4c1bd3 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -37,8 +37,7 @@ namespace vkcv SwapChain swapChain = SwapChain::create(window, context); - std::vector<vk::ImageView> imageViews; - imageViews = createImageViews( context, swapChain); + std::vector<vk::ImageView> swapchainImageViews = createSwapchainImageViews( context, swapChain); const auto& queueManager = context.getQueueManager(); @@ -47,7 +46,7 @@ namespace vkcv const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet); const auto defaultSyncResources = createSyncResources(context.getDevice()); - return Core(std::move(context) , window, swapChain, imageViews, commandResources, defaultSyncResources); + return Core(std::move(context) , window, swapChain, swapchainImageViews, commandResources, defaultSyncResources); } const Context &Core::getContext() const @@ -55,12 +54,11 @@ namespace vkcv return m_Context; } - Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, + Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> swapchainImageViews, const CommandResources& commandResources, const SyncResources& syncResources) noexcept : m_Context(std::move(context)), m_window(window), m_swapchain(swapChain), - m_swapchainImageViews(imageViews), m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}, m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), @@ -81,15 +79,17 @@ namespace vkcv m_swapchain.signalSwapchainRecreation(); }); - m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); + const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + m_ImageManager->setSwapchainImages( + swapchainImages, + swapchainImageViews, + swapChain.getExtent().width, + swapChain.getExtent().height, + swapChain.getSwapchainFormat()); } Core::~Core() noexcept { m_Context.getDevice().waitIdle(); - for (auto image : m_swapchainImageViews) { - m_Context.m_Device.destroyImageView(image); - } destroyCommandResources(m_Context.getDevice(), m_CommandResources); destroySyncResources(m_Context.getDevice(), m_SyncResources); @@ -145,16 +145,12 @@ namespace vkcv bool Core::beginFrame(uint32_t& width, uint32_t& height) { if (m_swapchain.shouldUpdateSwapchain()) { m_Context.getDevice().waitIdle(); - - for (auto image : m_swapchainImageViews) - m_Context.m_Device.destroyImageView(image); - + m_swapchain.updateSwapchain(m_Context, m_window); - m_swapchainImageViews = createImageViews(m_Context, m_swapchain); - m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + const auto swapchainViews = createSwapchainImageViews(m_Context, m_swapchain); + const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - m_swapchainImageLayouts.clear(); - m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); + m_ImageManager->setSwapchainImages(swapchainImages, swapchainViews, width, height, m_swapchain.getSwapchainFormat()); } if (acquireSwapchainImage() != Result::SUCCESS) { @@ -170,6 +166,8 @@ namespace vkcv width = extent.width; height = extent.height; + m_ImageManager->setCurrentSwapchainImageIndex(m_currentSwapchainImageIndex); + return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max()); } @@ -212,23 +210,16 @@ namespace vkcv const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); - const vk::ImageView swapchainImageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; - std::vector<vk::ImageView> attachmentsViews; for (const ImageHandle handle : renderTargets) { vk::ImageView targetHandle; const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); - if (handle.isSwapchainImage()) { - recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::eColorAttachmentOptimal); - targetHandle = m_swapchainImageViews[m_currentSwapchainImageIndex]; - } - else { - targetHandle = m_ImageManager->getVulkanImageView(handle); - const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); - const vk::ImageLayout targetLayout = - isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; - m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); - } + + targetHandle = m_ImageManager->getVulkanImageView(handle); + const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); + const vk::ImageLayout targetLayout = + isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; + m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); attachmentsViews.push_back(targetHandle); } @@ -458,7 +449,7 @@ namespace vkcv return m_DescriptorManager->getDescriptorSet(handle); } - std::vector<vk::ImageView> Core::createImageViews( Context &context, SwapChain& swapChain){ + std::vector<vk::ImageView> Core::createSwapchainImageViews( Context &context, SwapChain& swapChain){ std::vector<vk::ImageView> imageViews; std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); imageViews.reserve( swapChainImages.size() ); @@ -486,20 +477,11 @@ namespace vkcv return imageViews; } - void Core::recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout) { - auto& imageLayout = m_swapchainImageLayouts[m_currentSwapchainImageIndex]; - const auto transitionBarrier = createSwapchainImageLayoutTransitionBarrier( - m_swapchainImages[m_currentSwapchainImageIndex], - imageLayout, - newLayout); - recordImageBarrier(cmdBuffer, transitionBarrier); - imageLayout = newLayout; - } - - void Core::prepareSwapchainImageForPresent(const CommandStreamHandle handle) { - m_CommandStreamManager->recordCommandsToStream(handle, [&](vk::CommandBuffer cmdBuffer) { - recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::ePresentSrcKHR); - }); + void Core::prepareSwapchainImageForPresent(const CommandStreamHandle cmdStream) { + auto swapchainHandle = ImageHandle::createSwapchainImageHandle(); + recordCommandsToStream(cmdStream, [swapchainHandle, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(swapchainHandle, vk::ImageLayout::ePresentSrcKHR, cmdBuffer); + }, nullptr); } void Core::prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image) { diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index 2f713bbd07688fa63d80f3d9e11b614f1d15da39..fff98dc5b56d0ad085c6ebc68acf7230223d9cd1 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -69,6 +69,8 @@ namespace vkcv { for (uint64_t id = 0; id < m_images.size(); id++) { destroyImageById(id); } + for (const auto swapchainImage : m_swapchainImages) + m_core->getContext().getDevice().destroy(swapchainImage.m_view); } bool isDepthImageFormat(vk::Format format) { @@ -210,8 +212,12 @@ namespace vkcv { } vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + + if (handle.isSwapchainImage()) { + m_swapchainImages[m_currentSwapchainInputImage].m_handle; + } + const uint64_t id = handle.getId(); - if (id >= m_images.size()) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; @@ -223,8 +229,13 @@ namespace vkcv { } vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { + + if (handle.isSwapchainImage()) { + vkcv_log(LogLevel::ERROR, "Swapchain image has no memory"); + return nullptr; + } + const uint64_t id = handle.getId(); - if (id >= m_images.size()) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; @@ -236,27 +247,31 @@ namespace vkcv { } vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle) const { - const uint64_t id = handle.getId(); + if (handle.isSwapchainImage()) { + return m_swapchainImages[m_currentSwapchainInputImage].m_view; + } + + const uint64_t id = handle.getId(); if (id >= m_images.size()) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } - auto& image = m_images[id]; - - return image.m_view; + return m_images[id].m_view; } void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) { - const uint64_t id = handle.getId(); + uint64_t id = handle.getId(); - if (id >= m_images.size()) { + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); SubmitInfo submitInfo; @@ -285,13 +300,14 @@ namespace vkcv { vk::CommandBuffer cmdBuffer) { const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); - if (id >= m_images.size()) { + if (id >= m_images.size() && !isSwapchainImage) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); recordImageBarrier(cmdBuffer, transitionBarrier); image.m_layout = newLayout; @@ -302,13 +318,14 @@ namespace vkcv { vk::CommandBuffer cmdBuffer) { const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); - if (id >= m_images.size()) { + if (id >= m_images.size() && !isSwapchainImage) { std::cerr << "Error: ImageManager::recordImageMemoryBarrier invalid handle" << std::endl; return; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; const auto transitionBarrier = createImageLayoutTransitionBarrier(image, image.m_layout); recordImageBarrier(cmdBuffer, transitionBarrier); } @@ -329,6 +346,11 @@ namespace vkcv { { const uint64_t id = handle.getId(); + if (handle.isSwapchainImage()) { + vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled"); + return; + } + if (id >= m_images.size()) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return; @@ -402,39 +424,42 @@ namespace vkcv { uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { const uint64_t id = handle.getId(); - - if (id >= m_images.size()) { + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; return image.m_width; } uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const { const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); - if (id >= m_images.size()) { + if (id >= m_images.size() && !isSwapchainImage) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; return image.m_height; } uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const { const uint64_t id = handle.getId(); - - if (id >= m_images.size()) { + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } - auto& image = m_images[id]; + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; return image.m_depth; } @@ -469,13 +494,32 @@ namespace vkcv { vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const { const uint64_t id = handle.getId(); + const bool isSwapchainFormat = handle.isSwapchainImage(); - if (id >= m_images.size()) { + if (id >= m_images.size() && !isSwapchainFormat) { vkcv_log(LogLevel::ERROR, "Invalid handle"); return vk::Format::eUndefined; } - return m_images[id].m_format; + return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format; + } + + void ImageManager::setCurrentSwapchainImageIndex(int index) { + m_currentSwapchainInputImage = index; + } + + void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, + uint32_t width, uint32_t height, vk::Format format) { + + // destroy old views + for (auto image : m_swapchainImages) + m_core->getContext().getDevice().destroyImageView(image.m_view); + + assert(images.size() == views.size()); + m_swapchainImages.clear(); + for (int i = 0; i < images.size(); i++) { + m_swapchainImages.push_back(Image(images[i], nullptr, views[i], width, height, 1, format, 1, 1)); + } } } \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index 6b3bea33f902db94567cdefa3d3c90ea78054876..7bb4cc7ef7951a8677c19f589bd7948dbcedb25d 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -41,6 +41,8 @@ namespace vkcv { vk::Format format, uint32_t layers, uint32_t levels); + + Image(); }; private: @@ -48,6 +50,8 @@ namespace vkcv { BufferManager& m_bufferManager; std::vector<Image> m_images; + std::vector<Image> m_swapchainImages; + int m_currentSwapchainInputImage; ImageManager(BufferManager& bufferManager) noexcept; @@ -103,5 +107,10 @@ namespace vkcv { [[nodiscard]] vk::Format getImageFormat(const ImageHandle& handle) const; + + void setCurrentSwapchainImageIndex(int index); + void setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, + uint32_t width, uint32_t height, vk::Format format); + }; } \ No newline at end of file diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/SwapChain.cpp index b787536b66cfd802dfd435a773a584c875eeb391..a6e1f2a141be71f7f296ad66a5a64341f370b379 100644 --- a/src/vkcv/SwapChain.cpp +++ b/src/vkcv/SwapChain.cpp @@ -186,7 +186,7 @@ namespace vkcv chosenSurfaceFormat.colorSpace, // imageColorSpace chosenExtent, // imageExtent 1, // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers" - vk::ImageUsageFlagBits::eColorAttachment, // imageUsage TODO: what attachments? only color? depth? + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, // imageUsage TODO: what attachments? only color? depth? vk::SharingMode::eExclusive, // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues" 0, // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT nullptr, // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT @@ -227,7 +227,7 @@ namespace vkcv m_SwapchainColorSpace, extent2D, 1, - vk::ImageUsageFlagBits::eColorAttachment, + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, vk::SharingMode::eExclusive, 0, nullptr,