Skip to content

Instantly share code, notes, and snippets.

@bynmz
Last active March 29, 2024 07:45
Show Gist options
  • Save bynmz/35254c4a22aa832412c1fabefb71ebce to your computer and use it in GitHub Desktop.
Save bynmz/35254c4a22aa832412c1fabefb71ebce to your computer and use it in GitHub Desktop.
App3D::App3D() {
globalPool =
DescriptorPool::Builder(nileDevice)
.setMaxSets(SwapChain::MAX_FRAMES_IN_FLIGHT)
.addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, SwapChain::MAX_FRAMES_IN_FLIGHT)
.build();
// build frame descriptor pools
framePools.resize(SwapChain::MAX_FRAMES_IN_FLIGHT);
auto framePoolBuilder = DescriptorPool::Builder(nileDevice)
.setMaxSets(1000)
.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000)
.addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000)
.setPoolFlags(VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT);
for (int i = 0; i < framePools.size(); i++) {
framePools[i] = framePoolBuilder.build();
}
globalSetLayout =
DescriptorSetLayout::Builder(nileDevice)
.addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL_GRAPHICS)
.build();
for (int i = 0; i < uboBuffers.size(); i++) {
uboBuffers[i] = std::make_unique<Buffer>(
nileDevice,
sizeof(GlobalUbo),
1,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
uboBuffers[i]->map();
}
for (int i = 0; i < globalDescriptorSets.size(); i++) {
auto bufferInfo = uboBuffers[i]->descriptorInfo();
DescriptorWriter(*globalSetLayout, *globalPool)
.writeBuffer(0, &bufferInfo)
.build(globalDescriptorSets[i]);
}
}
#pragma once
namespace mge{
class App3D {
public:
static constexpr int WIDTH = 800;
static constexpr int HEIGHT = 600;
App3D();
virtual ~App3D();
App3D(const App3D &) = delete;
App3D &operator=(const App3D &) = delete;
static constexpr int MAX_FRAMES = SwapChain::MAX_FRAMES_IN_FLIGHT;
Window Window{WIDTH, HEIGHT, "My Graphics Engine 3D"};
Device Device{Window};
Renderer Renderer{Window, Device};
// note: order of declarations matters
std::unique_ptr<DescriptorPool> globalPool{};
std::unique_ptr<DescriptorSetLayout> globalSetLayout{};
std::vector<std::unique_ptr<DescriptorPool>> framePools;
GameObjectManager gameObjectManager{Device};
std::vector<std::unique_ptr<Buffer>> uboBuffers{MAX_FRAMES};
std::vector<VkDescriptorSet> globalDescriptorSets{MAX_FRAMES};
Camera camera{};
KeyboardMovementController cameraController{};
virtual void start();
private:
virtual void loadGameObjects();
virtual void loop();
};
} // namespace mge
#include "descriptors.hpp"
// std
#include <cassert>
#include <stdexcept>
namespace mge{
// *************** Descriptor Set Layout Builder *********************
DescriptorSetLayout::Builder &DescriptorSetLayout::Builder::addBinding(
uint32_t binding,
VkDescriptorType descriptorType,
VkShaderStageFlags stageFlags,
uint32_t count) {
assert(bindings.count(binding) == 0 && "Binding already in use");
VkDescriptorSetLayoutBinding layoutBinding{};
layoutBinding.binding = binding;
layoutBinding.descriptorType = descriptorType;
layoutBinding.descriptorCount = count;
layoutBinding.stageFlags = stageFlags;
bindings[binding] = layoutBinding;
return *this;
}
std::unique_ptr<DescriptorSetLayout> DescriptorSetLayout::Builder::build() const {
return std::make_unique<DescriptorSetLayout>(Device, bindings);
}
// *************** Descriptor Set Layout *********************
DescriptorSetLayout::DescriptorSetLayout(
Device &Device, std::unordered_map<uint32_t, VkDescriptorSetLayoutBinding> bindings)
: Device{Device}, bindings{bindings} {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings{};
for (auto kv : bindings) {
setLayoutBindings.push_back(kv.second);
}
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo{};
descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutInfo.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
descriptorSetLayoutInfo.pBindings = setLayoutBindings.data();
if (vkCreateDescriptorSetLayout(
Device.device(),
&descriptorSetLayoutInfo,
nullptr,
&descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor set layout!");
}
}
DescriptorSetLayout::~DescriptorSetLayout() {
vkDestroyDescriptorSetLayout(Device.device(), descriptorSetLayout, nullptr);
}
// *************** Descriptor Pool Builder *********************
DescriptorPool::Builder &DescriptorPool::Builder::addPoolSize(
VkDescriptorType descriptorType, uint32_t count) {
poolSizes.push_back({descriptorType, count});
return *this;
}
DescriptorPool::Builder &DescriptorPool::Builder::setPoolFlags(
VkDescriptorPoolCreateFlags flags) {
poolFlags = flags;
return *this;
}
DescriptorPool::Builder &DescriptorPool::Builder::setMaxSets(uint32_t count) {
maxSets = count;
return *this;
}
std::unique_ptr<DescriptorPool> DescriptorPool::Builder::build() const {
return std::make_unique<DescriptorPool>(Device, maxSets, poolFlags, poolSizes);
}
// *************** Descriptor Pool *********************
DescriptorPool::DescriptorPool(
Device &Device,
uint32_t maxSets,
VkDescriptorPoolCreateFlags poolFlags,
const std::vector<VkDescriptorPoolSize> &poolSizes)
: Device{Device} {
VkDescriptorPoolCreateInfo descriptorPoolInfo{};
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
descriptorPoolInfo.pPoolSizes = poolSizes.data();
descriptorPoolInfo.maxSets = maxSets;
descriptorPoolInfo.flags = poolFlags;
if (vkCreateDescriptorPool(Device.device(), &descriptorPoolInfo, nullptr, &descriptorPool) !=
VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
}
DescriptorPool::~DescriptorPool() {
vkDestroyDescriptorPool(Device.device(), descriptorPool, nullptr);
}
bool DescriptorPool::allocateDescriptor(
const VkDescriptorSetLayout descriptorSetLayout, VkDescriptorSet &descriptor) const {
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.pSetLayouts = &descriptorSetLayout;
allocInfo.descriptorSetCount = 1;
// Might want to create a "DescriptorPoolManager" class that handles this case, and builds
// a new pool whenever an old pool fills up. But this is beyond our current scope
if (vkAllocateDescriptorSets(Device.device(), &allocInfo, &descriptor) != VK_SUCCESS) {
return false;
}
return true;
}
void DescriptorPool::freeDescriptors(std::vector<VkDescriptorSet> &descriptors) const {
vkFreeDescriptorSets(
Device.device(),
descriptorPool,
static_cast<uint32_t>(descriptors.size()),
descriptors.data());
}
void DescriptorPool::resetPool() {
vkResetDescriptorPool(Device.device(), descriptorPool, 0);
}
// *************** Descriptor Writer *********************
DescriptorWriter::DescriptorWriter(DescriptorSetLayout &setLayout, DescriptorPool &pool)
: setLayout{setLayout}, pool{pool} {}
DescriptorWriter &DescriptorWriter::writeBuffer(
uint32_t binding, VkDescriptorBufferInfo *bufferInfo) {
assert(setLayout.bindings.count(binding) == 1 && "Layout does not contain specified binding");
auto &bindingDescription = setLayout.bindings[binding];
assert(
bindingDescription.descriptorCount == 1 &&
"Binding single descriptor info, but binding expects multiple");
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorType = bindingDescription.descriptorType;
write.dstBinding = binding;
write.pBufferInfo = bufferInfo;
write.descriptorCount = 1;
writes.push_back(write);
return *this;
}
DescriptorWriter &DescriptorWriter::writeImage(
uint32_t binding, VkDescriptorImageInfo *imageInfo) {
assert(setLayout.bindings.count(binding) == 1 && "Layout does not contain specified binding");
auto &bindingDescription = setLayout.bindings[binding];
assert(
bindingDescription.descriptorCount == 1 &&
"Binding single descriptor info, but binding expects multiple");
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorType = bindingDescription.descriptorType;
write.dstBinding = binding;
write.pImageInfo = imageInfo;
write.descriptorCount = 1;
writes.push_back(write);
return *this;
}
bool DescriptorWriter::build(VkDescriptorSet &set) {
bool success = pool.allocateDescriptor(setLayout.getDescriptorSetLayout(), set);
if (!success) {
return false;
}
overwrite(set);
return true;
}
void DescriptorWriter::overwrite(VkDescriptorSet &set) {
for (auto &write : writes) {
write.dstSet = set;
}
vkUpdateDescriptorSets(pool.Device.device(), writes.size(), writes.data(), 0, nullptr);
}
} // namespace mge
#pragma once
#include "device.hpp"
// std
#include <memory>
#include <unordered_map>
#include <vector>
namespace mge{
class DescriptorSetLayout {
public:
class Builder {
public:
Builder(Device &Device) : Device{Device} {}
Builder &addBinding(
uint32_t binding,
VkDescriptorType descriptorType,
VkShaderStageFlags stageFlags,
uint32_t count = 1);
std::unique_ptr<DescriptorSetLayout> build() const;
private:
Device &Device;
std::unordered_map<uint32_t, VkDescriptorSetLayoutBinding> bindings{};
};
DescriptorSetLayout(
Device &Device, std::unordered_map<uint32_t, VkDescriptorSetLayoutBinding> bindings);
~DescriptorSetLayout();
DescriptorSetLayout(const DescriptorSetLayout &) = delete;
DescriptorSetLayout &operator=(const DescriptorSetLayout &) = delete;
VkDescriptorSetLayout getDescriptorSetLayout() const { return descriptorSetLayout; }
private:
Device &Device;
VkDescriptorSetLayout descriptorSetLayout;
std::unordered_map<uint32_t, VkDescriptorSetLayoutBinding> bindings;
friend class DescriptorWriter;
};
class DescriptorPool {
public:
class Builder {
public:
Builder(Device &Device) : Device{Device} {}
Builder &addPoolSize(VkDescriptorType descriptorType, uint32_t count);
Builder &setPoolFlags(VkDescriptorPoolCreateFlags flags);
Builder &setMaxSets(uint32_t count);
std::unique_ptr<DescriptorPool> build() const;
private:
Device &Device;
std::vector<VkDescriptorPoolSize> poolSizes{};
uint32_t maxSets = 1000;
VkDescriptorPoolCreateFlags poolFlags = 0;
};
DescriptorPool(
Device &Device,
uint32_t maxSets,
VkDescriptorPoolCreateFlags poolFlags,
const std::vector<VkDescriptorPoolSize> &poolSizes);
~DescriptorPool();
DescriptorPool(const DescriptorPool &) = delete;
DescriptorPool &operator=(const DescriptorPool &) = delete;
bool allocateDescriptor(
const VkDescriptorSetLayout descriptorSetLayout, VkDescriptorSet &descriptor) const;
void freeDescriptors(std::vector<VkDescriptorSet> &descriptors) const;
void resetPool();
private:
Device &Device;
VkDescriptorPool descriptorPool;
friend class DescriptorWriter;
};
class DescriptorWriter {
public:
DescriptorWriter(DescriptorSetLayout &setLayout, DescriptorPool &pool);
DescriptorWriter &writeBuffer(uint32_t binding, VkDescriptorBufferInfo *bufferInfo);
DescriptorWriter &writeImage(uint32_t binding, VkDescriptorImageInfo *imageInfo);
bool build(VkDescriptorSet &set);
void overwrite(VkDescriptorSet &set);
private:
DescriptorSetLayout &setLayout;
DescriptorPool &pool;
std::vector<VkWriteDescriptorSet> writes;
};
} // namespace mge
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment