Add combined image sampler descriptor support

This commit is contained in:
Bailey Harrison 2023-03-21 20:10:23 +00:00
parent 41174515a8
commit 9f88131371
6 changed files with 122 additions and 57 deletions

View File

@ -34,14 +34,14 @@ namespace engine {
struct SetZeroBuffer {
glm::mat4 proj;
};
gfx::DescriptorBuffer* setZeroBuffer;
gfx::UniformBuffer* setZeroBuffer;
/* uniforms for per-frame data */
const gfx::DescriptorSetLayout* setOneLayout;
const gfx::DescriptorSet* setOne;
struct SetOneBuffer {
glm::mat4 view;
};
gfx::DescriptorBuffer* setOneBuffer;
gfx::UniformBuffer* setOneBuffer;
};
class Application {

View File

@ -2,6 +2,7 @@
#include <cstdint>
#include <vector>
#include <type_traits>
// Enums and structs for the graphics abstraction
@ -9,7 +10,7 @@ namespace engine::gfx {
// handles (incomplete types)
struct Pipeline;
struct DescriptorBuffer;
struct UniformBuffer;
struct Buffer;
struct Texture;
struct DrawBuffer;
@ -80,10 +81,19 @@ namespace engine::gfx {
LINEAR,
};
struct VertexBufferDesc {
uint64_t size;
enum class DescriptorType {
UNIFORM_BUFFER,
COMBINED_IMAGE_SAMPLER,
};
namespace ShaderStageFlags {
enum Bits : uint32_t {
VERTEX = 1 << 0,
FRAGMENT = 1 << 1,
};
typedef std::underlying_type<Bits>::type Flags;
}
struct VertexAttribDescription {
VertexAttribDescription(uint32_t location, VertexAttribFormat format, uint32_t offset) :
location(location),
@ -108,4 +118,9 @@ namespace engine::gfx {
std::vector<const DescriptorSetLayout*> descriptorSetLayouts;
};
struct DescriptorSetLayoutBinding {
DescriptorType descriptorType = DescriptorType::UNIFORM_BUFFER;
ShaderStageFlags::Flags stageFlags = 0;
};
}

View File

@ -32,17 +32,18 @@ namespace engine {
gfx::Pipeline* createPipeline(const gfx::PipelineInfo& info);
void destroyPipeline(const gfx::Pipeline* pipeline);
gfx::DescriptorSetLayout* createDescriptorSetLayout();
gfx::DescriptorSetLayout* createDescriptorSetLayout(const std::vector<gfx::DescriptorSetLayoutBinding>& bindings);
void destroyDescriptorSetLayout(const gfx::DescriptorSetLayout* layout);
gfx::DescriptorSet* allocateDescriptorSet(const gfx::DescriptorSetLayout* layout);
// This updates all copies of the descriptor. This cannot be used after any frames have been renderered
void updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range);
void updateDescriptorUniformBuffer(const gfx::DescriptorSet* set, uint32_t binding, const gfx::UniformBuffer* buffer, size_t offset, size_t range);
void updateDescriptorCombinedImageSampler(const gfx::DescriptorSet* set, uint32_t binding);
gfx::DescriptorBuffer* createDescriptorBuffer(uint64_t size, const void* initialData);
void destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer);
gfx::UniformBuffer* createUniformBuffer(uint64_t size, const void* initialData);
void destroyUniformBuffer(const gfx::UniformBuffer* descriptorBuffer);
void writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data);
void writeUniformBuffer(gfx::UniformBuffer* buffer, uint64_t offset, uint64_t size, const void* data);
// Loads data into staging buffer and copies that into a single GPU buffer.
gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data);

View File

@ -76,21 +76,28 @@ namespace engine {
// initialise the render data
renderData.gfxdev = std::make_unique<GFXDevice>(appName, appVersion, m_window->getHandle(), graphicsSettings);
renderData.setZeroLayout = gfx()->createDescriptorSetLayout();
std::vector<gfx::DescriptorSetLayoutBinding> layoutBindings;
{
auto& binding0 = layoutBindings.emplace_back();
binding0.descriptorType = gfx::DescriptorType::UNIFORM_BUFFER;
binding0.stageFlags = gfx::ShaderStageFlags::VERTEX;
}
renderData.setZeroLayout = gfx()->createDescriptorSetLayout(layoutBindings);
renderData.setZero = gfx()->allocateDescriptorSet(renderData.setZeroLayout);
RenderData::SetZeroBuffer initialSetZeroData{
.proj = glm::perspectiveZO(glm::radians(70.0f), 1024.0f / 768.0f, 0.1f, 1000.0f),
};
renderData.setZeroBuffer = gfx()->createDescriptorBuffer(sizeof(RenderData::SetZeroBuffer), &initialSetZeroData);
gfx()->updateDescriptor(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer));
renderData.setZeroBuffer = gfx()->createUniformBuffer(sizeof(RenderData::SetZeroBuffer), &initialSetZeroData);
gfx()->updateDescriptorUniformBuffer(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer));
renderData.setOneLayout = gfx()->createDescriptorSetLayout();
renderData.setOneLayout = gfx()->createDescriptorSetLayout(layoutBindings);
renderData.setOne = gfx()->allocateDescriptorSet(renderData.setOneLayout);
RenderData::SetOneBuffer initialSetOneData{
.view = glm::mat4{ 1.0f },
};
renderData.setOneBuffer = gfx()->createDescriptorBuffer(sizeof(RenderData::SetOneBuffer), &initialSetOneData);
gfx()->updateDescriptor(renderData.setOne, 0, renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer));
renderData.setOneBuffer = gfx()->createUniformBuffer(sizeof(RenderData::SetOneBuffer), &initialSetOneData);
gfx()->updateDescriptorUniformBuffer(renderData.setOne, 0, renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer));
// default resources
{
@ -135,9 +142,9 @@ namespace engine {
Application::~Application()
{
gfx()->destroyDescriptorBuffer(renderData.setOneBuffer);
gfx()->destroyUniformBuffer(renderData.setOneBuffer);
gfx()->destroyDescriptorSetLayout(renderData.setOneLayout);
gfx()->destroyDescriptorBuffer(renderData.setZeroBuffer);
gfx()->destroyUniformBuffer(renderData.setZeroBuffer);
gfx()->destroyDescriptorSetLayout(renderData.setZeroLayout);
}

View File

@ -87,7 +87,6 @@ namespace engine {
VkBuffer buffer = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr;
VkDeviceSize size = 0;
bool hostVisible = false;
};
struct gfx::Pipeline {
@ -113,7 +112,7 @@ namespace engine {
std::array<VkDescriptorSet, FRAMES_IN_FLIGHT> sets; // frames in flight cannot use the same descriptor set in case the buffer needs updating
};
struct gfx::DescriptorBuffer {
struct gfx::UniformBuffer {
gfx::Buffer stagingBuffer{};
std::array<gfx::Buffer, FRAMES_IN_FLIGHT> gpuBuffers;
};
@ -165,24 +164,39 @@ namespace engine {
switch (level) {
case gfx::MSAALevel::MSAA_OFF:
return VK_SAMPLE_COUNT_1_BIT;
break;
case gfx::MSAALevel::MSAA_2X:
return VK_SAMPLE_COUNT_2_BIT;
break;
case gfx::MSAALevel::MSAA_4X:
return VK_SAMPLE_COUNT_4_BIT;
break;
case gfx::MSAALevel::MSAA_8X:
return VK_SAMPLE_COUNT_8_BIT;
break;
case gfx::MSAALevel::MSAA_16X:
return VK_SAMPLE_COUNT_16_BIT;
break;
default:
throw std::runtime_error("Unknown MSAA level");
}
}
static VkDescriptorType getDescriptorType(gfx::DescriptorType type)
{
switch (type) {
case gfx::DescriptorType::UNIFORM_BUFFER:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case gfx::DescriptorType::COMBINED_IMAGE_SAMPLER:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
default:
throw std::runtime_error("Unknown descriptor type");
}
}
static VkShaderStageFlags getShaderStageFlags(gfx::ShaderStageFlags::Flags flags)
{
VkShaderStageFlags out = 0;
if (flags & gfx::ShaderStageFlags::VERTEX) out |= VK_SHADER_STAGE_VERTEX_BIT;
if (flags & gfx::ShaderStageFlags::FRAGMENT) out |= VK_SHADER_STAGE_FRAGMENT_BIT;
return out;
}
}
// functions
@ -296,7 +310,7 @@ namespace engine {
Swapchain swapchain{};
VkDescriptorPool descriptorPool;
std::array<std::unordered_set<gfx::DescriptorBuffer*>, FRAMES_IN_FLIGHT> descriptorBufferWriteQueues{};
std::array<std::unordered_set<gfx::UniformBuffer*>, FRAMES_IN_FLIGHT> uniformBufferWriteQueues{};
VkCommandPool transferCommandPool = VK_NULL_HANDLE;
@ -540,16 +554,16 @@ namespace engine {
// transfer cmds...
std::vector<VkBufferMemoryBarrier2> barriers{};
for (gfx::DescriptorBuffer* descriptorBuffer : pimpl->descriptorBufferWriteQueues[currentFrameIndex]) {
for (gfx::UniformBuffer* uniformBuffer : pimpl->uniformBufferWriteQueues[currentFrameIndex]) {
VkBufferCopy copyRegion{};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = descriptorBuffer->stagingBuffer.size;
copyRegion.size = uniformBuffer->stagingBuffer.size;
vkCmdCopyBuffer(
frameData.transferBuf,
descriptorBuffer->stagingBuffer.buffer,
descriptorBuffer->gpuBuffers[currentFrameIndex].buffer,
uniformBuffer->stagingBuffer.buffer,
uniformBuffer->gpuBuffers[currentFrameIndex].buffer,
1,
&copyRegion
);
@ -562,11 +576,11 @@ namespace engine {
barrier.dstAccessMask = 0;
barrier.srcQueueFamilyIndex = pimpl->device.queues.transferQueueFamily;
barrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily;
barrier.buffer = descriptorBuffer->gpuBuffers[currentFrameIndex].buffer;
barrier.buffer = uniformBuffer->gpuBuffers[currentFrameIndex].buffer;
barrier.offset = 0;
barrier.size = descriptorBuffer->gpuBuffers[currentFrameIndex].size;
barrier.size = uniformBuffer->gpuBuffers[currentFrameIndex].size;
}
pimpl->descriptorBufferWriteQueues[currentFrameIndex].clear();
pimpl->uniformBufferWriteQueues[currentFrameIndex].clear();
VkDependencyInfo dependencyInfo{};
dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
@ -989,23 +1003,28 @@ namespace engine {
delete pipeline;
}
gfx::DescriptorSetLayout* GFXDevice::createDescriptorSetLayout()
gfx::DescriptorSetLayout* GFXDevice::createDescriptorSetLayout(const std::vector<gfx::DescriptorSetLayoutBinding>& bindings)
{
gfx::DescriptorSetLayout* out = new gfx::DescriptorSetLayout{};
std::vector<VkDescriptorSetLayoutBinding> bindings{};
auto& binding = bindings.emplace_back();
binding.binding = 0; // This should be as low as possible to avoid wasting memory
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.descriptorCount = 1; // if > 1, accessible as an array in the shader
binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // only accessible in vertex
std::vector<VkDescriptorSetLayoutBinding> vulkanBindings{};
uint32_t i = 0;
for (const auto& binding : bindings) {
auto& vulkanBinding = vulkanBindings.emplace_back();
vulkanBinding.binding = i; // This should be as low as possible to avoid wasting memory
vulkanBinding.descriptorType = converters::getDescriptorType(binding.descriptorType);
vulkanBinding.descriptorCount = 1; // if > 1, accessible as an array in the shader
vulkanBinding.stageFlags = converters::getShaderStageFlags(binding.stageFlags);
++i;
}
VkDescriptorSetLayoutCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.bindingCount = (uint32_t)bindings.size();
info.pBindings = bindings.data();
info.bindingCount = (uint32_t)vulkanBindings.size();
info.pBindings = vulkanBindings.data();
VKCHECK(vkCreateDescriptorSetLayout(pimpl->device.device, &info, nullptr, &out->layout));
return out;
@ -1039,7 +1058,7 @@ namespace engine {
return set;
}
void GFXDevice::updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range)
void GFXDevice::updateDescriptorUniformBuffer(const gfx::DescriptorSet* set, uint32_t binding, const gfx::UniformBuffer* buffer, size_t offset, size_t range)
{
assert(pimpl->FRAMECOUNT == 0);
@ -1065,14 +1084,39 @@ namespace engine {
}
}
gfx::DescriptorBuffer* GFXDevice::createDescriptorBuffer(uint64_t size, const void* initialData)
void GFXDevice::updateDescriptorCombinedImageSampler(const gfx::DescriptorSet *set, uint32_t binding)
{
assert(pimpl->FRAMECOUNT == 0);
VkDescriptorImageInfo imageInfo{};
imageInfo.sampler = VK_NULL_HANDLE;
imageInfo.imageView = VK_NULL_HANDLE;
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
VkWriteDescriptorSet descriptorWrite{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->sets[i],
.dstBinding = binding,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &imageInfo,
.pBufferInfo = nullptr,
.pTexelBufferView = nullptr
};
vkUpdateDescriptorSets(pimpl->device.device, 1, &descriptorWrite, 0, nullptr);
}
}
gfx::UniformBuffer* GFXDevice::createUniformBuffer(uint64_t size, const void* initialData)
{
gfx::DescriptorBuffer* out = new gfx::DescriptorBuffer{};
gfx::UniformBuffer* out = new gfx::UniformBuffer{};
/* first make staging buffer */
out->stagingBuffer.size = size;
out->stagingBuffer.type = gfx::BufferType::UNIFORM;
out->stagingBuffer.hostVisible = true;
{
VkBufferCreateInfo stagingBufferInfo{};
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -1098,7 +1142,6 @@ namespace engine {
for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
out->gpuBuffers[i].size = out->stagingBuffer.size;
out->gpuBuffers[i].type = gfx::BufferType::UNIFORM;
out->gpuBuffers[i].hostVisible = false;
VkBufferCreateInfo gpuBufferInfo{};
gpuBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -1121,18 +1164,18 @@ namespace engine {
}
void GFXDevice::destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer)
void GFXDevice::destroyUniformBuffer(const gfx::UniformBuffer* uniformBuffer)
{
for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->gpuBuffers[i].buffer, descriptorBuffer->gpuBuffers[i].allocation);
vmaDestroyBuffer(pimpl->allocator, uniformBuffer->gpuBuffers[i].buffer, uniformBuffer->gpuBuffers[i].allocation);
}
vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->stagingBuffer.buffer, descriptorBuffer->stagingBuffer.allocation);
vmaDestroyBuffer(pimpl->allocator, uniformBuffer->stagingBuffer.buffer, uniformBuffer->stagingBuffer.allocation);
delete descriptorBuffer;
delete uniformBuffer;
}
void GFXDevice::writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data)
void GFXDevice::writeUniformBuffer(gfx::UniformBuffer* buffer, uint64_t offset, uint64_t size, const void* data)
{
assert(offset + size <= buffer->stagingBuffer.size);
@ -1145,7 +1188,7 @@ namespace engine {
/* queue the writes to each gpu buffer */
// This is required as buffers cannot be updated if they are currently in use
for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) {
pimpl->descriptorBufferWriteQueues[i].insert(buffer);
pimpl->uniformBufferWriteQueues[i].insert(buffer);
}
}
@ -1157,7 +1200,6 @@ namespace engine {
auto out = new gfx::Buffer{};
out->size = size;
out->type = type;
out->hostVisible = false;
VkBuffer stagingBuffer;
VmaAllocation stagingAllocation;

View File

@ -48,13 +48,13 @@ namespace engine {
RenderData::SetZeroBuffer setZeroBuffer{
.proj = projMatrix
};
m_gfx->writeDescriptorBuffer(renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer), &setZeroBuffer);
m_gfx->writeUniformBuffer(renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer), &setZeroBuffer);
}
RenderData::SetOneBuffer setOneBuffer{
.view = viewMatrix
};
m_gfx->writeDescriptorBuffer(renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer), &setOneBuffer);
m_gfx->writeUniformBuffer(renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer), &setOneBuffer);
/* render all renderable entities */