Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
The current architecture where textures, where are uploaded later, means that the DescriptorSetLayout can't be created until that point.

Some way to create descriptor layouts without having the imageinfos at hand would be good.
  • Loading branch information
Hjaltesorgenfrei committed Sep 30, 2022
1 parent 514f468 commit 381a6de
Show file tree
Hide file tree
Showing 8 changed files with 513 additions and 94 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,24 @@ Some ideas were also taken from [Zeux's blog](https://zeux.io/2020/02/27/writing
- [ ] Create a style to autoformat with
- [ ] Move swap chain to its own class
- [ ] Fix the hash for checking if vertices are equal.

### Descriptor Layout Idea

The DescriptorSet setup could work by creating DescriptorSetLayouts before.
Then validating the created DescriptorSets against a provided layout.
The DescriptorSetLayout could also have a builder
`.addBinding(uint32_t binding, vk::DescriptorType type, vk::ShaderStageFlags stageFlags, uint32t_t count = 1)`
If any binding has > 1 count, then it should be variable length and partially bound.
Only the last binding may have variable length, so this should also be checked. Maybe it should just be a flag?
[VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDescriptorBindingFlagBits.html)

Using a buffer for the other locations might be a possibility to still add all material properties as bindless. But that is to be looked at.


Then the DescriptorBuilder would have
```cpp
DescriptorBuilder &bindBuffer(uint32_t binding, vk::DescriptorBufferInfo *bufferInfo, vk::DescriptorType type);
DescriptorBuilder &bindImage(uint32_t binding, vk::DescriptorImageInfo *imageInfo, vk::DescriptorType type);
DescriptorBuilder &bindImages(uint32_t binding, std::vector<vk::DescriptorImageInfo>& imageInfos, vk::DescriptorType type);
```
bindImages would also validate that the length is less than the max set in descriptor builder.
10 changes: 5 additions & 5 deletions src/AssetManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

std::shared_ptr<UploadedTexture> AssetManager::getTexture(std::string filename) {
std::shared_ptr<UploadedTexture> AssetManager::getTexture(const std::string& filename) {
if (uploadedTextures.find(filename) == uploadedTextures.end()) {
auto texture = std::make_shared<UploadedTexture>();
createTextureImage(filename.c_str(), texture);
Expand All @@ -15,7 +15,7 @@ std::shared_ptr<UploadedTexture> AssetManager::getTexture(std::string filename)
return uploadedTextures[filename];
}

void AssetManager::createTextureImage(const char *filename, std::shared_ptr<UploadedTexture> texture) {
void AssetManager::createTextureImage(const char *filename, const std::shared_ptr<UploadedTexture>& texture) {
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load(filename, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);

Expand Down Expand Up @@ -55,14 +55,14 @@ void AssetManager::createTextureImage(const char *filename, std::shared_ptr<Uplo
});
}

void AssetManager::createTextureImageView(std::shared_ptr<UploadedTexture> texture) {
void AssetManager::createTextureImageView(const std::shared_ptr<UploadedTexture>& texture) {
texture->textureImageView = createImageView(device->device(), texture->textureImage._image, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor, texture->mipLevels);
deletionQueue.push_function([&, texture]() {
device->device().destroyImageView(texture->textureImageView);
});
}

void AssetManager::createTextureSampler(std::shared_ptr<UploadedTexture> texture) {
void AssetManager::createTextureSampler(const std::shared_ptr<UploadedTexture>& texture) {
auto properties = device->physicalDevice().getProperties();

vk::SamplerCreateInfo samplerInfo{
Expand Down Expand Up @@ -120,7 +120,7 @@ AllocatedImage AssetManager::createImage(int width, int height, uint32_t mipLeve
.usage = VMA_MEMORY_USAGE_GPU_ONLY};

VkImage image;
VkImageCreateInfo imageInfoCreate = static_cast<VkImageCreateInfo>(imageInfo); // TODO: Add VMA HPP and fix this soup.
auto imageInfoCreate = static_cast<VkImageCreateInfo>(imageInfo); // TODO: Add VMA HPP and fix this soup.
if (vmaCreateImage(device->allocator(), &imageInfoCreate, &vmaAllocCreateInfo, &image, &allocatedImage._allocation, nullptr) != VK_SUCCESS) {
throw std::runtime_error("Failed to allocate image");
}
Expand Down
8 changes: 4 additions & 4 deletions src/AssetManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AssetManager {
deletionQueue.flush();
}

std::shared_ptr<UploadedTexture> getTexture(std::string filename);
std::shared_ptr<UploadedTexture> getTexture(const std::string& filename);

[[nodiscard]] AllocatedImage createImage(int width, int height, uint32_t mipLevels, vk::SampleCountFlagBits numSamples, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags flags);

Expand All @@ -38,11 +38,11 @@ class AssetManager {
DeletionQueue deletionQueue{};
std::map<std::string, std::shared_ptr<UploadedTexture>> uploadedTextures;

void createTextureImage(const char* filename, std::shared_ptr<UploadedTexture> texture);
void createTextureImage(const char* filename, const std::shared_ptr<UploadedTexture>& texture);

void createTextureImageView(std::shared_ptr<UploadedTexture> texture);
void createTextureImageView(const std::shared_ptr<UploadedTexture>& texture);

void createTextureSampler(std::shared_ptr<UploadedTexture> texture);
void createTextureSampler(const std::shared_ptr<UploadedTexture>& texture);

template <typename T>
[[nodiscard]] AllocatedBuffer stageData(std::span<T>& dataToStage);
Expand Down
108 changes: 26 additions & 82 deletions src/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@ Renderer::Renderer(std::shared_ptr<WindowWrapper> window, std::shared_ptr<Vulkan
: window(window), device{device}, assetManager(assetManager) {
this->renderData = renderData;
try {
descriptorAllocator.init(device->device());
descriptorLayoutCache.init(device->device());
mainDeletionQueue.push_function([&](){
descriptorLayoutCache.cleanup();
descriptorAllocator.cleanup();
});
createSwapChain();
createImageViews();
createRenderPass();
createDescriptorSetLayout();
createTextureDescriptorSetLayout();
uploadMeshes();
createGraphicsPipelineLayout();
createGraphicsPipeline();
createCommandPool();
Expand All @@ -56,7 +62,6 @@ Renderer::Renderer(std::shared_ptr<WindowWrapper> window, std::shared_ptr<Vulkan
createDescriptorSets();
createCommandBuffers();
createSyncObjects();
uploadMeshes();
}
catch (const std::exception& e) {
std::cerr << "Renderer failed to initialize!" << std::endl;
Expand All @@ -80,8 +85,25 @@ Material Renderer::createMaterial(std::vector<std::string>& texturePaths) {
textures.push_back(assetManager.getTexture(filename));
}

Material material{};
material.textureSet = createTextureDescriptorSet(textures);

std::vector<vk::DescriptorImageInfo> imageInfos;
for (const auto& texture : textures) {
vk::DescriptorImageInfo imageInfo {
.sampler = texture->textureSampler,
.imageView = texture->textureImageView,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
imageInfos.push_back(imageInfo);
}

Material material{};
auto textureResult = DescriptorBuilder::begin(&descriptorLayoutCache, &descriptorAllocator)
.bindImages(0, imageInfos, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment)
.build(material.textureSet, textureDescriptorSetLayout);

if(!textureResult) {
throw std::runtime_error("Failed to create Material");
}

return material;
}
Expand Down Expand Up @@ -396,40 +418,6 @@ void Renderer::createDescriptorSetLayout() {
});
}

void Renderer::createTextureDescriptorSetLayout() {
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
.binding = 0,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 256,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
.pImmutableSamplers = nullptr
};

std::array<vk::DescriptorSetLayoutBinding, 1> bindings = {samplerLayoutBinding};

std::array<vk::DescriptorBindingFlags, 1> flags = {vk::DescriptorBindingFlagBits::eVariableDescriptorCount | vk::DescriptorBindingFlagBits::ePartiallyBound};

vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlags {
.bindingCount = static_cast<uint32_t>(bindings.size()),
.pBindingFlags = flags.data()
};

vk::DescriptorSetLayoutCreateInfo layoutInfo {
.pNext = &bindingFlags,
.bindingCount = static_cast<uint32_t>(bindings.size()),
.pBindings = bindings.data()
};

if(device->device().createDescriptorSetLayout(&layoutInfo, nullptr, &textureDescriptorSetLayout) != vk::Result::eSuccess) {
throw std::runtime_error("Failed to create texture descriptor set layout");
}
mainDeletionQueue.push_function([&]() {
device->device().destroyDescriptorSetLayout(textureDescriptorSetLayout);
});
}



void Renderer::createGraphicsPipelineLayout() {
vk::PushConstantRange pushConstantRange {
.stageFlags = vk::ShaderStageFlagBits::eVertex,
Expand Down Expand Up @@ -901,50 +889,6 @@ void Renderer::createDescriptorSets() {
}
}

vk::DescriptorSet Renderer::createTextureDescriptorSet(std::vector<std::shared_ptr<UploadedTexture>>& textures) {
uint32_t counts[1];
counts[0] = static_cast<uint32_t>(textures.size());

vk::DescriptorSetVariableDescriptorCountAllocateInfo setCounts = {
.descriptorSetCount = 1,
.pDescriptorCounts = counts
};

vk::DescriptorSetAllocateInfo textureAllocInfo {
.pNext = &setCounts,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &textureDescriptorSetLayout
};

vk::DescriptorSet textureSet{};
if(device->device().allocateDescriptorSets(&textureAllocInfo, &textureSet) != vk::Result::eSuccess) {
throw std::runtime_error("Failed to create Texture Descriptor set!");
}

std::vector<vk::DescriptorImageInfo> imageInfos;
for (const auto& texture : textures) {
vk::DescriptorImageInfo imageInfo {
.sampler = texture->textureSampler,
.imageView = texture->textureImageView,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
imageInfos.push_back(imageInfo);
}

vk::WriteDescriptorSet imageDescriptor {
.dstSet = textureSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = static_cast<uint32_t>(imageInfos.size()),
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = imageInfos.data(),
};

device->device().updateDescriptorSets(1, &imageDescriptor, 0, nullptr);
return textureSet;
}

void Renderer::copyBuffer(vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize size) {
device->immediateSubmit([&](vk::CommandBuffer cmd){
vk::BufferCopy copyRegion{
Expand Down
7 changes: 4 additions & 3 deletions src/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "RenderData.h"
#include "VkTypes.h"
#include "VulkanDevice.h"
#include "VkDescriptors.h"

const int MAX_FRAMES_IN_FLIGHT = 2;

Expand Down Expand Up @@ -58,6 +59,9 @@ class Renderer {
vk::DescriptorPool descriptorPool;
vk::RenderPass renderPass;

DescriptorAllocator descriptorAllocator;
DescriptorLayoutCache descriptorLayoutCache;

vk::PipelineLayout pipelineLayout;
vk::Pipeline graphicsPipeline;

Expand Down Expand Up @@ -125,9 +129,6 @@ class Renderer {
template <typename T>
AllocatedBuffer uploadBuffer(std::vector<T>& meshData, VkBufferUsageFlags usage);

void createTextureDescriptorSetLayout();
vk::DescriptorSet createTextureDescriptorSet(std::vector<std::shared_ptr<UploadedTexture>>& texture);

void transitionImageLayout(vk::Image image, vk::Format format, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, uint32_t mipLevels);

void createUniformBuffers();
Expand Down
Loading

0 comments on commit 381a6de

Please sign in to comment.