initial commit

This commit is contained in:
Kbz-8
2025-05-01 23:03:47 +02:00
commit 5eb5821c2a
200 changed files with 434542 additions and 0 deletions

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

@@ -0,0 +1,184 @@
#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)
{
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, false);
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, bool use_raw_size)
{
auto device = RenderCore::Get().GetDevice();
m_buffer = kvfCreateBuffer(device, usage, size);
VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements);
if(use_raw_size)
{
m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties));
Message("test % - %", size, m_memory.size);
}
else
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties));
RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset);
Message("Vulkan: created buffer");
s_buffer_count++;
}
bool GPUBuffer::CopyFrom(const GPUBuffer& buffer) 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());
kvfEndCommandBuffer(cmd);
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);
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, true);
if(new_buffer.CopyFrom(*this))
Swap(new_buffer);
new_buffer.Destroy();
Message("Vulkan: pushed buffer to GPU memory");
}
void GPUBuffer::Destroy() noexcept
{
if(m_buffer == VK_NULL_HANDLE)
return;
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;
Message("Vulkan: destroyed buffer");
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 to 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 to 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 UniformBuffer::Init(std::uint32_t size)
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_buffers[i].Init(BufferType::HighDynamic, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, {});
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();
}
}

View File

@@ -0,0 +1,167 @@
#include <kvf.h>
#include <algorithm>
#include <Core/Logs.h>
#include <Renderer/Image.h>
#include <Renderer/Enums.h>
#include <Renderer/Buffer.h>
#include <Renderer/Descriptor.h>
#include <Renderer/RenderCore.h>
namespace Scop
{
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");
}
DescriptorSet::DescriptorSet(const ShaderSetLayout& layout, VkDescriptorSetLayout vklayout, ShaderType shader_type)
: m_set_layout(vklayout)
{
for(auto& [binding, type] : layout.binds)
{
m_descriptors.emplace_back();
m_descriptors.back().type = type;
m_descriptors.back().shader_type = shader_type;
m_descriptors.back().binding = binding;
}
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_set[i] = kvfAllocateDescriptorSet(RenderCore::Get().GetDevice(), vklayout);
}
DescriptorSet::DescriptorSet(VkDescriptorSetLayout layout, const std::vector<Descriptor>& descriptors)
: m_set_layout(layout), m_descriptors(descriptors)
{
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_set[i] = kvfAllocateDescriptorSet(RenderCore::Get().GetDevice(), layout);
}
void DescriptorSet::SetImage(std::size_t i, std::uint32_t binding, class Image& image)
{
Verify(m_set[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_set[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_set[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_set[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_set[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_set[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_set[i], &buffer_infos[buffer_index], descriptor.binding);
buffer_index++;
}
write_index++;
}
RenderCore::Get().vkUpdateDescriptorSets(RenderCore::Get().GetDevice(), writes.size(), writes.data(), 0, nullptr);
}
}

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

@@ -0,0 +1,245 @@
#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)
{
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), true);
RenderCore::Get().vkBindImageMemory(RenderCore::Get().GetDevice(), m_image, m_memory.memory, 0);
Message("Vulkan: image created");
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);
if(is_single_time_cmd_buffer)
cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
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;
}
kvfTransitionImageLayout(RenderCore::Get().GetDevice(), m_image, kvf_type, cmd, m_format, m_layout, new_layout, is_single_time_cmd_buffer);
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;
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
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
{
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);
}
Message("Vulkan: image destroyed");
m_image = VK_NULL_HANDLE;
s_image_count--;
}
void CubeTexture::Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format)
{
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);
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();
}
}

View File

@@ -0,0 +1,94 @@
#include <Renderer/Memory/Chunk.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <algorithm>
namespace Scop
{
MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index)
: m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index)
{
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)
FatalError("Vulkan: failed to allocate memory for a chunk");
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");
}
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 + 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)->free)
{
it->size = (it + 1)->offset + (it + 1)->size - it->offset;
m_blocks.erase(it + 1);
end = false;
break;
}
}
}
}
MemoryChunk::~MemoryChunk()
{
RenderCore::Get().vkFreeMemory(m_device, m_memory, nullptr);
}
}

View File

@@ -0,0 +1,50 @@
#include <Renderer/Memory/DeviceAllocator.h>
#include <Maths/Constants.h>
#include <Core/Logs.h>
#include <optional>
namespace Scop
{
constexpr VkDeviceSize CHUNK_SIZE = MaxValue<std::uint16_t>();
[[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");
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;
}
}
}
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, (CHUNK_SIZE < size + alignment ? size + alignment : CHUNK_SIZE), memory_type_index));
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");
for(auto& chunk : m_chunks)
{
if(chunk->Has(block))
{
chunk->Deallocate(block);
return;
}
}
Error("Device Allocator: unable to free a block; could not find it's chunk");
}
}

View File

@@ -0,0 +1,184 @@
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Renderer.h>
#include <Renderer/Vertex.h>
#include <Core/EventBus.h>
#include <Core/Logs.h>
namespace Scop
{
void GraphicPipeline::Init(const GraphicPipelineDescriptor& descriptor)
{
if(!descriptor.vertex_shader || !descriptor.fragment_shader)
FatalError("Vulkan: invalid shaders");
m_attachments = descriptor.color_attachments;
p_vertex_shader = descriptor.vertex_shader;
p_fragment_shader = descriptor.fragment_shader;
p_renderer = descriptor.renderer;
p_depth = descriptor.depth;
std::vector<VkPushConstantRange> push_constants;
std::vector<VkDescriptorSetLayout> set_layouts;
push_constants.insert(push_constants.end(), p_vertex_shader->GetPipelineLayout().push_constants.begin(), p_vertex_shader->GetPipelineLayout().push_constants.end());
push_constants.insert(push_constants.end(), p_fragment_shader->GetPipelineLayout().push_constants.begin(), p_fragment_shader->GetPipelineLayout().push_constants.end());
set_layouts.insert(set_layouts.end(), p_vertex_shader->GetPipelineLayout().set_layouts.begin(), p_vertex_shader->GetPipelineLayout().set_layouts.end());
set_layouts.insert(set_layouts.end(), p_fragment_shader->GetPipelineLayout().set_layouts.begin(), p_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());
TransitionAttachments();
CreateFramebuffers(m_attachments, descriptor.clear_color_attachments);
VkPhysicalDeviceFeatures features{};
RenderCore::Get().vkGetPhysicalDeviceFeatures(RenderCore::Get().GetPhysicalDevice(), &features);
KvfGraphicsPipelineBuilder* builder = kvfCreateGPipelineBuilder();
kvfGPipelineBuilderAddShaderStage(builder, p_vertex_shader->GetShaderStage(), p_vertex_shader->GetShaderModule(), "main");
kvfGPipelineBuilderAddShaderStage(builder, p_fragment_shader->GetShaderStage(), p_fragment_shader->GetShaderModule(), "main");
kvfGPipelineBuilderSetInputTopology(builder, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
kvfGPipelineBuilderSetCullMode(builder, descriptor.culling, VK_FRONT_FACE_CLOCKWISE);
kvfGPipelineBuilderEnableAlphaBlending(builder);
if(p_depth)
kvfGPipelineBuilderEnableDepthTest(builder, (descriptor.depth_test_equal ? VK_COMPARE_OP_EQUAL : VK_COMPARE_OP_LESS), true);
else
kvfGPipelineBuilderDisableDepthTest(builder);
if(features.fillModeNonSolid)
kvfGPipelineBuilderSetPolygonMode(builder, descriptor.mode, 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(!descriptor.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);
Message("Vulkan: graphics pipeline created");
kvfDestroyGPipelineBuilder(builder);
}
bool GraphicPipeline::BindPipeline(VkCommandBuffer command_buffer, std::size_t framebuffer_index, std::array<float, 4> clear) noexcept
{
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(p_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
{
RenderCore::Get().vkCmdEndRenderPass(command_buffer);
}
void GraphicPipeline::Destroy() noexcept
{
RenderCore::Get().WaitDeviceIdle();
p_vertex_shader.reset();
p_fragment_shader.reset();
for(auto& fb : m_framebuffers)
{
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
Message("Vulkan: framebuffer destroyed");
}
m_framebuffers.clear();
kvfDestroyPipelineLayout(RenderCore::Get().GetDevice(), m_pipeline_layout);
m_pipeline_layout = VK_NULL_HANDLE;
Message("Vulkan: graphics pipeline layout destroyed");
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
m_renderpass = VK_NULL_HANDLE;
Message("Vulkan: renderpass destroyed");
kvfDestroyPipeline(RenderCore::Get().GetDevice(), m_pipeline);
m_pipeline = VK_NULL_HANDLE;
Message("Vulkan: graphics pipeline destroyed");
}
void GraphicPipeline::CreateFramebuffers(const std::vector<NonOwningPtr<Texture>>& render_targets, bool clear_attachments)
{
std::vector<VkAttachmentDescription> attachments;
std::vector<VkImageView> attachment_views;
if(p_renderer)
{
attachments.push_back(kvfBuildSwapchainAttachmentDescription(p_renderer->GetSwapchain().Get(), clear_attachments));
attachment_views.push_back(p_renderer->GetSwapchain().GetSwapchainImages()[0].GetImageView());
}
for(NonOwningPtr<Texture> image : render_targets)
{
attachments.push_back(kvfBuildAttachmentDescription((kvfIsDepthFormat(image->GetFormat()) ? KVF_IMAGE_DEPTH : KVF_IMAGE_COLOR), image->GetFormat(), image->GetLayout(), image->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
attachment_views.push_back(image->GetImageView());
}
if(p_depth)
{
attachments.push_back(kvfBuildAttachmentDescription((kvfIsDepthFormat(p_depth->GetFormat()) ? KVF_IMAGE_DEPTH : KVF_IMAGE_COLOR), p_depth->GetFormat(), p_depth->GetLayout(), p_depth->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
attachment_views.push_back(p_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(p_renderer)
{
for(const Image& image : p_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(p_depth)
p_depth->TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
for(NonOwningPtr<Texture> image : m_attachments)
{
if(!image->IsInit())
continue;
image->TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, cmd);
}
}
}

View File

@@ -0,0 +1,91 @@
#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) : 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");
GeneratePipelineLayout(m_layout);
}
void Shader::GeneratePipelineLayout(ShaderLayout layout)
{
for(auto& [n, set] : layout.set_layouts)
{
std::vector<VkDescriptorSetLayoutBinding> bindings(set.binds.size());
for(std::size_t i = 0; i < set.binds.size(); i++)
{
bindings[i].binding = set.binds[i].first;
bindings[i].descriptorCount = 1;
bindings[i].descriptorType = set.binds[i].second;
bindings[i].pImmutableSamplers = nullptr;
bindings[i].stageFlags = m_stage;
}
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()
{
kvfDestroyShaderModule(RenderCore::Get().GetDevice(), m_module);
m_module = VK_NULL_HANDLE;
Message("Vulkan: shader module destroyed");
for(auto& layout : m_set_layouts)
{
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), layout);
Message("Vulkan: descriptor set layout destroyed");
}
}
Shader::~Shader()
{
if(m_module != VK_NULL_HANDLE)
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);
Message("Vulkan: shader loaded %", filepath);
return shader;
}
}

View File

@@ -0,0 +1,272 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <vector>
#include <cstdint>
#include <Core/Engine.h>
#include <Platform/Window.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);
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();
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,113 @@
#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
{
Vec4f color;
Vec2f position;
};
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 = std::make_shared<DescriptorSet>(p_vertex_shader->GetShaderLayout().set_layouts[0].second, p_vertex_shader->GetPipelineLayout().set_layouts[0], ShaderType::Vertex);
p_texture_set = std::make_shared<DescriptorSet>(p_fragment_shader->GetShaderLayout().set_layouts[0].second, p_fragment_shader->GetPipelineLayout().set_layouts[0], 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 = VK_CULL_MODE_NONE;
pipeline_descriptor.clear_color_attachments = false;
m_pipeline.Init(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(auto sprite : scene.GetSprites())
{
SpriteData sprite_data;
sprite_data.position = Vec2f{ static_cast<float>(sprite->GetPosition().x), static_cast<float>(sprite->GetPosition().y) };
sprite_data.color = sprite->GetColor();
if(!sprite->IsSetInit())
sprite->UpdateDescriptorSet(*p_texture_set);
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());
}
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,72 @@
#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 = std::make_shared<DescriptorSet>(p_fragment_shader->GetShaderLayout().set_layouts[0].second, p_fragment_shader->GetPipelineLayout().set_layouts[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 = VK_CULL_MODE_NONE;
pipeline_descriptor.no_vertex_inputs = true;
m_pipeline.Init(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,50 @@
#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>
namespace Scop
{
struct ModelData
{
Mat4f model_mat;
Mat4f normal_mat;
};
void ForwardPass::Pass(Scene& scene, Renderer& renderer, class Texture& render_target)
{
Scene::ForwardData& data = scene.GetForwardData();
GraphicPipeline& pipeline = scene.GetPipeline();
if(pipeline.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();
if(scene.GetForwardData().wireframe)
pipeline_descriptor.mode = VK_POLYGON_MODE_LINE;
pipeline_descriptor.clear_color_attachments = false;
pipeline.Init(pipeline_descriptor);
}
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
pipeline.BindPipeline(cmd, 0, {});
for(auto actor : scene.GetActors())
{
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);
actor->GetModel().Draw(cmd, *data.matrices_set, pipeline, *data.albedo_set, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef(), renderer.GetCurrentFrameIndex());
}
pipeline.EndPipeline(cmd);
}
}

View File

@@ -0,0 +1,50 @@
#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_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);
}
m_main_render_texture.Clear(renderer.GetActiveCommandBuffer(), Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f });
scene.GetDepth().Clear(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_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_final.Destroy();
m_main_render_texture.Destroy();
}
}

View File

@@ -0,0 +1,85 @@
#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 = std::make_shared<DescriptorSet>(p_fragment_shader->GetShaderLayout().set_layouts[0].second, p_fragment_shader->GetPipelineLayout().set_layouts[0], 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 = VK_CULL_MODE_NONE;
pipeline_descriptor.depth_test_equal = true;
pipeline_descriptor.clear_color_attachments = false;
m_pipeline.Init(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();
}
}

View File

@@ -0,0 +1,70 @@
#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)
{
p_window = window;
m_swapchain.Init(p_window);
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
Message("Vulkan: image available semaphore created");
m_render_finished_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
Message("Vulkan: render finished semaphore created");
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");
}
}
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_current_frame_index], 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_current_frame_index]);
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 < MAX_FRAMES_IN_FLIGHT; i++)
{
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_image_available_semaphores[i]);
Message("Vulkan: image available semaphore destroyed");
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
Message("Vulkan: render finished 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();
}
}

View File

@@ -0,0 +1,41 @@
#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());
}
m_passes.Pass(scene, renderer);
}
void SceneRenderer::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
m_passes.Destroy();
}
}

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);
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