This commit is contained in:
Kbz-8
2025-06-16 15:18:27 +02:00
parent 15510fa8a7
commit cd7e5ad26f
165 changed files with 78107 additions and 0 deletions

246
Runtime/Sources/Renderer/Buffer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,246 @@
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <Renderer/Buffer.h>
namespace Scop
{
void GPUBuffer::Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name, bool dedicated_alloc)
{
if(type == BufferType::Constant)
{
if(data.Empty())
{
Warning("Vulkan: trying to create constant buffer without data (constant buffers cannot be modified after creation)");
return;
}
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
else if(type == BufferType::HighDynamic)
{
m_usage = usage;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
else // LowDynamic or Staging
{
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
if(type == BufferType::Staging && data.Empty())
Warning("Vulkan: trying to create staging buffer without data (wtf?)");
CreateBuffer(size, m_usage, m_flags, std::move(name), dedicated_alloc);
if(!data.Empty())
{
if(m_memory.map != nullptr)
std::memcpy(m_memory.map, data.GetData(), data.GetSize());
}
if(type == BufferType::Constant || type == BufferType::LowDynamic)
PushToGPU();
}
void GPUBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name, bool dedicated_alloc)
{
auto device = RenderCore::Get().GetDevice();
m_buffer = kvfCreateBuffer(device, usage, size);
VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements);
//m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset);
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
std::string alloc_name{ name };
if(usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT)
alloc_name.append("_index_buffer");
else if(usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
alloc_name.append("_vertex_buffer");
else if(usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
alloc_name.append("_uniform_buffer");
else
alloc_name.append("_buffer");
if(m_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
alloc_name.append("_gpu");
m_name = name;
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = VK_OBJECT_TYPE_BUFFER;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_buffer);
name_info.pObjectName = m_name.c_str();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
#endif
m_size = size;
m_is_dedicated_alloc = dedicated_alloc;
s_buffer_count++;
}
bool GPUBuffer::CopyFrom(const GPUBuffer& buffer, std::size_t src_offset, std::size_t dst_offset) noexcept
{
if(!(m_usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT))
{
Error("Vulkan: buffer cannot be the destination of a copy because it does not have the correct usage flag");
return false;
}
if(!(buffer.m_usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
{
Error("Vulkan: buffer cannot be the source of a copy because it does not have the correct usage flag");
return false;
}
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
kvfCopyBufferToBuffer(cmd, m_buffer, buffer.Get(), buffer.GetSize(), src_offset, dst_offset);
kvfEndCommandBuffer(cmd);
if(!RenderCore::Get().StackSubmits())
{
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfWaitForFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
}
else
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE);
return true;
}
void GPUBuffer::PushToGPU() noexcept
{
GPUBuffer new_buffer;
new_buffer.m_usage = (this->m_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
new_buffer.m_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
new_buffer.CreateBuffer(m_memory.size, new_buffer.m_usage, new_buffer.m_flags, m_name, m_is_dedicated_alloc);
if(new_buffer.CopyFrom(*this))
Swap(new_buffer);
new_buffer.Destroy();
}
void GPUBuffer::Destroy() noexcept
{
if(m_buffer == VK_NULL_HANDLE)
return;
RenderCore::Get().WaitDeviceIdle();
RenderCore::Get().vkDestroyBuffer(RenderCore::Get().GetDevice(), m_buffer, nullptr);
RenderCore::Get().GetAllocator().Deallocate(m_memory);
m_buffer = VK_NULL_HANDLE;
m_memory = NULL_MEMORY_BLOCK;
s_buffer_count--;
}
void GPUBuffer::Swap(GPUBuffer& buffer) noexcept
{
std::swap(m_buffer, buffer.m_buffer);
m_memory.Swap(buffer.m_memory);
std::swap(m_usage, buffer.m_usage);
std::swap(m_flags, buffer.m_flags);
}
void VertexBuffer::SetData(CPUBuffer data)
{
if(data.GetSize() > m_memory.size)
{
Error("Vulkan: trying to store too much data in a vertex buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in a vertex buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, data);
CopyFrom(staging);
staging.Destroy();
}
void IndexBuffer::SetData(CPUBuffer data)
{
if(data.GetSize() > m_memory.size)
{
Error("Vulkan: trying to store too much data in an index buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in an index buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, data);
CopyFrom(staging);
staging.Destroy();
}
void MeshBuffer::SetVertexData(CPUBuffer data)
{
if(data.GetSize() > m_index_offset)
{
Error("Vulkan: trying to store too much data in a vertex buffer (% bytes in % bytes)", data.GetSize(), m_index_offset);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in a vertex buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, data);
CopyFrom(staging, 0, 0);
staging.Destroy();
}
void MeshBuffer::SetIndexData(CPUBuffer data)
{
if(data.GetSize() > m_memory.size - m_index_offset)
{
Error("Vulkan: trying to store too much data in an index buffer (% bytes in % bytes)", data.GetSize(), m_memory.size - m_index_offset);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in an index buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, data);
CopyFrom(staging, 0, m_index_offset);
staging.Destroy();
}
void UniformBuffer::Init(std::uint32_t size, std::string_view name)
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_buffers[i].Init(BufferType::HighDynamic, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, {}, name);
m_maps[i] = m_buffers[i].GetMap();
if(m_maps[i] == nullptr)
FatalError("Vulkan: unable to map a uniform buffer");
}
}
void UniformBuffer::SetData(CPUBuffer data, std::size_t frame_index)
{
if(data.GetSize() > m_buffers[frame_index].GetSize())
{
Error("Vulkan: invalid data size to update to a uniform buffer, % != %", data.GetSize(), m_buffers[frame_index].GetSize());
return;
}
if(m_maps[frame_index] != nullptr)
std::memcpy(m_maps[frame_index], data.GetData(), data.GetSize());
}
void UniformBuffer::Destroy() noexcept
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_buffers[i].Destroy();
}
}

293
Runtime/Sources/Renderer/Descriptor.cpp git.filemode.normal_file
View File

@@ -0,0 +1,293 @@
#include <kvf.h>
#include <algorithm>
#include <Core/Logs.h>
#include <Renderer/Image.h>
#include <Renderer/Enums.h>
#include <Renderer/Buffer.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Descriptor.h>
#include <Renderer/RenderCore.h>
namespace Scop
{
constexpr std::size_t MAX_SETS_PER_POOL = MAX_FRAMES_IN_FLIGHT * 1024;
void TransitionImageToCorrectLayout(Image& image, VkCommandBuffer cmd)
{
if(!image.IsInit())
return;
if(image.GetType() == ImageType::Color || image.GetType() == ImageType::Cube)
image.TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
else if(image.GetType() == ImageType::Depth)
image.TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, cmd);
else
Error("Vulkan: cannot transition descriptor image layout, unkown image type");
}
void DescriptorPool::Init() noexcept
{
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_SETS_PER_POOL },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, MAX_SETS_PER_POOL },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, MAX_SETS_PER_POOL }
};
VkDescriptorPoolCreateInfo pool_info{};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.poolSizeCount = sizeof(pool_sizes) / sizeof(pool_sizes[0]);
pool_info.pPoolSizes = pool_sizes;
pool_info.maxSets = MAX_SETS_PER_POOL;
pool_info.flags = 0;
kvfCheckVk(RenderCore::Get().vkCreateDescriptorPool(RenderCore::Get().GetDevice(), &pool_info, nullptr, &m_pool));
m_allocation_count = 0;
}
void DescriptorPool::Destroy() noexcept
{
if(m_pool == VK_NULL_HANDLE)
return;
for(auto& set : m_free_sets)
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), set->m_set_layout);
for(auto& set : m_used_sets)
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), set->m_set_layout);
RenderCore::Get().vkDestroyDescriptorPool(RenderCore::Get().GetDevice(), m_pool, nullptr);
m_pool = VK_NULL_HANDLE;
m_allocation_count = 0;
m_free_sets.clear();
m_used_sets.clear();
}
std::shared_ptr<DescriptorSet> DescriptorPool::RequestDescriptorSet(const ShaderSetLayout& layout, ShaderType shader_type)
{
auto it = std::find_if(m_free_sets.begin(), m_free_sets.end(), [&](std::shared_ptr<DescriptorSet> set)
{
return shader_type == set->GetShaderType() && layout == set->GetShaderLayout();
});
if(it != m_free_sets.end())
{
std::shared_ptr<DescriptorSet> set = *it;
m_free_sets.erase(it);
m_used_sets.push_back(set);
return set;
}
std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> vulkan_sets;
VkShaderStageFlagBits vulkan_shader_stage;
switch(shader_type)
{
case ShaderType::Vertex: vulkan_shader_stage = VK_SHADER_STAGE_VERTEX_BIT; break;
case ShaderType::Fragment: vulkan_shader_stage = VK_SHADER_STAGE_FRAGMENT_BIT; break;
default: FatalError("wtf"); vulkan_shader_stage = VK_SHADER_STAGE_VERTEX_BIT; /* Just to shut up warnings */ break;
}
std::vector<VkDescriptorSetLayoutBinding> bindings(layout.binds.size());
{
std::size_t i = 0;
for(auto& [bind, type] : layout.binds)
{
bindings[i].binding = bind;
bindings[i].descriptorCount = 1;
bindings[i].descriptorType = type;
bindings[i].pImmutableSamplers = nullptr;
bindings[i].stageFlags = vulkan_shader_stage;
i++;
}
}
VkDescriptorSetLayout vulkan_layout = kvfCreateDescriptorSetLayout(RenderCore::Get().GetDevice(), bindings.data(), bindings.size());
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &vulkan_layout;
VkDescriptorSet vulkan_set;
kvfCheckVk(RenderCore::Get().vkAllocateDescriptorSets(RenderCore::Get().GetDevice(), &alloc_info, &vulkan_set));
m_allocation_count++;
vulkan_sets[i] = vulkan_set;
}
std::shared_ptr<DescriptorSet> set(new DescriptorSet(*this, vulkan_layout, layout, std::move(vulkan_sets), shader_type));
m_used_sets.push_back(set);
return set;
}
void DescriptorPool::ReturnDescriptorSet(std::shared_ptr<DescriptorSet> set)
{
//std::size_t i = 0;
auto it = std::find_if(m_used_sets.begin(), m_used_sets.end(), [&](const std::shared_ptr<DescriptorSet>& rhs_set)
{
//i++;
//std::cout << m_used_sets.size() << " " << i << std::endl;
return set == rhs_set;
});
if(it == m_used_sets.end())
return;
m_used_sets.erase(it);
m_free_sets.push_back(set);
}
DescriptorPool& DescriptorPoolManager::GetAvailablePool()
{
for(auto& pool : m_pools)
{
if(pool.GetNumberOfSetsAllocated() < MAX_SETS_PER_POOL)
return pool;
}
m_pools.emplace_back().Init();
return m_pools.back();
}
void DescriptorPoolManager::Destroy()
{
for(auto& pool : m_pools)
pool.Destroy();
m_pools.clear();
}
DescriptorSet::DescriptorSet(DescriptorPool& pool, VkDescriptorSetLayout vulkan_layout, const ShaderSetLayout& layout, std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> vulkan_sets, ShaderType shader_type) :
m_shader_layout(layout),
m_sets(std::move(vulkan_sets)),
m_set_layout(vulkan_layout),
m_shader_type(shader_type),
m_pool(pool)
{
for(auto& [binding, type] : layout.binds)
{
m_descriptors.emplace_back();
m_descriptors.back().type = type;
m_descriptors.back().binding = binding;
}
}
void DescriptorSet::SetImage(std::size_t i, std::uint32_t binding, class Image& image)
{
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set image; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
{
Error("Vulkan: trying to bind an image to the wrong descriptor");
return;
}
it->image_ptr = &image;
}
void DescriptorSet::SetStorageBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
{
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
return;
}
it->storage_buffer_ptr = &buffer;
}
void DescriptorSet::SetUniformBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
{
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
{
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
return;
}
it->uniform_buffer_ptr = &buffer;
}
void DescriptorSet::Update(std::size_t i, VkCommandBuffer cmd) noexcept
{
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
std::size_t image_count = 0;
std::size_t buffer_count = 0;
for(auto& descriptor : m_descriptors)
{
if(descriptor.image_ptr)
image_count++;
else if(descriptor.uniform_buffer_ptr || descriptor.storage_buffer_ptr)
buffer_count++;
else
FatalError("unknown descriptor data");
}
std::vector<VkWriteDescriptorSet> writes(m_descriptors.size());
std::vector<VkDescriptorBufferInfo> buffer_infos(buffer_count);
std::vector<VkDescriptorImageInfo> image_infos(image_count);
std::size_t buffer_index = 0;
std::size_t image_index = 0;
std::size_t write_index = 0;
for(auto& descriptor : m_descriptors)
{
if(descriptor.image_ptr)
{
TransitionImageToCorrectLayout(*descriptor.image_ptr, cmd);
VkDescriptorImageInfo info{};
info.sampler = descriptor.image_ptr->GetSampler();
info.imageLayout = descriptor.image_ptr->GetLayout();
info.imageView = descriptor.image_ptr->GetImageView();
image_infos[image_index] = std::move(info);
writes[write_index] = kvfWriteImageToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &image_infos[image_index], descriptor.binding);
image_index++;
}
else if(descriptor.uniform_buffer_ptr)
{
VkDescriptorBufferInfo info{};
info.buffer = descriptor.uniform_buffer_ptr->Get();
info.offset = descriptor.uniform_buffer_ptr->GetOffset();
info.range = VK_WHOLE_SIZE;
buffer_infos[buffer_index] = std::move(info);
writes[write_index] = kvfWriteUniformBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &buffer_infos[buffer_index], descriptor.binding);
buffer_index++;
}
else if(descriptor.storage_buffer_ptr)
{
VkDescriptorBufferInfo info{};
info.buffer = descriptor.storage_buffer_ptr->Get();
info.offset = descriptor.storage_buffer_ptr->GetOffset();
info.range = VK_WHOLE_SIZE;
buffer_infos[buffer_index] = std::move(info);
writes[write_index] = kvfWriteStorageBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &buffer_infos[buffer_index], descriptor.binding);
buffer_index++;
}
write_index++;
}
RenderCore::Get().vkUpdateDescriptorSets(RenderCore::Get().GetDevice(), writes.size(), writes.data(), 0, nullptr);
}
void DescriptorSet::ReturnDescriptorSetToPool()
{
m_pool.ReturnDescriptorSet(shared_from_this());
}
}

265
Runtime/Sources/Renderer/Image.cpp git.filemode.normal_file
View File

@@ -0,0 +1,265 @@
#include <Renderer/Image.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
namespace Scop
{
void Image::Init(ImageType type, std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, bool is_multisampled, std::string_view name, bool dedicated_alloc)
{
m_type = type;
m_width = width;
m_height = height;
m_format = format;
m_tiling = tiling;
m_is_multisampled = is_multisampled;
KvfImageType kvf_type = KVF_IMAGE_OTHER;
switch(m_type)
{
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
default: break;
}
if(m_is_multisampled)
{
VkImageCreateInfo image_info{};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = width;
image_info.extent.height = height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = (m_type == ImageType::Cube ? 6 : 1);
image_info.format = format;
image_info.tiling = tiling;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
image_info.samples = VK_SAMPLE_COUNT_4_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
kvfCheckVk(RenderCore::Get().vkCreateImage(RenderCore::Get().GetDevice(), &image_info, nullptr, &m_image));
}
else
m_image = kvfCreateImage(RenderCore::Get().GetDevice(), width, height, format, tiling, usage, kvf_type);
VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetImageMemoryRequirements(RenderCore::Get().GetDevice(), m_image, &mem_requirements);
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
RenderCore::Get().vkBindImageMemory(RenderCore::Get().GetDevice(), m_image, m_memory.memory, m_memory.offset);
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = VK_OBJECT_TYPE_IMAGE;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_image);
name_info.pObjectName = name.data();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
#endif
s_image_count++;
}
void Image::CreateImageView(VkImageViewType type, VkImageAspectFlags aspect_flags, int layer_count) noexcept
{
m_image_view = kvfCreateImageView(RenderCore::Get().GetDevice(), m_image, m_format, type, aspect_flags, layer_count);
}
void Image::CreateSampler() noexcept
{
m_sampler = kvfCreateSampler(RenderCore::Get().GetDevice(), VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_MIPMAP_MODE_NEAREST);
}
void Image::TransitionLayout(VkImageLayout new_layout, VkCommandBuffer cmd)
{
if(new_layout == m_layout)
return;
bool is_single_time_cmd_buffer = (cmd == VK_NULL_HANDLE);
KvfImageType kvf_type = KVF_IMAGE_OTHER;
switch(m_type)
{
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
default: break;
}
if(is_single_time_cmd_buffer)
cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfTransitionImageLayout(RenderCore::Get().GetDevice(), m_image, kvf_type, cmd, m_format, m_layout, new_layout, is_single_time_cmd_buffer);
if(is_single_time_cmd_buffer)
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
m_layout = new_layout;
}
void Image::Clear(VkCommandBuffer cmd, Vec4f color)
{
VkImageSubresourceRange subresource_range{};
subresource_range.baseMipLevel = 0;
subresource_range.layerCount = (m_type == ImageType::Cube ? 6 : 1);
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
if(m_type == ImageType::Color || m_type == ImageType::Cube)
{
VkImageLayout old_layout = m_layout;
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
VkClearColorValue clear_color = VkClearColorValue({ { color.x, color.y, color.z, color.w } });
RenderCore::Get().vkCmdClearColorImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
TransitionLayout(old_layout, cmd);
}
else if(m_type == ImageType::Depth)
{
VkClearDepthStencilValue clear_depth_stencil = { 1.0f, 1 };
subresource_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
RenderCore::Get().vkCmdClearDepthStencilImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_depth_stencil, 1, &subresource_range);
TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
}
}
void Image::DestroySampler() noexcept
{
if(m_sampler != VK_NULL_HANDLE)
kvfDestroySampler(RenderCore::Get().GetDevice(), m_sampler);
m_sampler = VK_NULL_HANDLE;
}
void Image::DestroyImageView() noexcept
{
if(m_image_view != VK_NULL_HANDLE)
kvfDestroyImageView(RenderCore::Get().GetDevice(), m_image_view);
m_image_view = VK_NULL_HANDLE;
}
void Image::Destroy() noexcept
{
if(m_image == VK_NULL_HANDLE && m_image_view == VK_NULL_HANDLE && m_sampler == VK_NULL_HANDLE)
return;
RenderCore::Get().WaitDeviceIdle();
DestroySampler();
DestroyImageView();
if(m_image != VK_NULL_HANDLE)
{
RenderCore::Get().GetAllocator().Deallocate(m_memory);
m_memory = NULL_MEMORY_BLOCK;
kvfDestroyImage(RenderCore::Get().GetDevice(), m_image);
}
m_image = VK_NULL_HANDLE;
m_memory = NULL_MEMORY_BLOCK;
m_image = VK_NULL_HANDLE;
m_image_view = VK_NULL_HANDLE;
m_sampler = VK_NULL_HANDLE;
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
m_width = 0;
m_height = 0;
m_is_multisampled = false;
s_image_count--;
}
void CubeTexture::Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format, std::string_view name)
{
if(!pixels)
FatalError("Vulkan: a cubemap cannot be created without pixels data");
std::array<std::vector<std::uint8_t>, 6> texture_data;
std::uint32_t face_width = width / 4;
std::uint32_t face_height = height / 3;
std::size_t size = 0;
for(std::uint32_t cy = 0, face = 0; cy < 3; cy++)
{
for(std::uint32_t cx = 0; cx < 4; cx++)
{
if(cx == 0 || cx == 2 || cx == 3)
{
if(cy != 1)
continue;
}
texture_data[face] = std::vector<std::uint8_t>(face_width * face_height * sizeof(std::uint32_t));
size += sizeof(std::uint32_t) * width * height;
for(std::uint32_t y = 0; y < face_height; y++)
{
std::uint32_t offset = y;
std::uint32_t yp = cy * face_height + offset;
for(std::uint32_t x = 0; x < face_width; x++)
{
offset = x;
std::uint32_t xp = cx * face_width + offset;
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 0] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 0];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 1] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 1];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 2] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 2];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 3] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 3];
}
}
face++;
}
}
CPUBuffer complete_data(size);
std::uint32_t pointer_offset = 0;
const std::uint32_t face_order[6] = { 3, 1, 0, 5, 2, 4 };
for(std::uint32_t face : face_order)
{
std::size_t current_size = face_width * face_height * sizeof(std::uint32_t);
std::memcpy(complete_data.GetData() + pointer_offset, texture_data[face].data(), current_size);
pointer_offset += current_size;
}
Image::Init(ImageType::Cube, face_width, face_height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, false, std::move(name));
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_CUBE, VK_IMAGE_ASPECT_COLOR_BIT, 6);
Image::CreateSampler();
GPUBuffer staging_buffer;
staging_buffer.Init(BufferType::Staging, complete_data.GetSize(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, complete_data);
std::vector<VkBufferImageCopy> buffer_copy_regions;
std::uint32_t offset = 0;
for(std::uint32_t face = 0; face < 6; face++)
{
VkBufferImageCopy buffer_copy_region{};
buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
buffer_copy_region.imageSubresource.mipLevel = 0;
buffer_copy_region.imageSubresource.baseArrayLayer = face;
buffer_copy_region.imageSubresource.layerCount = 1;
buffer_copy_region.imageExtent.width = face_width;
buffer_copy_region.imageExtent.height = face_height;
buffer_copy_region.imageExtent.depth = 1;
buffer_copy_region.bufferOffset = offset;
buffer_copy_regions.push_back(buffer_copy_region);
offset += face_width * face_height * kvfFormatSize(format);
}
VkImageSubresourceRange subresource_range{};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.layerCount = 6;
auto device = RenderCore::Get().GetDevice();
VkCommandBuffer cmd = kvfCreateCommandBuffer(device);
kvfBeginCommandBuffer(cmd, 0);
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
RenderCore::Get().vkCmdCopyBufferToImage(cmd, staging_buffer.Get(), Image::Get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, buffer_copy_regions.size(), buffer_copy_regions.data());
TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
kvfEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(device);
kvfSubmitSingleTimeCommandBuffer(device, cmd, KVF_GRAPHICS_QUEUE, fence);
kvfWaitForFence(device, fence);
kvfDestroyFence(device, fence);
staging_buffer.Destroy();
}
}

110
Runtime/Sources/Renderer/Memory/Chunk.cpp git.filemode.normal_file
View File

@@ -0,0 +1,110 @@
#include <Renderer/Memory/Chunk.h>
#include <Renderer/RenderCore.h>
#include <Core/EventBus.h>
#include <Core/Logs.h>
#include <algorithm>
namespace Scop
{
namespace Internal
{
struct MemoryChunkAllocFailedEvent : public EventBase
{
Event What() const override { return Event::MemoryChunkAllocationFailed; }
};
}
MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index, bool is_dedicated, std::uint32_t& vram_usage, std::uint32_t& vram_host_visible_usage)
: m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index), m_is_dedicated(is_dedicated)
{
Verify(device != VK_NULL_HANDLE, "Memory Chunk : invalid device");
VkMemoryAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = size;
alloc_info.memoryTypeIndex = m_memory_type_index;
if(RenderCore::Get().vkAllocateMemory(m_device, &alloc_info, nullptr, &m_memory) != VK_SUCCESS)
{
EventBus::Send("__ScopDeviceAllocator", Internal::MemoryChunkAllocFailedEvent{});
return;
}
VkPhysicalDeviceMemoryProperties properties;
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(m_physical, &properties);
if((properties.memoryTypes[m_memory_type_index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
{
if(RenderCore::Get().vkMapMemory(m_device, m_memory, 0, VK_WHOLE_SIZE, 0, &p_map) != VK_SUCCESS)
FatalError("Vulkan: failed to map a host visible chunk");
vram_host_visible_usage += size;
}
else
vram_usage += size;
MemoryBlock& block = m_blocks.emplace_back();
block.memory = m_memory;
block.offset = 0;
block.size = size;
block.free = true;
}
[[nodiscard]] std::optional<MemoryBlock> MemoryChunk::Allocate(VkDeviceSize size, VkDeviceSize alignment)
{
for(std::size_t i = 0; i < m_blocks.size(); i++)
{
if(!m_blocks[i].free || m_blocks[i].size < size)
continue;
VkDeviceSize offset_displacement = (m_blocks[i].offset % alignment != 0) ? alignment - m_blocks[i].offset % alignment : 0;
VkDeviceSize old_size_available = m_blocks[i].size - offset_displacement;
if(size + offset_displacement <= m_blocks[i].size)
{
m_blocks[i].offset += offset_displacement;
m_blocks[i].size = size;
m_blocks[i].free = false;
if(p_map != nullptr)
m_blocks[i].map = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(p_map) + m_blocks[i].offset);
MemoryBlock new_block;
new_block.memory = m_memory;
new_block.offset = m_blocks[i].offset + m_blocks[i].size;
new_block.size = old_size_available - size;
new_block.free = true;
if(new_block.size > 0)
m_blocks.emplace(m_blocks.begin() + i + 1, new_block);
return m_blocks[i];
}
}
return std::nullopt;
}
void MemoryChunk::Deallocate(const MemoryBlock& block)
{
auto it = std::find(m_blocks.begin(), m_blocks.end(), block);
if(it == m_blocks.end())
FatalError("Memory Chunk : cannot deallocate a block that is owned by another chunk");
it->free = true;
bool end = false;
while(!end)
{
end = true;
for(auto it = m_blocks.begin(); it != m_blocks.end(); ++it)
{
if(it->free && it + 1 != m_blocks.end() && (it + 1)->free)
{
it->size += (it + 1)->size;
m_blocks.erase(it + 1);
end = false;
break;
}
}
}
}
MemoryChunk::~MemoryChunk()
{
RenderCore::Get().vkFreeMemory(m_device, m_memory, nullptr);
}
}

View File

@@ -0,0 +1,112 @@
#include <Renderer/Memory/DeviceAllocator.h>
#include <Renderer/RenderCore.h>
#include <Maths/Constants.h>
#include <Core/Logs.h>
#include <Core/EventBus.h>
#include <optional>
namespace Scop
{
#define AlignUp(val, alignment) ((val + alignment - 1) & ~(alignment - 1))
void DeviceAllocator::AttachToDevice(VkDevice device, VkPhysicalDevice physical) noexcept
{
m_device = device;
m_physical = physical;
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(physical, &m_mem_props);
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::MemoryChunkAllocationFailed)
m_last_chunk_creation_failed = true;
};
EventBus::RegisterListener({ functor, "__ScopDeviceAllocator" });
}
[[nodiscard]] MemoryBlock DeviceAllocator::Allocate(VkDeviceSize size, VkDeviceSize alignment, std::int32_t memory_type_index, bool dedicated_chunk)
{
Verify(m_device != VK_NULL_HANDLE, "invalid device");
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
const std::lock_guard<std::mutex> guard(m_alloc_mutex);
if(!dedicated_chunk)
{
for(auto& chunk : m_chunks)
{
if(chunk->GetMemoryTypeIndex() == memory_type_index)
{
std::optional<MemoryBlock> block = chunk->Allocate(size, alignment);
if(block.has_value())
return *block;
}
}
}
VkDeviceSize chunk_size = dedicated_chunk ? size + alignment : CalcPreferredChunkSize(memory_type_index);
if(chunk_size < size + alignment)
chunk_size = size + alignment;
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, dedicated_chunk, m_vram_usage, m_vram_host_visible_usage));
if(m_last_chunk_creation_failed && !dedicated_chunk)
{
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of preferred chunk size.
std::uint32_t new_block_size_shift = 0;
while(m_last_chunk_creation_failed && new_block_size_shift < NEW_BLOCK_SIZE_SHIFT_MAX)
{
m_last_chunk_creation_failed = false;
m_chunks.pop_back();
chunk_size /= 2;
if(chunk_size < size + alignment)
{
m_last_chunk_creation_failed = true;
break;
}
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, false, m_vram_usage, m_vram_host_visible_usage));
}
}
// If we could not recover from allocation failure
if(m_last_chunk_creation_failed)
FatalError("Device Allocator: could not allocate a memory chunk");
std::optional<MemoryBlock> block = m_chunks.back()->Allocate(size, alignment);
m_allocations_count++;
if(block.has_value())
return *block;
FatalError("Device Allocator: could not allocate a memory block");
return {}; // to avoid warnings
}
void DeviceAllocator::Deallocate(const MemoryBlock& block)
{
Verify(m_device != VK_NULL_HANDLE, "invalid device");
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
const std::lock_guard<std::mutex> guard(m_dealloc_mutex);
for(auto it = m_chunks.begin(); it != m_chunks.end(); ++it)
{
if((*it)->Has(block))
{
(*it)->Deallocate(block);
if((*it)->IsDedicated())
{
if((*it)->GetMap() != nullptr) // If it is host visible
m_vram_host_visible_usage -= (*it)->GetSize();
else
m_vram_usage -= (*it)->GetSize();
m_chunks.erase(it);
m_allocations_count--;
}
return;
}
}
Error("Device Allocator: unable to free a block; could not find it's chunk");
}
VkDeviceSize DeviceAllocator::CalcPreferredChunkSize(std::uint32_t mem_type_index)
{
std::uint32_t heap_index = m_mem_props.memoryTypes[mem_type_index].heapIndex;
VkDeviceSize heap_size = m_mem_props.memoryHeaps[heap_index].size;
bool is_small_heap = heap_size <= SMALL_HEAP_MAX_SIZE;
return AlignUp((is_small_heap ? (heap_size / 8) : DEFAULT_LARGE_HEAP_BLOCK_SIZE), (VkDeviceSize)32);
}
}

View File

@@ -0,0 +1,236 @@
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Renderer.h>
#include <Renderer/Vertex.h>
#include <Graphics/Enums.h>
#include <Core/EventBus.h>
#include <Core/Logs.h>
namespace Scop
{
void GraphicPipeline::Init(GraphicPipelineDescriptor descriptor)
{
Setup(std::move(descriptor));
m_description.vertex_shader->SetPipelineInUse(this);
m_description.fragment_shader->SetPipelineInUse(this);
std::vector<VkPushConstantRange> push_constants;
std::vector<VkDescriptorSetLayout> set_layouts;
push_constants.insert(push_constants.end(), m_description.vertex_shader->GetPipelineLayout().push_constants.begin(), m_description.vertex_shader->GetPipelineLayout().push_constants.end());
push_constants.insert(push_constants.end(), m_description.fragment_shader->GetPipelineLayout().push_constants.begin(), m_description.fragment_shader->GetPipelineLayout().push_constants.end());
set_layouts.insert(set_layouts.end(), m_description.vertex_shader->GetPipelineLayout().set_layouts.begin(), m_description.vertex_shader->GetPipelineLayout().set_layouts.end());
set_layouts.insert(set_layouts.end(), m_description.fragment_shader->GetPipelineLayout().set_layouts.begin(), m_description.fragment_shader->GetPipelineLayout().set_layouts.end());
m_pipeline_layout = kvfCreatePipelineLayout(RenderCore::Get().GetDevice(), set_layouts.data(), set_layouts.size(), push_constants.data(), push_constants.size());
CreateFramebuffers(m_description.color_attachments, m_description.clear_color_attachments);
VkPhysicalDeviceFeatures features{};
RenderCore::Get().vkGetPhysicalDeviceFeatures(RenderCore::Get().GetPhysicalDevice(), &features);
VkCullModeFlags cullmode;
switch(m_description.culling)
{
case CullMode::None: cullmode = VK_CULL_MODE_NONE; break;
case CullMode::Back: cullmode = VK_CULL_MODE_BACK_BIT; break;
case CullMode::Front: cullmode = VK_CULL_MODE_FRONT_BIT; break;
case CullMode::FrontAndBack: cullmode = VK_CULL_MODE_FRONT_AND_BACK; break;
default: break;
}
KvfGraphicsPipelineBuilder* builder = kvfCreateGPipelineBuilder();
kvfGPipelineBuilderAddShaderStage(builder, m_description.vertex_shader->GetShaderStage(), m_description.vertex_shader->GetShaderModule(), "main");
kvfGPipelineBuilderAddShaderStage(builder, m_description.fragment_shader->GetShaderStage(), m_description.fragment_shader->GetShaderModule(), "main");
kvfGPipelineBuilderSetInputTopology(builder, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
kvfGPipelineBuilderSetCullMode(builder, cullmode, VK_FRONT_FACE_CLOCKWISE);
kvfGPipelineBuilderEnableAlphaBlending(builder);
if(m_description.depth)
kvfGPipelineBuilderEnableDepthTest(builder, (m_description.depth_test_equal ? VK_COMPARE_OP_EQUAL : VK_COMPARE_OP_LESS), true);
else
kvfGPipelineBuilderDisableDepthTest(builder);
if(m_description.wireframe && features.fillModeNonSolid)
kvfGPipelineBuilderSetPolygonMode(builder, VK_POLYGON_MODE_LINE, 1.0f);
else
kvfGPipelineBuilderSetPolygonMode(builder, VK_POLYGON_MODE_FILL, 1.0f);
if(features.sampleRateShading)
kvfGPipelineBuilderSetMultisamplingShading(builder, VK_SAMPLE_COUNT_1_BIT, 0.25f);
else
kvfGPipelineBuilderSetMultisampling(builder, VK_SAMPLE_COUNT_1_BIT);
if(!m_description.no_vertex_inputs)
{
VkVertexInputBindingDescription binding_description = Vertex::GetBindingDescription();
auto attributes_description = Vertex::GetAttributeDescriptions();
kvfGPipelineBuilderSetVertexInputs(builder, binding_description, attributes_description.data(), attributes_description.size());
}
m_pipeline = kvfCreateGraphicsPipeline(RenderCore::Get().GetDevice(), VK_NULL_HANDLE, m_pipeline_layout, builder, m_renderpass);
kvfDestroyGPipelineBuilder(builder);
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = VK_OBJECT_TYPE_PIPELINE;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_pipeline);
name_info.pObjectName = m_description.name.data();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
name_info.objectType = VK_OBJECT_TYPE_RENDER_PASS;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_renderpass);
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
name_info.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_description.vertex_shader->GetShaderModule());
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_description.fragment_shader->GetShaderModule());
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
name_info.objectType = VK_OBJECT_TYPE_FRAMEBUFFER;
for(VkFramebuffer fb : m_framebuffers)
{
name_info.objectHandle = reinterpret_cast<std::uint64_t>(fb);
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
}
Message("Vulkan: % graphics pipeline created", m_description.name);
#else
Message("Vulkan: graphics pipeline created");
#endif
}
bool GraphicPipeline::BindPipeline(VkCommandBuffer command_buffer, std::size_t framebuffer_index, std::array<float, 4> clear) noexcept
{
if(s_bound_pipeline != nullptr)
{
Error("Vulkan: cannot bind a graphics pipeline because another one have not been unbound");
return false;
}
s_bound_pipeline = this;
TransitionAttachments(command_buffer);
VkFramebuffer fb = m_framebuffers[framebuffer_index];
VkExtent2D fb_extent = kvfGetFramebufferSize(fb);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = fb_extent.width;
viewport.height = fb_extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
RenderCore::Get().vkCmdSetViewport(command_buffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = fb_extent;
RenderCore::Get().vkCmdSetScissor(command_buffer, 0, 1, &scissor);
for(int i = 0; i < m_clears.size(); i++)
{
m_clears[i].color.float32[0] = clear[0];
m_clears[i].color.float32[1] = clear[1];
m_clears[i].color.float32[2] = clear[2];
m_clears[i].color.float32[3] = clear[3];
}
if(m_description.depth)
m_clears.back().depthStencil = VkClearDepthStencilValue{ 1.0f, 0 };
kvfBeginRenderPass(m_renderpass, command_buffer, fb, fb_extent, m_clears.data(), m_clears.size());
RenderCore::Get().vkCmdBindPipeline(command_buffer, GetPipelineBindPoint(), GetPipeline());
return true;
}
void GraphicPipeline::EndPipeline(VkCommandBuffer command_buffer) noexcept
{
if(s_bound_pipeline != this)
return;
RenderCore::Get().vkCmdEndRenderPass(command_buffer);
s_bound_pipeline = nullptr;
}
void GraphicPipeline::Destroy() noexcept
{
if(m_pipeline == VK_NULL_HANDLE)
return;
RenderCore::Get().WaitDeviceIdle();
for(auto& fb : m_framebuffers)
{
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
Message("Vulkan: framebuffer destroyed");
}
kvfDestroyPipelineLayout(RenderCore::Get().GetDevice(), m_pipeline_layout);
Message("Vulkan: graphics pipeline layout destroyed");
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
Message("Vulkan: renderpass destroyed");
kvfDestroyPipeline(RenderCore::Get().GetDevice(), m_pipeline);
m_description.vertex_shader.reset();
m_description.fragment_shader.reset();
m_description.color_attachments.clear();
m_framebuffers.clear();
m_clears.clear();
m_renderpass = VK_NULL_HANDLE;
m_pipeline = VK_NULL_HANDLE;
m_pipeline_layout = VK_NULL_HANDLE;
Message("Vulkan: graphics pipeline destroyed");
}
void GraphicPipeline::CreateFramebuffers(const std::vector<NonOwningPtr<Texture>>& render_targets, bool clear_attachments)
{
TransitionAttachments();
std::vector<VkAttachmentDescription> attachments;
std::vector<VkImageView> attachment_views;
if(m_description.renderer)
{
attachments.push_back(kvfBuildSwapchainAttachmentDescription(m_description.renderer->GetSwapchain().Get(), clear_attachments));
attachment_views.push_back(m_description.renderer->GetSwapchain().GetSwapchainImages()[0].GetImageView());
}
for(NonOwningPtr<Texture> image : render_targets)
{
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image->GetFormat(), image->GetLayout(), image->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
attachment_views.push_back(image->GetImageView());
}
if(m_description.depth)
{
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_DEPTH, m_description.depth->GetFormat(), m_description.depth->GetLayout(), m_description.depth->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
attachment_views.push_back(m_description.depth->GetImageView());
}
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), GetPipelineBindPoint());
m_clears.clear();
m_clears.resize(attachments.size());
Message("Vulkan: renderpass created");
if(m_description.renderer)
{
for(const Image& image : m_description.renderer->GetSwapchain().GetSwapchainImages())
{
attachment_views[0] = image.GetImageView();
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image.GetWidth(), .height = image.GetHeight() }));
Message("Vulkan: framebuffer created");
}
}
for(NonOwningPtr<Texture> image : render_targets)
{
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image->GetWidth(), .height = image->GetHeight() }));
Message("Vulkan: framebuffer created");
}
}
void GraphicPipeline::TransitionAttachments(VkCommandBuffer cmd)
{
if(m_description.depth)
m_description.depth->TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
for(NonOwningPtr<Texture> image : m_description.color_attachments)
image->TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, cmd);
}
}

102
Runtime/Sources/Renderer/Pipelines/Shader.cpp git.filemode.normal_file
View File

@@ -0,0 +1,102 @@
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <fstream>
namespace Scop
{
Shader::Shader(const std::vector<std::uint32_t>& bytecode, ShaderType type, ShaderLayout layout, std::string name) : m_name(std::move(name)), m_bytecode(bytecode), m_layout(std::move(layout))
{
switch(type)
{
case ShaderType::Vertex : m_stage = VK_SHADER_STAGE_VERTEX_BIT; break;
case ShaderType::Fragment : m_stage = VK_SHADER_STAGE_FRAGMENT_BIT; break;
case ShaderType::Compute : m_stage = VK_SHADER_STAGE_COMPUTE_BIT; break;
default : FatalError("wtf"); break;
}
m_module = kvfCreateShaderModule(RenderCore::Get().GetDevice(), m_bytecode.data(), m_bytecode.size());
Message("Vulkan: shader module % created", m_name);
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_module);
name_info.pObjectName = m_name.c_str();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
#endif
GeneratePipelineLayout(m_layout);
}
void Shader::GeneratePipelineLayout(ShaderLayout layout)
{
for(auto& [_, set] : layout.set_layouts)
{
std::vector<VkDescriptorSetLayoutBinding> bindings(set.binds.size());
std::size_t i = 0;
for(auto& [bind, type] : set.binds)
{
bindings[i].binding = bind;
bindings[i].descriptorCount = 1;
bindings[i].descriptorType = type;
bindings[i].pImmutableSamplers = nullptr;
bindings[i].stageFlags = m_stage;
i++;
}
m_set_layouts.emplace_back(kvfCreateDescriptorSetLayout(RenderCore::Get().GetDevice(), bindings.data(), bindings.size()));
Message("Vulkan: descriptor set layout created");
m_pipeline_layout_part.set_layouts.push_back(m_set_layouts.back());
}
std::size_t i = 0;
std::vector<VkPushConstantRange> push_constants(layout.push_constants.size());
m_pipeline_layout_part.push_constants.resize(layout.push_constants.size());
for(auto& pc : layout.push_constants)
{
VkPushConstantRange push_constant_range = {};
push_constant_range.offset = pc.offset;
push_constant_range.size = pc.size;
push_constant_range.stageFlags = m_stage;
push_constants[i] = push_constant_range;
m_pipeline_layout_part.push_constants[i] = push_constant_range;
i++;
}
}
void Shader::Destroy()
{
if(m_module == VK_NULL_HANDLE)
return;
kvfDestroyShaderModule(RenderCore::Get().GetDevice(), m_module);
m_module = VK_NULL_HANDLE;
Message("Vulkan: shader module % destroyed", m_name);
for(auto& layout : m_set_layouts)
{
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), layout);
Message("Vulkan: descriptor set layout destroyed");
}
}
Shader::~Shader()
{
Destroy();
}
std::shared_ptr<Shader> LoadShaderFromFile(const std::filesystem::path& filepath, ShaderType type, ShaderLayout layout)
{
std::ifstream stream(filepath, std::ios::binary);
if(!stream.is_open())
FatalError("Renderer : unable to open a spirv shader file, %", filepath);
std::vector<std::uint32_t> data;
stream.seekg(0);
std::uint32_t part = 0;
while(stream.read(reinterpret_cast<char*>(&part), sizeof(part)))
data.push_back(part);
stream.close();
std::shared_ptr<Shader> shader = std::make_shared<Shader>(data, type, layout, filepath.stem().string());
Message("Vulkan: shader loaded %", filepath);
return shader;
}
}

277
Runtime/Sources/Renderer/RenderCore.cpp git.filemode.normal_file
View File

@@ -0,0 +1,277 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <vector>
#include <cstdint>
#include <Core/Engine.h>
#include <Platform/Window.h>
#include <Renderer/Descriptor.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Vulkan/VulkanLoader.h>
#include <Maths/Mat4.h>
#include <Core/Logs.h>
#define KVF_IMPLEMENTATION
#ifdef DEBUG
#define KVF_ENABLE_VALIDATION_LAYERS
#endif
#define KVF_ASSERT(x) (Scop::Assert(x, "internal kvf assertion " #x))
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#include <kvf.h>
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#include <kvf.h>
#pragma clang diagnostic pop
#else
#include <kvf.h>
#endif
namespace Scop
{
static std::unique_ptr<VulkanLoader> loader;
std::optional<std::uint32_t> FindMemoryType(std::uint32_t type_filter, VkMemoryPropertyFlags properties, bool error)
{
VkPhysicalDeviceMemoryProperties mem_properties;
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(RenderCore::Get().GetPhysicalDevice(), &mem_properties);
for(std::uint32_t i = 0; i < mem_properties.memoryTypeCount; i++)
{
if((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
if(error)
FatalError("Vulkan: failed to find suitable memory type");
return std::nullopt;
}
void ErrorCallback(const char* message) noexcept
{
Logs::Report(LogType::FatalError, 0, "", "", message);
std::cout << std::endl;
}
void ValidationErrorCallback(const char* message) noexcept
{
Logs::Report(LogType::Error, 0, "", "", message);
std::cout << std::endl;
}
void WarningCallback(const char* message) noexcept
{
Logs::Report(LogType::Warning, 0, "", "", message);
std::cout << std::endl;
}
RenderCore* RenderCore::s_instance = nullptr;
RenderCore::RenderCore()
{
if(s_instance != nullptr)
return;
s_instance = this;
Window window("", 1, 1, true);
std::vector<const char*> instance_extensions = window.GetRequiredVulkanInstanceExtentions();
loader = std::make_unique<VulkanLoader>();
LoadKVFGlobalVulkanFunctionPointers();
kvfSetErrorCallback(&ErrorCallback);
kvfSetWarningCallback(&WarningCallback);
kvfSetValidationErrorCallback(&ValidationErrorCallback);
kvfSetValidationWarningCallback(&WarningCallback);
//kvfAddLayer("VK_LAYER_MESA_overlay");
m_instance = kvfCreateInstance(instance_extensions.data(), instance_extensions.size());
Message("Vulkan: instance created");
loader->LoadInstance(m_instance);
LoadKVFInstanceVulkanFunctionPointers();
VkSurfaceKHR surface = window.CreateVulkanSurface(m_instance);
m_physical_device = kvfPickGoodDefaultPhysicalDevice(m_instance, surface);
// just for style
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(m_physical_device, &props);
Message("Vulkan: physical device picked '%'", props.deviceName);
const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
VkPhysicalDeviceFeatures features{};
vkGetPhysicalDeviceFeatures(m_physical_device, &features);
m_device = kvfCreateDevice(m_physical_device, device_extensions, sizeof(device_extensions) / sizeof(device_extensions[0]), &features);
Message("Vulkan: logical device created");
loader->LoadDevice(m_device);
LoadKVFDeviceVulkanFunctionPointers();
vkDestroySurfaceKHR(m_instance, surface, nullptr);
m_allocator.AttachToDevice(m_device, m_physical_device);
p_descriptor_pool_manager = std::make_unique<DescriptorPoolManager>();
ShaderLayout vertex_shader_layout(
{
{ 0,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
})
}
}, { ShaderPushConstantLayout({ 0, sizeof(Mat4f) * 2 }) }
);
m_internal_shaders[DEFAULT_VERTEX_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
ShaderLayout default_fragment_shader_layout(
{
{ 1,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER },
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
})
}
}, {}
);
m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardDefaultFragment.spv", ShaderType::Fragment, std::move(default_fragment_shader_layout));
ShaderLayout basic_fragment_shader_layout(
{
{ 1,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER },
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
})
}
}, {}
);
m_internal_shaders[BASIC_FRAGMENT_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardBasicFragment.spv", ShaderType::Fragment, std::move(basic_fragment_shader_layout));
}
#undef SCOP_LOAD_FUNCTION
#define SCOP_LOAD_FUNCTION(fn) pfns.fn = this->fn
void RenderCore::LoadKVFGlobalVulkanFunctionPointers() const noexcept
{
KvfGlobalVulkanFunctions pfns;
SCOP_LOAD_FUNCTION(vkCreateInstance);
SCOP_LOAD_FUNCTION(vkEnumerateInstanceExtensionProperties);
SCOP_LOAD_FUNCTION(vkEnumerateInstanceLayerProperties);
SCOP_LOAD_FUNCTION(vkGetInstanceProcAddr);
kvfPassGlobalVulkanFunctionPointers(&pfns);
}
void RenderCore::LoadKVFInstanceVulkanFunctionPointers() const noexcept
{
KvfInstanceVulkanFunctions pfns;
SCOP_LOAD_FUNCTION(vkCreateDevice);
SCOP_LOAD_FUNCTION(vkDestroyInstance);
SCOP_LOAD_FUNCTION(vkEnumerateDeviceExtensionProperties);
SCOP_LOAD_FUNCTION(vkEnumeratePhysicalDevices);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceFeatures);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceFormatProperties);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceImageFormatProperties);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceProperties);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
SCOP_LOAD_FUNCTION(vkDestroySurfaceKHR);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR);
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
kvfPassInstanceVulkanFunctionPointers(&pfns);
}
void RenderCore::LoadKVFDeviceVulkanFunctionPointers() const noexcept
{
KvfDeviceVulkanFunctions pfns;
SCOP_LOAD_FUNCTION(vkAllocateCommandBuffers);
SCOP_LOAD_FUNCTION(vkAllocateDescriptorSets);
SCOP_LOAD_FUNCTION(vkBeginCommandBuffer);
SCOP_LOAD_FUNCTION(vkCmdBeginRenderPass);
SCOP_LOAD_FUNCTION(vkCmdCopyBuffer);
SCOP_LOAD_FUNCTION(vkCmdCopyBufferToImage);
SCOP_LOAD_FUNCTION(vkCmdCopyImage);
SCOP_LOAD_FUNCTION(vkCmdCopyImageToBuffer);
SCOP_LOAD_FUNCTION(vkCmdEndRenderPass);
SCOP_LOAD_FUNCTION(vkCmdPipelineBarrier);
SCOP_LOAD_FUNCTION(vkCreateBuffer);
SCOP_LOAD_FUNCTION(vkCreateCommandPool);
SCOP_LOAD_FUNCTION(vkCreateDescriptorPool);
SCOP_LOAD_FUNCTION(vkCreateDescriptorSetLayout);
SCOP_LOAD_FUNCTION(vkCreateFence);
SCOP_LOAD_FUNCTION(vkCreateFramebuffer);
SCOP_LOAD_FUNCTION(vkCreateGraphicsPipelines);
SCOP_LOAD_FUNCTION(vkCreateImage);
SCOP_LOAD_FUNCTION(vkCreateImageView);
SCOP_LOAD_FUNCTION(vkCreatePipelineLayout);
SCOP_LOAD_FUNCTION(vkCreateRenderPass);
SCOP_LOAD_FUNCTION(vkCreateSampler);
SCOP_LOAD_FUNCTION(vkCreateSemaphore);
SCOP_LOAD_FUNCTION(vkCreateShaderModule);
SCOP_LOAD_FUNCTION(vkDestroyBuffer);
SCOP_LOAD_FUNCTION(vkDestroyCommandPool);
SCOP_LOAD_FUNCTION(vkDestroyDescriptorPool);
SCOP_LOAD_FUNCTION(vkDestroyDescriptorSetLayout);
SCOP_LOAD_FUNCTION(vkDestroyDevice);
SCOP_LOAD_FUNCTION(vkDestroyFence);
SCOP_LOAD_FUNCTION(vkDestroyFramebuffer);
SCOP_LOAD_FUNCTION(vkDestroyImage);
SCOP_LOAD_FUNCTION(vkDestroyImageView);
SCOP_LOAD_FUNCTION(vkDestroyPipeline);
SCOP_LOAD_FUNCTION(vkDestroyPipelineLayout);
SCOP_LOAD_FUNCTION(vkDestroyRenderPass);
SCOP_LOAD_FUNCTION(vkDestroySampler);
SCOP_LOAD_FUNCTION(vkDestroySemaphore);
SCOP_LOAD_FUNCTION(vkDestroyShaderModule);
SCOP_LOAD_FUNCTION(vkDeviceWaitIdle);
SCOP_LOAD_FUNCTION(vkEndCommandBuffer);
SCOP_LOAD_FUNCTION(vkFreeCommandBuffers);
SCOP_LOAD_FUNCTION(vkGetDeviceQueue);
SCOP_LOAD_FUNCTION(vkGetImageSubresourceLayout);
SCOP_LOAD_FUNCTION(vkQueueSubmit);
SCOP_LOAD_FUNCTION(vkResetCommandBuffer);
SCOP_LOAD_FUNCTION(vkResetDescriptorPool);
SCOP_LOAD_FUNCTION(vkResetEvent);
SCOP_LOAD_FUNCTION(vkResetFences);
SCOP_LOAD_FUNCTION(vkUpdateDescriptorSets);
SCOP_LOAD_FUNCTION(vkWaitForFences);
SCOP_LOAD_FUNCTION(vkCreateSwapchainKHR);
SCOP_LOAD_FUNCTION(vkDestroySwapchainKHR);
SCOP_LOAD_FUNCTION(vkGetSwapchainImagesKHR);
SCOP_LOAD_FUNCTION(vkQueuePresentKHR);
kvfPassDeviceVulkanFunctionPointers(m_physical_device, m_device, &pfns);
}
#undef SCOP_LOAD_FUNCTION
RenderCore::~RenderCore()
{
if(s_instance == nullptr)
return;
WaitDeviceIdle();
p_descriptor_pool_manager->Destroy();
p_descriptor_pool_manager.reset();
m_allocator.DetachFromDevice();
for(auto shader: m_internal_shaders)
shader->Destroy();
kvfDestroyDevice(m_device);
Message("Vulkan: logical device destroyed");
kvfDestroyInstance(m_instance);
Message("Vulkan: instance destroyed");
loader.reset();
s_instance = nullptr;
}
}

View File

@@ -0,0 +1,141 @@
#include <Renderer/RenderPasses/2DPass.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/ViewerData.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Core/Engine.h>
#include <Maths/Mat4.h>
namespace Scop
{
struct SpriteData
{
Mat4f model_matrix;
Vec4f color;
};
struct ViewerData2D
{
Mat4f projection;
};
void Render2DPass::Init()
{
ShaderLayout vertex_shader_layout(
{
{ 0,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
})
}
}, { ShaderPushConstantLayout({ 0, sizeof(SpriteData) }) }
);
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/2DVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
ShaderLayout fragment_shader_layout(
{
{ 1,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
})
}
}, {}
);
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/2DFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
m_pipeline.Destroy();
};
EventBus::RegisterListener({ functor, "__ScopRender2DPass" });
p_viewer_data_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_vertex_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Vertex);
p_texture_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(1), ShaderType::Fragment);
p_viewer_data_buffer = std::make_shared<UniformBuffer>();
p_viewer_data_buffer->Init(sizeof(ViewerData2D));
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
p_viewer_data_set->SetUniformBuffer(i, 0, p_viewer_data_buffer->Get(i));
p_viewer_data_set->Update(i);
}
}
void Render2DPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
{
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
{
GraphicPipelineDescriptor pipeline_descriptor;
pipeline_descriptor.vertex_shader = p_vertex_shader;
pipeline_descriptor.fragment_shader = p_fragment_shader;
pipeline_descriptor.color_attachments = { &render_target };
pipeline_descriptor.culling = CullMode::None;
pipeline_descriptor.clear_color_attachments = false;
pipeline_descriptor.name = "2D_pass_pipeline";
m_pipeline.Init(std::move(pipeline_descriptor));
}
std::uint32_t frame_index = renderer.GetCurrentFrameIndex();
ViewerData2D viewer_data;
viewer_data.projection = Mat4f::Ortho(0.0f, render_target.GetWidth(), render_target.GetHeight(), 0.0f);
static CPUBuffer buffer(sizeof(ViewerData2D));
std::memcpy(buffer.GetData(), &viewer_data, buffer.GetSize());
p_viewer_data_buffer->SetData(buffer, frame_index);
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
m_pipeline.BindPipeline(cmd, 0, {});
for(const auto& [_, sprite] : scene.GetSprites())
{
SpriteData sprite_data;
sprite_data.color = sprite.GetColor();
Mat4f translation_matrix = Mat4f::Identity().ApplyTranslation(Vec3f{ Vec2f(sprite.GetPosition()), 0.0f });
Mat4f scale_matrix = Mat4f::Identity().ApplyScale(Vec3f{ sprite.GetScale(), 1.0f });
sprite_data.model_matrix = Mat4f::Identity();
sprite_data.model_matrix.ConcatenateTransform(scale_matrix);
sprite_data.model_matrix.ConcatenateTransform(translation_matrix);
if(!sprite.IsSetInit())
const_cast<Sprite&>(sprite).UpdateDescriptorSet(p_texture_set);
const_cast<Sprite&>(sprite).Bind(frame_index, cmd);
std::array<VkDescriptorSet, 2> sets = { p_viewer_data_set->GetSet(frame_index), sprite.GetSet(frame_index) };
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
RenderCore::Get().vkCmdPushConstants(cmd, m_pipeline.GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(SpriteData), &sprite_data);
sprite.GetMesh()->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
}
for(const auto& [_, text] : scene.GetTexts())
{
SpriteData sprite_data;
sprite_data.color = text.GetColor();
Mat4f translation_matrix = Mat4f::Identity().ApplyTranslation(Vec3f{ Vec2f(text.GetPosition()), 0.0f });
Mat4f scale_matrix = Mat4f::Identity().ApplyScale(Vec3f{ text.GetScale(), 1.0f });
sprite_data.model_matrix = Mat4f::Identity();
sprite_data.model_matrix.ConcatenateTransform(scale_matrix);
sprite_data.model_matrix.ConcatenateTransform(translation_matrix);
if(!text.IsSetInit())
const_cast<Text&>(text).UpdateDescriptorSet(p_texture_set);
const_cast<Text&>(text).Bind(frame_index, cmd);
std::array<VkDescriptorSet, 2> sets = { p_viewer_data_set->GetSet(frame_index), text.GetSet(frame_index) };
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
RenderCore::Get().vkCmdPushConstants(cmd, m_pipeline.GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(SpriteData), &sprite_data);
text.GetMesh()->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
}
m_pipeline.EndPipeline(cmd);
}
void Render2DPass::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_pipeline.Destroy();
p_vertex_shader.reset();
p_fragment_shader.reset();
p_viewer_data_set.reset();
p_viewer_data_buffer->Destroy();
p_texture_set.reset();
}
}

View File

@@ -0,0 +1,73 @@
#include <Renderer/RenderPasses/FinalPass.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Core/EventBus.h>
#include <Core/Engine.h>
namespace Scop
{
void FinalPass::Init()
{
ShaderLayout vertex_shader_layout(
{}, {}
);
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
ShaderLayout fragment_shader_layout(
{
{ 0,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
})
}
}, {}
);
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
m_pipeline.Destroy();
};
EventBus::RegisterListener({ functor, "__ScopFinalPass" });
p_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Fragment);
}
void FinalPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
{
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
{
GraphicPipelineDescriptor pipeline_descriptor;
pipeline_descriptor.vertex_shader = p_vertex_shader;
pipeline_descriptor.fragment_shader = p_fragment_shader;
pipeline_descriptor.renderer = &renderer;
pipeline_descriptor.culling = CullMode::None;
pipeline_descriptor.no_vertex_inputs = true;
pipeline_descriptor.name = "final_pass_pipeline";
m_pipeline.Init(std::move(pipeline_descriptor));
}
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
p_set->SetImage(renderer.GetCurrentFrameIndex(), 0, render_target);
p_set->Update(renderer.GetCurrentFrameIndex(), cmd);
m_pipeline.BindPipeline(cmd, renderer.GetSwapchain().GetImageIndex(), { 0.0f, 0.0f, 0.0f, 1.0f });
VkDescriptorSet set = p_set->GetSet(renderer.GetCurrentFrameIndex());
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, 1, &set, 0, nullptr);
RenderCore::Get().vkCmdDraw(cmd, 3, 1, 0, 0);
renderer.GetDrawCallsCounterRef()++;
renderer.GetPolygonDrawnCounterRef()++;
m_pipeline.EndPipeline(cmd);
}
void FinalPass::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_pipeline.Destroy();
p_vertex_shader.reset();
p_fragment_shader.reset();
p_set.reset();
}
}

View File

@@ -0,0 +1,118 @@
#include <Renderer/RenderPasses/ForwardPass.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/ViewerData.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Maths/Mat4.h>
#include <map>
namespace Scop
{
struct ModelData
{
Mat4f model_mat;
Mat4f normal_mat;
};
void ForwardPass::Pass(Scene& scene, Renderer& renderer, class Texture& render_target)
{
NonOwningPtr<GraphicPipeline> pipeline = &scene.GetPipeline();
if(scene.GetPipeline().GetPipeline() == VK_NULL_HANDLE)
{
GraphicPipelineDescriptor pipeline_descriptor;
pipeline_descriptor.vertex_shader = RenderCore::Get().GetDefaultVertexShader();
pipeline_descriptor.fragment_shader = scene.GetFragmentShader();
pipeline_descriptor.color_attachments = { &render_target };
pipeline_descriptor.depth = &scene.GetDepth();
pipeline_descriptor.clear_color_attachments = false;
pipeline_descriptor.name = "forward_pass_pipeline";
scene.GetPipeline().Init(std::move(pipeline_descriptor));
}
auto render_actor = [&render_target, &renderer, &scene, &pipeline](Actor& actor)
{
Scene::ForwardData& data = scene.GetForwardData();
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
std::shared_ptr<GraphicPipeline> custom_pipeline = (actor.GetCustomPipeline().has_value() ? actor.GetCustomPipeline()->pipeline : nullptr);
if(custom_pipeline && !custom_pipeline->IsPipelineBound())
{
pipeline->EndPipeline(cmd);
pipeline = actor.GetCustomPipeline()->pipeline.get();
if(pipeline->GetDescription().depth != NonOwningPtr<DepthImage>{ &scene.GetDepth() })
{
GraphicPipelineDescriptor descriptor = pipeline->GetDescription();
descriptor.color_attachments = { &render_target };
descriptor.depth = &scene.GetDepth();
descriptor.renderer = nullptr;
descriptor.clear_color_attachments = false;
pipeline->Init(std::move(descriptor));
}
pipeline->BindPipeline(cmd, 0, {});
}
else if(!custom_pipeline && !scene.GetPipeline().IsPipelineBound())
{
pipeline->EndPipeline(cmd);
pipeline = &scene.GetPipeline();
pipeline->BindPipeline(cmd, 0, {});
}
ModelData model_data;
model_data.model_mat = Mat4f::Identity();
model_data.model_mat.SetTranslation(actor.GetPosition() - actor.GetModel().GetCenter());
model_data.model_mat.SetScale(actor.GetScale());
model_data.model_mat = Mat4f::Translate(-actor.GetModel().GetCenter()) * Mat4f::Rotate(actor.GetOrientation()) * model_data.model_mat;
model_data.normal_mat = model_data.model_mat;
model_data.normal_mat.Inverse().Transpose();
RenderCore::Get().vkCmdPushConstants(cmd, pipeline->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(ModelData), &model_data);
if(custom_pipeline)
{
if(!actor.GetCustomPipeline()->data_uniform_buffer)
{
actor.GetCustomPipeline()->data_uniform_buffer = std::make_shared<UniformBuffer>();
actor.GetCustomPipeline()->data_uniform_buffer->Init(actor.GetCustomPipeline()->data.GetSize(), "custom_data_buffer");
}
else
actor.GetCustomPipeline()->data_uniform_buffer->SetData(actor.GetCustomPipeline()->data, renderer.GetCurrentFrameIndex());
if(!actor.GetCustomPipeline()->set)
{
actor.GetCustomPipeline()->set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(custom_pipeline->GetDescription().vertex_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Vertex);
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
actor.GetCustomPipeline()->set->SetUniformBuffer(i, 0, data.matrices_buffer->Get(i));
actor.GetCustomPipeline()->set->SetUniformBuffer(i, 1, actor.GetCustomPipeline()->data_uniform_buffer->Get(i));
actor.GetCustomPipeline()->set->Update(i);
}
}
actor.GetModel().Draw(cmd, actor.GetCustomPipeline()->set, *pipeline, data.albedo_set, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef(), renderer.GetCurrentFrameIndex());
}
else
actor.GetModel().Draw(cmd, data.matrices_set, *pipeline, data.albedo_set, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef(), renderer.GetCurrentFrameIndex());
};
std::multimap<float, Actor&> sorted_actors;
for(auto& [_, actor] : scene.GetActors())
{
if(!actor.IsVisible())
continue;
if(!actor.IsOpaque())
{
float distance = Vec3f::Distance(actor.GetPosition(), scene.GetCamera()->GetPosition());
sorted_actors.emplace(distance, const_cast<Actor&>(actor));
}
else
render_actor(const_cast<Actor&>(actor));
}
for(auto it = sorted_actors.rbegin(); it != sorted_actors.rend(); ++it)
{
if(!it->second.IsVisible())
continue;
render_actor(it->second);
}
if(pipeline)
pipeline->EndPipeline(renderer.GetActiveCommandBuffer());
}
}

View File

@@ -0,0 +1,62 @@
#include <Renderer/RenderPasses/Passes.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
namespace Scop
{
void RenderPasses::Init()
{
m_skybox.Init();
m_2Dpass.Init();
m_post_process.Init();
m_final.Init();
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
m_main_render_texture.Destroy();
};
EventBus::RegisterListener({ functor, "__ScopRenderPasses" });
}
void RenderPasses::Pass(Scene& scene, Renderer& renderer)
{
if(!m_main_render_texture.IsInit())
{
auto extent = kvfGetSwapchainImagesSize(renderer.GetSwapchain().Get());
m_main_render_texture.Init({}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_UNORM, false, "scop_main_render_texture", false);
}
scene.GetDepth().Clear(renderer.GetActiveCommandBuffer(), {});
m_main_render_texture.Clear(renderer.GetActiveCommandBuffer(), Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f });
m_main_render_texture.TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, renderer.GetActiveCommandBuffer());
if(scene.GetDescription().render_3D_enabled)
m_forward.Pass(scene, renderer, m_main_render_texture);
if(scene.GetDescription().render_skybox_enabled)
m_skybox.Pass(scene, renderer, m_main_render_texture);
if(scene.GetDescription().render_post_process_enabled && scene.GetDescription().post_process_shader)
{
m_post_process.Pass(scene, renderer, m_main_render_texture);
if(scene.GetDescription().render_2D_enabled)
m_2Dpass.Pass(scene, renderer, m_post_process.GetProcessTexture());
m_final.Pass(scene, renderer, m_post_process.GetProcessTexture());
}
else
{
if(scene.GetDescription().render_2D_enabled)
m_2Dpass.Pass(scene, renderer, m_main_render_texture);
m_final.Pass(scene, renderer, m_main_render_texture);
}
}
void RenderPasses::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_skybox.Destroy();
m_2Dpass.Destroy();
m_post_process.Destroy();
m_final.Destroy();
m_main_render_texture.Destroy();
}
}

View File

@@ -0,0 +1,74 @@
#include <Renderer/RenderPasses/PostProcessPass.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Core/EventBus.h>
#include <Core/Engine.h>
namespace Scop
{
void PostProcessPass::Init()
{
ShaderLayout vertex_shader_layout(
{}, {}
);
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
{
m_render_texture.Destroy();
m_pipeline.Destroy();
}
};
EventBus::RegisterListener({ functor, "__ScopPostProcessPass" });
}
void PostProcessPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
{
Scene::PostProcessData& data = scene.GetPostProcessData();
if(!m_render_texture.IsInit())
{
auto extent = kvfGetSwapchainImagesSize(renderer.GetSwapchain().Get());
m_render_texture.Init({}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_UNORM, false, "scop_post_process_render_texture", false);
}
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
{
GraphicPipelineDescriptor pipeline_descriptor;
pipeline_descriptor.vertex_shader = p_vertex_shader;
pipeline_descriptor.fragment_shader = scene.GetDescription().post_process_shader;
pipeline_descriptor.color_attachments = { &m_render_texture };
pipeline_descriptor.culling = CullMode::None;
pipeline_descriptor.clear_color_attachments = false;
pipeline_descriptor.no_vertex_inputs = true;
pipeline_descriptor.name = "post_process_pass_pipeline";
m_pipeline.Init(std::move(pipeline_descriptor));
}
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
data.set->SetImage(renderer.GetCurrentFrameIndex(), 0, render_target);
data.set->SetImage(renderer.GetCurrentFrameIndex(), 1, scene.GetDepth());
data.set->SetUniformBuffer(renderer.GetCurrentFrameIndex(), 2, data.data_buffer->Get(renderer.GetCurrentFrameIndex()));
data.set->Update(renderer.GetCurrentFrameIndex(), cmd);
m_pipeline.BindPipeline(cmd, 0, {});
VkDescriptorSet set = data.set->GetSet(renderer.GetCurrentFrameIndex());
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, 1, &set, 0, nullptr);
RenderCore::Get().vkCmdDraw(cmd, 3, 1, 0, 0);
renderer.GetDrawCallsCounterRef()++;
renderer.GetPolygonDrawnCounterRef()++;
m_pipeline.EndPipeline(cmd);
}
void PostProcessPass::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_render_texture.Destroy();
m_pipeline.Destroy();
p_vertex_shader.reset();
}
}

View File

@@ -0,0 +1,86 @@
#include <Renderer/RenderPasses/SkyboxPass.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Graphics/MeshFactory.h>
#include <Renderer/ViewerData.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Core/EventBus.h>
#include <Core/Engine.h>
namespace Scop
{
void SkyboxPass::Init()
{
ShaderLayout vertex_shader_layout(
{
{ 0,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
})
}
}, {}
);
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/SkyboxVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
ShaderLayout fragment_shader_layout(
{
{ 1,
ShaderSetLayout({
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
})
}
}, {}
);
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/SkyboxFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
m_pipeline.Destroy();
};
EventBus::RegisterListener({ functor, "__ScopSkyboxPass" });
m_cube = CreateCube();
p_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(1), ShaderType::Fragment);
}
void SkyboxPass::Pass(Scene& scene, Renderer& renderer, class Texture& render_target)
{
if(!scene.GetSkybox())
return;
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
{
GraphicPipelineDescriptor pipeline_descriptor;
pipeline_descriptor.vertex_shader = p_vertex_shader;
pipeline_descriptor.fragment_shader = p_fragment_shader;
pipeline_descriptor.color_attachments = { &render_target };
pipeline_descriptor.depth = &scene.GetDepth();
pipeline_descriptor.culling = CullMode::None;
pipeline_descriptor.depth_test_equal = true;
pipeline_descriptor.clear_color_attachments = false;
pipeline_descriptor.name = "skybox_pass_pipeline";
m_pipeline.Init(std::move(pipeline_descriptor));
}
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
p_set->SetImage(renderer.GetCurrentFrameIndex(), 0, *scene.GetSkybox());
p_set->Update(renderer.GetCurrentFrameIndex(), cmd);
m_pipeline.BindPipeline(cmd, 0, {});
std::array<VkDescriptorSet, 2> sets = { scene.GetForwardData().matrices_set->GetSet(renderer.GetCurrentFrameIndex()), p_set->GetSet(renderer.GetCurrentFrameIndex()) };
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
m_cube->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
m_pipeline.EndPipeline(cmd);
}
void SkyboxPass::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_pipeline.Destroy();
p_vertex_shader.reset();
p_fragment_shader.reset();
m_cube.reset();
p_set.reset();
}
}

96
Runtime/Sources/Renderer/Renderer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,96 @@
#include <Renderer/Renderer.h>
#include <Core/Logs.h>
#include <Core/Enums.h>
#include <Core/Engine.h>
#include <Core/EventBus.h>
namespace Scop
{
namespace Internal
{
struct FrameBeginEventBroadcast : public EventBase
{
Event What() const override { return Event::FrameBeginEventCode; }
};
}
void Renderer::Init(NonOwningPtr<Window> window)
{
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
{
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
{
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
Message("Vulkan: render finished semaphore destroyed");
}
m_render_finished_semaphores.clear();
for(std::size_t i = 0; i < m_swapchain.GetImagesCount(); i++)
{
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
Message("Vulkan: render finished semaphore created");
}
}
};
EventBus::RegisterListener({ functor, "__ScopRenderer" });
p_window = window;
m_swapchain.Init(p_window);
for(std::size_t i = 0; i < m_swapchain.GetImagesCount(); i++)
{
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
Message("Vulkan: render finished semaphore created");
}
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_cmd_buffers[i] = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
Message("Vulkan: command buffer created");
m_cmd_fences[i] = kvfCreateFence(RenderCore::Get().GetDevice());
Message("Vulkan: fence created");
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
Message("Vulkan: image available semaphore created");
}
}
void Renderer::BeginFrame()
{
kvfWaitForFence(RenderCore::Get().GetDevice(), m_cmd_fences[m_current_frame_index]);
m_swapchain.AquireFrame(m_image_available_semaphores[m_current_frame_index]);
RenderCore::Get().vkResetCommandBuffer(m_cmd_buffers[m_current_frame_index], 0);
kvfBeginCommandBuffer(m_cmd_buffers[m_current_frame_index], 0);
m_drawcalls = 0;
m_polygons_drawn = 0;
EventBus::SendBroadcast(Internal::FrameBeginEventBroadcast{});
}
void Renderer::EndFrame()
{
VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
kvfEndCommandBuffer(m_cmd_buffers[m_current_frame_index]);
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, m_render_finished_semaphores[m_swapchain.GetImageIndex()], m_image_available_semaphores[m_current_frame_index], m_cmd_fences[m_current_frame_index], wait_stages);
m_swapchain.Present(m_render_finished_semaphores[m_swapchain.GetImageIndex()]);
m_current_frame_index = (m_current_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
}
void Renderer::Destroy() noexcept
{
RenderCore::Get().WaitDeviceIdle();
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
{
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
Message("Vulkan: render finished semaphore destroyed");
}
m_render_finished_semaphores.clear();
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_image_available_semaphores[i]);
Message("Vulkan: image available semaphore destroyed");
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[i]);
Message("Vulkan: command buffer destroyed");
kvfDestroyFence(RenderCore::Get().GetDevice(), m_cmd_fences[i]);
Message("Vulkan: fence destroyed");
}
m_swapchain.Destroy();
}
}

43
Runtime/Sources/Renderer/SceneRenderer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,43 @@
#include <Renderer/ScenesRenderer.h>
#include <Renderer/Renderer.h>
#include <Graphics/Scene.h>
#include <Renderer/ViewerData.h>
#include <cstring>
namespace Scop
{
void SceneRenderer::Init()
{
m_passes.Init();
}
void SceneRenderer::Render(Scene& scene, Renderer& renderer)
{
if(scene.GetCamera())
{
ViewerData data;
data.projection_matrix = scene.GetCamera()->GetProj();
data.projection_matrix.GetInverse(&data.inv_projection_matrix);
data.view_matrix = scene.GetCamera()->GetView();
data.view_matrix.GetInverse(&data.inv_view_matrix);
data.view_proj_matrix = data.view_matrix * data.projection_matrix;
data.view_proj_matrix.GetInverse(&data.inv_view_proj_matrix);
data.camera_position = scene.GetCamera()->GetPosition();
static CPUBuffer buffer(sizeof(ViewerData));
std::memcpy(buffer.GetData(), &data, buffer.GetSize());
scene.GetForwardData().matrices_buffer->SetData(buffer, renderer.GetCurrentFrameIndex());
}
if(scene.GetDescription().render_post_process_enabled && scene.GetDescription().post_process_shader)
scene.GetPostProcessData().data_buffer->SetData(scene.GetPostProcessData().data, renderer.GetCurrentFrameIndex());
m_passes.Pass(scene, renderer);
}
void SceneRenderer::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_passes.Destroy();
}
}

254
Runtime/Sources/Renderer/Swapchain.cpp git.filemode.normal_file
View File

@@ -0,0 +1,254 @@
#include <Platform/Window.h>
#include <Renderer/Swapchain.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <Core/EventBus.h>
#include <Maths/Vec2.h>
namespace Scop
{
namespace Internal
{
struct ResizeEventBroadcast : public EventBase
{
Event What() const override { return Event::ResizeEventCode; }
};
}
std::string VulkanFormatName(VkFormat format)
{
#define STRINGIFY(x) case x: return #x
switch(format)
{
STRINGIFY(VK_FORMAT_UNDEFINED);
STRINGIFY(VK_FORMAT_R4G4_UNORM_PACK8);
STRINGIFY(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R5G6B5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B5G6R5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R5G5B5A1_UNORM_PACK16);
STRINGIFY(VK_FORMAT_B5G5R5A1_UNORM_PACK16);
STRINGIFY(VK_FORMAT_A1R5G5B5_UNORM_PACK16);
STRINGIFY(VK_FORMAT_R8_UNORM);
STRINGIFY(VK_FORMAT_R8_SNORM);
STRINGIFY(VK_FORMAT_R8_USCALED);
STRINGIFY(VK_FORMAT_R8_SSCALED);
STRINGIFY(VK_FORMAT_R8_UINT);
STRINGIFY(VK_FORMAT_R8_SINT);
STRINGIFY(VK_FORMAT_R8_SRGB);
STRINGIFY(VK_FORMAT_R8G8_UNORM);
STRINGIFY(VK_FORMAT_R8G8_SNORM);
STRINGIFY(VK_FORMAT_R8G8_USCALED);
STRINGIFY(VK_FORMAT_R8G8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8_UINT);
STRINGIFY(VK_FORMAT_R8G8_SINT);
STRINGIFY(VK_FORMAT_R8G8_SRGB);
STRINGIFY(VK_FORMAT_R8G8B8_UNORM);
STRINGIFY(VK_FORMAT_R8G8B8_SNORM);
STRINGIFY(VK_FORMAT_R8G8B8_USCALED);
STRINGIFY(VK_FORMAT_R8G8B8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8B8_UINT);
STRINGIFY(VK_FORMAT_R8G8B8_SINT);
STRINGIFY(VK_FORMAT_R8G8B8_SRGB);
STRINGIFY(VK_FORMAT_B8G8R8_UNORM);
STRINGIFY(VK_FORMAT_B8G8R8_SNORM);
STRINGIFY(VK_FORMAT_B8G8R8_USCALED);
STRINGIFY(VK_FORMAT_B8G8R8_SSCALED);
STRINGIFY(VK_FORMAT_B8G8R8_UINT);
STRINGIFY(VK_FORMAT_B8G8R8_SINT);
STRINGIFY(VK_FORMAT_B8G8R8_SRGB);
STRINGIFY(VK_FORMAT_R8G8B8A8_UNORM);
STRINGIFY(VK_FORMAT_R8G8B8A8_SNORM);
STRINGIFY(VK_FORMAT_R8G8B8A8_USCALED);
STRINGIFY(VK_FORMAT_R8G8B8A8_SSCALED);
STRINGIFY(VK_FORMAT_R8G8B8A8_UINT);
STRINGIFY(VK_FORMAT_R8G8B8A8_SINT);
STRINGIFY(VK_FORMAT_R8G8B8A8_SRGB);
STRINGIFY(VK_FORMAT_B8G8R8A8_UNORM);
STRINGIFY(VK_FORMAT_B8G8R8A8_SNORM);
STRINGIFY(VK_FORMAT_B8G8R8A8_USCALED);
STRINGIFY(VK_FORMAT_B8G8R8A8_SSCALED);
STRINGIFY(VK_FORMAT_B8G8R8A8_UINT);
STRINGIFY(VK_FORMAT_B8G8R8A8_SINT);
STRINGIFY(VK_FORMAT_B8G8R8A8_SRGB);
STRINGIFY(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_UINT_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SINT_PACK32);
STRINGIFY(VK_FORMAT_A8B8G8R8_SRGB_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_UINT_PACK32);
STRINGIFY(VK_FORMAT_A2R10G10B10_SINT_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SNORM_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_USCALED_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_UINT_PACK32);
STRINGIFY(VK_FORMAT_A2B10G10R10_SINT_PACK32);
STRINGIFY(VK_FORMAT_R16_UNORM);
STRINGIFY(VK_FORMAT_R16_SNORM);
STRINGIFY(VK_FORMAT_R16_USCALED);
STRINGIFY(VK_FORMAT_R16_SSCALED);
STRINGIFY(VK_FORMAT_R16_UINT);
STRINGIFY(VK_FORMAT_R16_SINT);
STRINGIFY(VK_FORMAT_R16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16_UNORM);
STRINGIFY(VK_FORMAT_R16G16_SNORM);
STRINGIFY(VK_FORMAT_R16G16_USCALED);
STRINGIFY(VK_FORMAT_R16G16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16_UINT);
STRINGIFY(VK_FORMAT_R16G16_SINT);
STRINGIFY(VK_FORMAT_R16G16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16B16_UNORM);
STRINGIFY(VK_FORMAT_R16G16B16_SNORM);
STRINGIFY(VK_FORMAT_R16G16B16_USCALED);
STRINGIFY(VK_FORMAT_R16G16B16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16B16_UINT);
STRINGIFY(VK_FORMAT_R16G16B16_SINT);
STRINGIFY(VK_FORMAT_R16G16B16_SFLOAT);
STRINGIFY(VK_FORMAT_R16G16B16A16_UNORM);
STRINGIFY(VK_FORMAT_R16G16B16A16_SNORM);
STRINGIFY(VK_FORMAT_R16G16B16A16_USCALED);
STRINGIFY(VK_FORMAT_R16G16B16A16_SSCALED);
STRINGIFY(VK_FORMAT_R16G16B16A16_UINT);
STRINGIFY(VK_FORMAT_R16G16B16A16_SINT);
STRINGIFY(VK_FORMAT_R16G16B16A16_SFLOAT);
STRINGIFY(VK_FORMAT_R32_UINT);
STRINGIFY(VK_FORMAT_R32_SINT);
STRINGIFY(VK_FORMAT_R32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32_UINT);
STRINGIFY(VK_FORMAT_R32G32_SINT);
STRINGIFY(VK_FORMAT_R32G32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32B32_UINT);
STRINGIFY(VK_FORMAT_R32G32B32_SINT);
STRINGIFY(VK_FORMAT_R32G32B32_SFLOAT);
STRINGIFY(VK_FORMAT_R32G32B32A32_UINT);
STRINGIFY(VK_FORMAT_R32G32B32A32_SINT);
STRINGIFY(VK_FORMAT_R32G32B32A32_SFLOAT);
STRINGIFY(VK_FORMAT_R64_UINT);
STRINGIFY(VK_FORMAT_R64_SINT);
STRINGIFY(VK_FORMAT_R64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64_UINT);
STRINGIFY(VK_FORMAT_R64G64_SINT);
STRINGIFY(VK_FORMAT_R64G64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64B64_UINT);
STRINGIFY(VK_FORMAT_R64G64B64_SINT);
STRINGIFY(VK_FORMAT_R64G64B64_SFLOAT);
STRINGIFY(VK_FORMAT_R64G64B64A64_UINT);
STRINGIFY(VK_FORMAT_R64G64B64A64_SINT);
STRINGIFY(VK_FORMAT_R64G64B64A64_SFLOAT);
STRINGIFY(VK_FORMAT_B10G11R11_UFLOAT_PACK32);
STRINGIFY(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);
default: return "Unknown format";
}
#undef STRINGIFY
return "Unknown format"; // To avoid warnings
}
void Swapchain::Init(NonOwningPtr<Window> window)
{
p_window = window;
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode && !m_resize)
m_resize = true;
};
EventBus::RegisterListener({ functor, "__ScopSwapchain" });
CreateSwapchain();
m_resize = false;
}
void Swapchain::AquireFrame(VkSemaphore signal)
{
if(m_resize)
{
RenderCore::Get().WaitDeviceIdle();
Destroy();
CreateSwapchain();
EventBus::SendBroadcast(Internal::ResizeEventBroadcast{});
m_resize = false;
}
VkResult result = RenderCore::Get().vkAcquireNextImageKHR(RenderCore::Get().GetDevice(), m_swapchain, UINT64_MAX, signal, VK_NULL_HANDLE, &m_current_image_index);
if(result == VK_SUBOPTIMAL_KHR)
m_resize = true; // Recreate Swapchain next time
else if(result == VK_ERROR_OUT_OF_DATE_KHR)
{
m_resize = true;
AquireFrame(signal);
}
else if(result != VK_SUCCESS)
FatalError("Vulkan: failed to acquire swapchain image, %", kvfVerbaliseVkResult(result));
}
void Swapchain::Present(VkSemaphore wait) noexcept
{
if(!kvfQueuePresentKHR(RenderCore::Get().GetDevice(), wait, m_swapchain, m_current_image_index))
m_resize = true;
}
void Swapchain::Destroy()
{
if(m_swapchain == VK_NULL_HANDLE)
return;
RenderCore::Get().WaitDeviceIdle();
for(Image& img : m_swapchain_images)
img.DestroyImageView();
m_swapchain_images.clear();
kvfDestroySwapchainKHR(RenderCore::Get().GetDevice(), m_swapchain);
m_swapchain = VK_NULL_HANDLE;
Message("Vulkan: swapchain destroyed");
RenderCore::Get().vkDestroySurfaceKHR(RenderCore::Get().GetInstance(), m_surface, nullptr);
m_surface = VK_NULL_HANDLE;
Message("Vulkan: surface destroyed");
}
void Swapchain::CreateSwapchain()
{
VkExtent2D extent;
do
{
Vec2ui size = p_window->GetVulkanDrawableSize();
extent = { size.x, size.y };
} while(extent.width == 0 || extent.height == 0);
m_surface = p_window->CreateVulkanSurface(RenderCore::Get().GetInstance());
Message("Vulkan: surface created");
m_swapchain = kvfCreateSwapchainKHR(RenderCore::Get().GetDevice(), RenderCore::Get().GetPhysicalDevice(), m_surface, extent, VK_NULL_HANDLE, false, true);
m_images_count = kvfGetSwapchainImagesCount(m_swapchain);
m_min_images_count = kvfGetSwapchainMinImagesCount(m_swapchain);
std::vector<VkImage> tmp(m_images_count);
m_swapchain_images.resize(m_images_count);
RenderCore::Get().vkGetSwapchainImagesKHR(RenderCore::Get().GetDevice(), m_swapchain, &m_images_count, tmp.data());
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
extent = kvfGetSwapchainImagesSize(m_swapchain); // fix the extent
VkFormat format = kvfGetSwapchainImagesFormat(m_swapchain);
for(std::size_t i = 0; i < m_images_count; i++)
{
m_swapchain_images[i].Init(tmp[i], format, extent.width, extent.height, VK_IMAGE_LAYOUT_UNDEFINED, std::string{ "swapchain_" + std::to_string(i) });
m_swapchain_images[i].TransitionLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, cmd);
m_swapchain_images[i].CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
}
kvfEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
Message("Vulkan: swapchain created with format %", VulkanFormatName(format));
}
}

View File

@@ -0,0 +1,119 @@
#include <Renderer/Vulkan/VulkanLoader.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <array>
#include <dlfcn.h>
#if defined(__GNUC__)
#define DISABLE_GCC_PEDANTIC_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wpedantic\"")
#define RESTORE_GCC_PEDANTIC_WARNINGS \
_Pragma("GCC diagnostic pop")
#else
#define DISABLE_GCC_PEDANTIC_WARNINGS
#define RESTORE_GCC_PEDANTIC_WARNINGS
#endif
namespace Scop
{
namespace Internal
{
static inline PFN_vkVoidFunction vkGetInstanceProcAddrStub(void* context, const char* name)
{
PFN_vkVoidFunction function = RenderCore::Get().vkGetInstanceProcAddr(static_cast<VkInstance>(context), name);
if(!function)
FatalError("Vulkan Loader: could not load '%'", name);
//Message("Vulkan Loader: loaded %", name);
return function;
}
static inline PFN_vkVoidFunction vkGetDeviceProcAddrStub(void* context, const char* name)
{
PFN_vkVoidFunction function = RenderCore::Get().vkGetDeviceProcAddr(static_cast<VkDevice>(context), name);
if(!function)
FatalError("Vulkan Loader: could not load '%'", name);
//Message("Vulkan Loader: loaded %", name);
return function;
}
static inline void* LoadLib(const char* libname)
{
return dlopen(libname, RTLD_NOW | RTLD_LOCAL);
}
static inline void* GetSymbol(void* module, const char* name)
{
return dlsym(module, name);
}
}
VulkanLoader::VulkanLoader()
{
std::array libnames{
"libvulkan.so.1",
"libvulkan.so"
};
for(auto libname : libnames)
{
p_module = Internal::LoadLib(libname);
if(p_module != nullptr)
{
DISABLE_GCC_PEDANTIC_WARNINGS;
RenderCore::Get().vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(Internal::GetSymbol(p_module, "vkGetInstanceProcAddr"));
RESTORE_GCC_PEDANTIC_WARNINGS;
if(RenderCore::Get().vkGetInstanceProcAddr)
{
Message("Vulkan Loader: libvulkan loaded using '%'", libname);
break;
}
}
}
if(!p_module || !RenderCore::Get().vkGetInstanceProcAddr)
FatalError("Vulkan Loader: failed to load libvulkan");
LoadGlobalFunctions(nullptr, Internal::vkGetInstanceProcAddrStub);
}
void VulkanLoader::LoadInstance(VkInstance instance)
{
LoadInstanceFunctions(instance, Internal::vkGetInstanceProcAddrStub);
}
void VulkanLoader::LoadDevice(VkDevice device)
{
LoadDeviceFunctions(device, Internal::vkGetDeviceProcAddrStub);
}
void VulkanLoader::LoadGlobalFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
{
#define SCOP_VULKAN_GLOBAL_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
#include <Renderer/Vulkan/VulkanDefs.h>
#undef SCOP_VULKAN_GLOBAL_FUNCTION
Message("Vulkan Loader: global functions loaded");
}
void VulkanLoader::LoadInstanceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
{
#define SCOP_VULKAN_INSTANCE_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
#include <Renderer/Vulkan/VulkanDefs.h>
#undef SCOP_VULKAN_INSTANCE_FUNCTION
Message("Vulkan Loader: instance functions loaded");
}
void VulkanLoader::LoadDeviceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
{
#define SCOP_VULKAN_DEVICE_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
#include <Renderer/Vulkan/VulkanDefs.h>
#undef SCOP_VULKAN_DEVICE_FUNCTION
Message("Vulkan Loader: device functions loaded");
}
VulkanLoader::~VulkanLoader()
{
dlclose(p_module);
p_module = nullptr;
Message("Vulkan Loader: libvulkan unloaded");
}
}

View File

@@ -0,0 +1,26 @@
#ifndef __SCOP_VULKAN_LOADER__
#define __SCOP_VULKAN_LOADER__
#include <vulkan/vulkan_core.h>
namespace Scop
{
class VulkanLoader
{
public:
VulkanLoader();
void LoadInstance(VkInstance instance);
void LoadDevice(VkDevice device);
~VulkanLoader();
private:
void LoadGlobalFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
void LoadInstanceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
void LoadDeviceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
private:
void* p_module = nullptr;
};
}
#endif