Skip to content

Commit

Permalink
[WIP] Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
IAmNotHanni committed Jul 9, 2024
1 parent 0580be9 commit e71abd9
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 73 deletions.
5 changes: 3 additions & 2 deletions include/inexor/vulkan-renderer/render-graph/buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class Buffer {
void destroy_buffer();

public:
/// Constructor for uniform buffers
/// Default constructor
/// @param device The device wrapper
/// @param buffer_name The name of the buffer
/// @param buffer_type The type of the buffer
Expand Down Expand Up @@ -129,7 +129,7 @@ class Buffer {
m_src_data = src_data;
m_src_data_size = src_data_size;
}

#if 0
template <typename BufferDataType>
void request_update(BufferDataType &data) {
return request_update(std::addressof(data), sizeof(data));
Expand All @@ -139,6 +139,7 @@ class Buffer {
void request_update(std::vector<BufferDataType> &data) {
return request_update(data.data(), sizeof(data) * data.size());
}
#endif
};

} // namespace inexor::vulkan_renderer::render_graph
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,31 @@ class CommandBuffer {
VkDependencyFlags dep_flags = 0) const;

/// Call vkCmdPipelineBarrier
/// @param src_stage_flags The the source stage flags
/// @param src_stage_flags The source stage flags
/// @param dst_stage_flags The destination stage flags
/// @param buffer_mem_barrier The buffer memory barrier
/// @return A const reference to the dereferenced ``this`` pointer (allowing for method calls to be chained)
const CommandBuffer &pipeline_buffer_memory_barrier(VkPipelineStageFlags src_stage_flags,
VkPipelineStageFlags dst_stage_flags,
const VkBufferMemoryBarrier &buffer_mem_barrier) const;

/// Call vkCmdPipelineBarrier
/// @param src_stage_flags The source stage flags
/// @param dst_stage_flags The destination stage flags
/// @param src_access_flags The source access flags
/// @param dst_access_flags The destination access flags
/// @param buffer
/// @param size
/// @param offset
/// @return A const reference to the dereferenced ``this`` pointer (allowing for method calls to be chained)
const CommandBuffer &pipeline_buffer_memory_barrier(VkPipelineStageFlags src_stage_flags,
VkPipelineStageFlags dst_stage_flags,
VkAccessFlags src_access_flags,
VkAccessFlags dst_access_flags,
VkBuffer buffer,
VkDeviceSize size = VK_WHOLE_SIZE,
VkDeviceSize offset = 0) const;

/// Call vkCmdPipelineBarrier
/// @param src_stage_flags The the source stage flags
/// @param dst_stage_flags The destination stage flags
Expand Down
19 changes: 8 additions & 11 deletions src/vulkan-renderer/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debug_messenger_callback(const VkDebugUtilsMessag

if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
m_validation_log->trace("{}", data->pMessage);
}
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
m_validation_log->info("{}", data->pMessage);
}
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
m_validation_log->warn("{}", data->pMessage);
}
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
m_validation_log->critical("{}", data->pMessage);
}
return false;
Expand Down Expand Up @@ -460,7 +457,7 @@ void Application::setup_render_graph() {
generate_octree_indices();
}
// Request update of the octree vertex buffer
m_vertex_buffer->request_update(m_octree_vertices);
m_vertex_buffer->request_update(m_octree_vertices.data(), sizeof(OctreeGpuVertex) * m_octree_vertices.size());
});

// TODO: How to prevent duplicate loading of shaders or how to deal with object lifetime?
Expand All @@ -472,22 +469,22 @@ void Application::setup_render_graph() {
// This means for m_index_buffer, on_init and on_update are defaulted to std::nullopt here!
m_index_buffer = m_render_graph->add_buffer("Octree", INDEX_BUFFER, [&]() {
// Request update of the octree index buffer
m_index_buffer->request_update(m_octree_indices);
m_index_buffer->request_update(m_octree_indices.data(), sizeof(std::uint32_t) * m_octree_indices.size());
});

// TODO: This must be in the init() method of some OctreeRenderer class in the future!
/// Initialize octree vertices and indices here
load_octree_geometry(false);
generate_octree_indices();
m_vertex_buffer->request_update(m_octree_vertices);
m_index_buffer->request_update(m_octree_indices);
m_vertex_buffer->request_update(m_octree_vertices.data(), sizeof(OctreeGpuVertex) * m_octree_vertices.size());
m_index_buffer->request_update(m_octree_indices.data(), sizeof(std::uint32_t) * m_octree_indices.size());

m_uniform_buffer = m_render_graph->add_buffer("Matrices", UNIFORM_BUFFER, [&]() {
// TODO: Update model matrix if required
m_mvp_matrices.view = m_camera->view_matrix();
m_mvp_matrices.proj = m_camera->perspective_matrix();
m_mvp_matrices.proj[1][1] *= -1;
m_uniform_buffer->request_update(m_mvp_matrices);
m_uniform_buffer->request_update(&m_mvp_matrices, sizeof(ModelViewPerspectiveMatrices));
});

using wrapper::descriptors::DescriptorSetLayoutBuilder;
Expand Down
86 changes: 39 additions & 47 deletions src/vulkan-renderer/render-graph/buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,39 @@ Buffer::~Buffer() {
}

void Buffer::create_buffer(const CommandBuffer &cmd_buf) {
if (m_src_data_size == 0) {
spdlog::warn("[Buffer::create_buffer] Warning: Can't create buffer of size 0!");
return;
}

// This helps us to find the correct VkBufferUsageFlags depending on the BufferType
const std::unordered_map<BufferType, VkBufferUsageFlags> BUFFER_USAGE{
{BufferType::UNIFORM_BUFFER, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT},
{BufferType::VERTEX_BUFFER, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT},
{BufferType::INDEX_BUFFER, VK_BUFFER_USAGE_INDEX_BUFFER_BIT},
{BufferType::VERTEX_BUFFER, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT},
{BufferType::INDEX_BUFFER, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT},
};
const auto buffer_ci = wrapper::make_info<VkBufferCreateInfo>({
.size = m_src_data_size,
.usage = BUFFER_USAGE.at(m_buffer_type),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, // TODO: Support VK_SHARING_MODE_CONCURRENT
// We do not support VK_SHARING_MODE_CONCURRENT
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
});
const std::unordered_map<BufferType, VmaAllocationCreateFlags> BUFFER_FLAGS{
{BufferType::UNIFORM_BUFFER, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT},
{BufferType::VERTEX_BUFFER,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT},
{BufferType::INDEX_BUFFER,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT},
};
// This helps us to find the correct VmaMemoryUsage depending on the BufferType
const std::unordered_map<BufferType, VmaMemoryUsage> MEMORY_USAGE{
{BufferType::UNIFORM_BUFFER, VMA_MEMORY_USAGE_AUTO_PREFER_HOST},
{BufferType::VERTEX_BUFFER, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE},
{BufferType::INDEX_BUFFER, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE},
};
const VmaAllocationCreateInfo alloc_ci{
.flags = BUFFER_FLAGS.at(m_buffer_type),
.usage = MEMORY_USAGE.at(m_buffer_type),
// TODO: Fix this for uniform buffers?
.flags = 0,
.usage = VMA_MEMORY_USAGE_AUTO,
};

// The memory for the buffer we would like to create can end up in mappable memory, which means we can simply use
// std::memcpy to copy the source date into it, or it ends up in non-mappable memory, which means we will need to
// use a staging buffer and a transfer operation (a copy command) to upload the data to gpu memory. Which memory is
// chosen by Vulkan Memory Allocator depends on the available memory and current memory usage.
if (const auto result =
vmaCreateBuffer(m_device.allocator(), &buffer_ci, &alloc_ci, &m_buffer, &m_alloc, &m_alloc_info);
result != VK_SUCCESS) {
Expand All @@ -69,26 +75,35 @@ void Buffer::create_buffer(const CommandBuffer &cmd_buf) {
// Set the buffer's internal debug name through Vulkan debug utils
m_device.set_debug_name(m_buffer, m_name);

// Ask VMA which memory was chosen for the allocation
// Query memory property flags
VkMemoryPropertyFlags mem_prop_flags{};
vmaGetAllocationMemoryProperties(m_device.allocator(), m_alloc, &mem_prop_flags);

// Upload the data depending on whether we can use std::memcpy or require a staging buffer
// Check if the allocation made by VMA ended up in mappable memory
if (mem_prop_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
// The allocation ended up in mappable memory and it is already mapped
// This means we can simply use std::memcpy to copy the data from the source into it
std::memcpy(m_alloc_info.pMappedData, m_src_data, m_src_data_size);

// After copying the data, we need to flush caches
if (const auto result = vmaFlushAllocation(m_device.allocator(), m_alloc, 0, VK_WHOLE_SIZE);
result != VK_SUCCESS) {
throw VulkanException("Error: vmaFlushAllocation failed for buffer " + m_name + " !", result);
}
} else {
// The allocation ended up in non-mappable memory and we need a staging buffer to upload the data
// Make sure to destroy the previous staging buffer
if (m_staging_buffer != VK_NULL_HANDLE) {
vmaDestroyBuffer(m_device.allocator(), m_staging_buffer, m_alloc);
m_staging_buffer = VK_NULL_HANDLE;
}
// The allocation ended up in non-mappable memory and we need a staging buffer and a copy command to upload data
const auto staging_buf_ci = wrapper::make_info<VkBufferCreateInfo>({
// The size of the staging buffer must be the size of the actual buffer
.size = m_src_data_size,
// This is the buffer usage bit for staging buffers
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
});
const VmaAllocationCreateInfo staging_buf_alloc_ci{
// TODO: Decide this depending on m_buffer_type!
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
Expand All @@ -107,43 +122,20 @@ void Buffer::create_buffer(const CommandBuffer &cmd_buf) {
// Copy the memory into the staging buffer
std::memcpy(m_staging_alloc_info.pMappedData, m_src_data, m_src_data_size);

// TODO: Decide here if flush is required as well?
if (const auto result = vmaFlushAllocation(m_device.allocator(), m_staging_alloc, 0, VK_WHOLE_SIZE);
result != VK_SUCCESS) {
throw VulkanException("Error: vmaFlushAllocation failed for staging buffer " + m_name + " !", result);
};

cmd_buf
// TODO: Maybe even abstract this further and use only .pipeline_buffer_memory_barrier(/*arguments*/)?
// Yeah we definitely need better abstraction for barriers...
.pipeline_buffer_memory_barrier(VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_TRANSFER_READ_BIT,
{
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = m_staging_buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
})
// Further abstraction is required here as well...
.copy_buffer(m_staging_buffer, m_buffer,
{
.srcOffset = 0,
.dstOffset = 0,
.size = m_src_data_size,
})
.pipeline_buffer_memory_barrier(
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
{
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT, // TODO: Depending on buffer type!
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = m_staging_buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
});
// TODO: Further abstraction! .pipeline_buffer_memory_barrier_before_copy_command and _after_copy_command
.pipeline_buffer_memory_barrier(VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT,
m_staging_buffer)
.copy_buffer(m_staging_buffer, m_buffer, m_src_data_size)
.pipeline_buffer_memory_barrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
m_buffer);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/vulkan-renderer/render-graph/render_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ std::shared_ptr<Texture> RenderGraph::add_texture(std::string texture_name,
const VkFormat format,
std::optional<std::function<void()>> on_init,
std::optional<std::function<void()>> on_update) {
// TODO: Check for names being empty only in wrappers!
if (texture_name.empty()) {
throw std::invalid_argument(
"[RenderGraph::add_texture] Error: Parameter 'texture_name' ist an empty std::string! "
Expand Down Expand Up @@ -86,6 +87,7 @@ void RenderGraph::compile() {
void RenderGraph::create_buffers() {
m_device.execute("RenderGraph::create_buffers", [&](const CommandBuffer &cmd_buf) {
for (const auto &buffer : m_buffers) {
// TODO: if(buffer->update_requested)
buffer->m_on_update();
buffer->create_buffer(cmd_buf);
}
Expand Down Expand Up @@ -116,6 +118,7 @@ void RenderGraph::create_graphics_pipelines() {
void RenderGraph::create_textures() {
for (const auto &texture : m_textures) {
if (texture->m_on_init) {
// TODO: if(texture->update_requested)...
// Call the initialization function of the texture (if specified)
std::invoke(texture->m_on_init.value());
}
Expand Down
4 changes: 2 additions & 2 deletions src/vulkan-renderer/renderers/imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ ImGuiRenderer::ImGuiRenderer(const wrapper::Device &device,
}
}
// NOTE: The index buffer does not have a separate update code because it is updated here with the vertices
m_vertex_buffer->request_update(m_vertex_data);
m_index_buffer->request_update(m_index_data);
m_vertex_buffer->request_update(m_vertex_data.data(), sizeof(ImDrawVert) * m_vertex_data.size());
m_index_buffer->request_update(m_index_data.data(), sizeof(std::uint32_t) * m_index_data.size());
});

m_index_buffer = render_graph.add_buffer("ImGui", BufferType::INDEX_BUFFER, [&]() {
Expand Down
29 changes: 19 additions & 10 deletions src/vulkan-renderer/wrapper/commands/command_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,6 @@ const CommandBuffer &CommandBuffer::copy_buffer_to_image(const VkBuffer src_buf,
return *this;
}

/*
const CommandBuffer &CommandBuffer::copy_buffer_to_image(const void *data,
const VkDeviceSize data_size, // NOLINT
const VkImage dst_img,
const VkBufferImageCopy &copy_region,
const std::string &name) const {
return copy_buffer_to_image(create_staging_buffer(data, data_size, name), dst_img, copy_region);
}
*/

const CommandBuffer &CommandBuffer::draw(const std::uint32_t vert_count,
const std::uint32_t inst_count,
const std::uint32_t first_vert,
Expand Down Expand Up @@ -299,6 +289,25 @@ CommandBuffer::pipeline_buffer_memory_barrier(const VkPipelineStageFlags src_sta
return pipeline_barrier(src_stage_flags, dst_stage_flags, {}, {}, {&buffer_mem_barrier, 1});
}

const CommandBuffer &CommandBuffer::pipeline_buffer_memory_barrier(VkPipelineStageFlags src_stage_flags,
VkPipelineStageFlags dst_stage_flags,
VkAccessFlags src_access_flags,
VkAccessFlags dst_access_flags,
VkBuffer buffer,
VkDeviceSize size,
VkDeviceSize offset) const {
return pipeline_buffer_memory_barrier(VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_TRANSFER_READ_BIT,
wrapper::make_info<VkBufferMemoryBarrier>({
.srcAccessMask = src_stage_flags,
.dstAccessMask = dst_stage_flags,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = buffer,
.offset = offset,
.size = size,
}));
}

const CommandBuffer &
CommandBuffer::pipeline_buffer_memory_barriers(const VkPipelineStageFlags src_stage_flags,
const VkPipelineStageFlags dst_stage_flags,
Expand Down
6 changes: 6 additions & 0 deletions src/vulkan-renderer/wrapper/make_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ VkBufferCreateInfo make_info(VkBufferCreateInfo info) {
return info;
}

template <>
VkBufferMemoryBarrier make_info(VkBufferMemoryBarrier info) {
info.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
return info;
}

template <>
VkCommandBufferAllocateInfo make_info(VkCommandBufferAllocateInfo info) {
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
Expand Down
1 change: 1 addition & 0 deletions src/vulkan-renderer/wrapper/swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Swapchain::choose_surface_format(const std::vector<VkSurfaceFormatKHR> &availabl
if (format != default_surface_format_priority_list.end()) {
spdlog::trace("Selecting swapchain image format {}", vk_tools::as_string(*format));
chosen_format = *format;
break;
}
}
// This can be std::nullopt
Expand Down

0 comments on commit e71abd9

Please sign in to comment.