mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
downsample & alpha shaderchain
This commit is contained in:
parent
0345ec9e69
commit
c3e1d0ded9
8 changed files with 231 additions and 16 deletions
|
|
@ -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<Core::ShaderModule> shaderModules{4};
|
||||
std::vector<Core::Pipeline> pipelines{4};
|
||||
std::vector<Core::DescriptorSet> descriptorSets{4};
|
||||
|
||||
Core::Image inImage;
|
||||
|
||||
std::vector<Core::Image> tempTex1{2}; // half-size
|
||||
std::vector<Core::Image> tempTex2{2}; // half-size
|
||||
std::vector<Core::Image> tempTex3{4}; // quarter-size
|
||||
|
||||
std::vector<Core::Image> outImages{4}; // quarter-size
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ALPHA_HPP
|
||||
|
|
@ -57,7 +57,7 @@ namespace Vulkan::Shaderchains {
|
|||
|
||||
Core::Image inImage;
|
||||
|
||||
std::vector<Core::Image> outImages;
|
||||
std::vector<Core::Image> outImages{7};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ namespace Vulkan::Utils {
|
|||
///
|
||||
void insertBarrier(
|
||||
const Vulkan::Core::CommandBuffer& buffer,
|
||||
std::vector<Vulkan::Core::Image> r2wImages,
|
||||
std::vector<Vulkan::Core::Image> w2rImages);
|
||||
std::vector<Vulkan::Core::Image> w2rImages,
|
||||
std::vector<Vulkan::Core::Image> r2wImages);
|
||||
|
||||
///
|
||||
/// Upload a DDS file to a Vulkan image.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
10
src/main.cpp
10
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 <renderdoc_app.h>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace Vulkan;
|
||||
|
||||
|
|
@ -52,6 +53,11 @@ int main() {
|
|||
// create the shaderchains
|
||||
Shaderchains::Downsample downsample(device, descriptorPool, inputImage);
|
||||
|
||||
std::vector<Shaderchains::Alpha> 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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using namespace Vulkan;
|
|||
|
||||
void Utils::insertBarrier(
|
||||
const Core::CommandBuffer& buffer,
|
||||
std::vector<Core::Image> r2wImages,
|
||||
std::vector<Core::Image> w2rImages) {
|
||||
std::vector<Core::Image> w2rImages,
|
||||
std::vector<Core::Image> r2wImages) {
|
||||
std::vector<VkImageMemoryBarrier2> 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<uint8_t> code(static_cast<size_t>(size));
|
||||
size -= 124 + 4; // dds header and magic bytes
|
||||
std::vector<char> code(static_cast<size_t>(size));
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
if (!file.read(reinterpret_cast<char*>(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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue