diff --git a/include/shaderchains/alpha.hpp b/include/shaderchains/alpha.hpp index e69de29..84a1b7a 100644 --- a/include/shaderchains/alpha.hpp +++ b/include/shaderchains/alpha.hpp @@ -0,0 +1,67 @@ +#ifndef ALPHA_HPP +#define ALPHA_HPP + +#include "core/commandbuffer.hpp" +#include "core/descriptorpool.hpp" +#include "core/descriptorset.hpp" +#include "core/image.hpp" +#include "core/pipeline.hpp" +#include "core/shadermodule.hpp" +#include "device.hpp" + +namespace Vulkan::Shaderchains { + + /// + /// Shader chain alpha. + /// + /// Takes an 8-bit R texture creates four quarter-sized 8-bit RGBA textures. + /// + class Alpha { + public: + /// + /// Initialize the shaderchain. + /// + /// @param device The Vulkan device to create the resources on. + /// @param pool The descriptor pool to allocate in. + /// @param inImage The input texture to process + /// + /// @throws ls::vulkan_error if resource creation fails. + /// + Alpha(const Device& device, const Core::DescriptorPool& pool, + const Core::Image& inImage); + + /// + /// Dispatch the shaderchain. + /// + /// @param buf The command buffer to use for dispatching. + /// + /// @throws std::logic_error if the command buffer is not recording. + /// + void Dispatch(const Core::CommandBuffer& buf); + + /// Get the output images. + [[nodiscard]] const auto& getOutImages() const { return this->outImages; } + + /// Trivially copyable, moveable and destructible + Alpha(const Alpha&) noexcept = default; + Alpha& operator=(const Alpha&) noexcept = default; + Alpha(Alpha&&) noexcept = default; + Alpha& operator=(Alpha&&) noexcept = default; + ~Alpha() = default; + private: + std::vector shaderModules{4}; + std::vector pipelines{4}; + std::vector descriptorSets{4}; + + Core::Image inImage; + + std::vector tempTex1{2}; // half-size + std::vector tempTex2{2}; // half-size + std::vector tempTex3{4}; // quarter-size + + std::vector outImages{4}; // quarter-size + }; + +} + +#endif // ALPHA_HPP diff --git a/include/shaderchains/downsample.hpp b/include/shaderchains/downsample.hpp index 9584e48..21cc8c3 100644 --- a/include/shaderchains/downsample.hpp +++ b/include/shaderchains/downsample.hpp @@ -57,7 +57,7 @@ namespace Vulkan::Shaderchains { Core::Image inImage; - std::vector outImages; + std::vector outImages{7}; }; } diff --git a/include/utils.hpp b/include/utils.hpp index 930a31e..aa968b7 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -22,8 +22,8 @@ namespace Vulkan::Utils { /// void insertBarrier( const Vulkan::Core::CommandBuffer& buffer, - std::vector r2wImages, - std::vector w2rImages); + std::vector w2rImages, + std::vector r2wImages); /// /// Upload a DDS file to a Vulkan image. diff --git a/src/core/sampler.cpp b/src/core/sampler.cpp index 5612925..0bb2e1f 100644 --- a/src/core/sampler.cpp +++ b/src/core/sampler.cpp @@ -13,7 +13,7 @@ Sampler::Sampler(const Device& device, VkSamplerAddressMode mode) { .addressModeU = mode, .addressModeV = mode, .addressModeW = mode, - .maxLod = VK_LOD_CLAMP_NONE + .maxLod = 15.99609F }; VkSampler samplerHandle{}; auto res = vkCreateSampler(device.handle(), &desc, nullptr, &samplerHandle); diff --git a/src/main.cpp b/src/main.cpp index 84cf91e..f16aedb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "core/image.hpp" #include "device.hpp" #include "instance.hpp" +#include "shaderchains/alpha.hpp" #include "shaderchains/downsample.hpp" #include "utils.hpp" @@ -13,7 +14,7 @@ #include #include #include -#include +#include using namespace Vulkan; @@ -52,6 +53,11 @@ int main() { // create the shaderchains Shaderchains::Downsample downsample(device, descriptorPool, inputImage); + std::vector alphas; + alphas.reserve(7); + for (size_t i = 0; i < 7; ++i) + alphas.emplace_back(device, descriptorPool, downsample.getOutImages().at(i)); + // start the rendering pipeline if (rdoc) rdoc->StartFrameCapture(nullptr, nullptr); @@ -60,6 +66,8 @@ int main() { commandBuffer.begin(); downsample.Dispatch(commandBuffer); + for (size_t i = 0; i < 7; i++) + alphas.at(6 - i).Dispatch(commandBuffer); // finish the rendering pipeline commandBuffer.end(); diff --git a/src/shaderchains/alpha.cpp b/src/shaderchains/alpha.cpp index e69de29..b99c4b1 100644 --- a/src/shaderchains/alpha.cpp +++ b/src/shaderchains/alpha.cpp @@ -0,0 +1,141 @@ +#include "shaderchains/alpha.hpp" +#include "utils.hpp" + +using namespace Vulkan::Shaderchains; + +Alpha::Alpha(const Device& device, const Core::DescriptorPool& pool, + const Core::Image& inImage) : inImage(inImage) { + this->shaderModules = {{ + Core::ShaderModule(device, "rsc/shaders/alpha/0.spv", + { { 1, VK_DESCRIPTOR_TYPE_SAMPLER }, + { 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE }, + { 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }), + Core::ShaderModule(device, "rsc/shaders/alpha/1.spv", + { { 1, VK_DESCRIPTOR_TYPE_SAMPLER }, + { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE }, + { 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }), + Core::ShaderModule(device, "rsc/shaders/alpha/2.spv", + { { 1, VK_DESCRIPTOR_TYPE_SAMPLER }, + { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE }, + { 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }), + Core::ShaderModule(device, "rsc/shaders/alpha/3.spv", + { { 1, VK_DESCRIPTOR_TYPE_SAMPLER }, + { 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE }, + { 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }) + }}; + for (size_t i = 0; i < 4; i++) { + this->pipelines.at(i) = Core::Pipeline(device, + this->shaderModules.at(i)); + this->descriptorSets.at(i) = Core::DescriptorSet(device, pool, + this->shaderModules.at(i)); + } + + auto extent = inImage.getExtent(); + auto halfWidth = (extent.width + 1) >> 1; + auto halfHeight = (extent.height + 1) >> 1; + auto quarterWidth = (extent.width + 3) >> 2; + auto quarterHeight = (extent.height + 3) >> 2; + + for (size_t i = 0; i < 2; i++) { + this->tempTex1.at(i) = Core::Image(device, + { halfWidth, halfHeight }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT); + this->tempTex2.at(i) = Core::Image(device, + { halfWidth, halfHeight }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT); + } + for (size_t i = 0; i < 4; i++) { + this->tempTex3.at(i) = Core::Image(device, + { quarterWidth, quarterHeight }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT); + this->outImages.at(i) = Core::Image(device, + { quarterWidth, quarterHeight }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT); + } + + this->descriptorSets.at(0).update(device) + .add(VK_DESCRIPTOR_TYPE_SAMPLER, Globals::samplerClampBorder) + .add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, inImage) + .add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempTex1) + .build(); + this->descriptorSets.at(1).update(device) + .add(VK_DESCRIPTOR_TYPE_SAMPLER, Globals::samplerClampBorder) + .add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempTex1) + .add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempTex2) + .build(); + this->descriptorSets.at(2).update(device) + .add(VK_DESCRIPTOR_TYPE_SAMPLER, Globals::samplerClampBorder) + .add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempTex2) + .add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempTex3) + .build(); + this->descriptorSets.at(3).update(device) + .add(VK_DESCRIPTOR_TYPE_SAMPLER, Globals::samplerClampBorder) + .add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempTex3) + .add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImages) + .build(); +} + +void Alpha::Dispatch(const Core::CommandBuffer& buf) { + const auto halfExtent = this->tempTex1.at(0).getExtent(); + const auto quarterExtent = this->tempTex3.at(0).getExtent(); + + // first pass + Utils::insertBarrier( + buf, + { this->inImage }, + this->tempTex1 + ); + + this->pipelines.at(0).bind(buf); + this->descriptorSets.at(0).bind(buf, this->pipelines.at(0)); + + uint32_t threadsX = (halfExtent.width + 7) >> 3; + uint32_t threadsY = (halfExtent.height + 7) >> 3; + buf.dispatch(threadsX, threadsY, 1); + + // second pass + Utils::insertBarrier( + buf, + this->tempTex1, + this->tempTex2 + ); + + this->pipelines.at(1).bind(buf); + this->descriptorSets.at(1).bind(buf, this->pipelines.at(1)); + + buf.dispatch(threadsX, threadsY, 1); + + // third pass + Utils::insertBarrier( + buf, + this->tempTex2, + this->tempTex3 + ); + + this->pipelines.at(2).bind(buf); + this->descriptorSets.at(2).bind(buf, this->pipelines.at(2)); + + threadsX = (quarterExtent.width + 7) >> 3; + threadsY = (quarterExtent.height + 7) >> 3; + buf.dispatch(threadsX, threadsY, 1); + + // fourth pass + Utils::insertBarrier( + buf, + this->tempTex3, + this->outImages + ); + + this->pipelines.at(3).bind(buf); + this->descriptorSets.at(3).bind(buf, this->pipelines.at(3)); + + buf.dispatch(threadsX, threadsY, 1); +} diff --git a/src/shaderchains/downsample.cpp b/src/shaderchains/downsample.cpp index 7614100..d6dc2ea 100644 --- a/src/shaderchains/downsample.cpp +++ b/src/shaderchains/downsample.cpp @@ -20,7 +20,6 @@ Downsample::Downsample(const Device& device, const Core::DescriptorPool& pool, auto extent = inImage.getExtent(); // create output images - this->outImages.resize(7); for (size_t i = 0; i < 7; i++) this->outImages.at(i) = Core::Image(device, { extent.width >> i, extent.height >> i }, @@ -44,8 +43,8 @@ void Downsample::Dispatch(const Core::CommandBuffer& buf) { Utils::insertBarrier( buf, - this->outImages, - { this->inImage } + { this->inImage }, + this->outImages ); this->pipeline.bind(buf); diff --git a/src/utils.cpp b/src/utils.cpp index 079b250..bb03b3c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -8,8 +8,8 @@ using namespace Vulkan; void Utils::insertBarrier( const Core::CommandBuffer& buffer, - std::vector r2wImages, - std::vector w2rImages) { + std::vector w2rImages, + std::vector r2wImages) { std::vector barriers(r2wImages.size() + w2rImages.size()); size_t index = 0; @@ -60,16 +60,16 @@ void Utils::insertBarrier( void Utils::uploadImage(const Device& device, const Core::CommandPool& commandPool, Core::Image& image, const std::string& path) { // read image bytecode - std::ifstream file(path, std::ios::ate | std::ios::binary); - if (!file) + std::ifstream file(path.data(), std::ios::binary | std::ios::ate); + if (!file.is_open()) throw std::system_error(errno, std::generic_category(), "Failed to open image: " + path); std::streamsize size = file.tellg(); - size -= 124 - 4; // dds header and magic bytes - std::vector code(static_cast(size)); + size -= 124 + 4; // dds header and magic bytes + std::vector code(static_cast(size)); - file.seekg(0, std::ios::beg); - if (!file.read(reinterpret_cast(code.data()), size)) + file.seekg(124 + 4, std::ios::beg); + if (!file.read(code.data(), size)) throw std::system_error(errno, std::generic_category(), "Failed to read image: " + path); file.close();