/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/gpu/vulkan/deferred_command_buffer.h" #include #include #include #include "xenia/base/assert.h" #include "xenia/base/math.h" #include "xenia/base/profiling.h" #include "xenia/gpu/vulkan/vulkan_command_processor.h" namespace xe { namespace gpu { namespace vulkan { DeferredCommandBuffer::DeferredCommandBuffer( const VulkanCommandProcessor& command_processor, size_t initial_size) : command_processor_(command_processor) { command_stream_.reserve(initial_size / sizeof(uintmax_t)); } void DeferredCommandBuffer::Reset() { command_stream_.clear(); } void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) { #if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES SCOPE_profile_cpu_f("gpu"); #endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = command_processor_.GetVulkanContext().GetVulkanProvider().dfn(); const uintmax_t* stream = command_stream_.data(); size_t stream_remaining = command_stream_.size(); while (stream_remaining) { const CommandHeader& header = *reinterpret_cast(stream); stream += kCommandHeaderSizeElements; stream_remaining -= kCommandHeaderSizeElements; switch (header.command) { case Command::kVkBindIndexBuffer: { auto& args = *reinterpret_cast(stream); dfn.vkCmdBindIndexBuffer(command_buffer, args.buffer, args.offset, args.index_type); } break; case Command::kVkCopyBuffer: { auto& args = *reinterpret_cast(stream); static_assert(alignof(VkBufferCopy) <= alignof(uintmax_t)); dfn.vkCmdCopyBuffer( command_buffer, args.src_buffer, args.dst_buffer, args.region_count, reinterpret_cast( reinterpret_cast(stream) + xe::align(sizeof(ArgsVkCopyBuffer), alignof(VkBufferCopy)))); } break; case Command::kVkPipelineBarrier: { auto& args = *reinterpret_cast(stream); size_t barrier_offset_bytes = sizeof(ArgsVkPipelineBarrier); const VkMemoryBarrier* memory_barriers; if (args.memory_barrier_count) { static_assert(alignof(VkMemoryBarrier) <= alignof(uintmax_t)); barrier_offset_bytes = xe::align(barrier_offset_bytes, alignof(VkMemoryBarrier)); memory_barriers = reinterpret_cast( reinterpret_cast(stream) + barrier_offset_bytes); barrier_offset_bytes += sizeof(VkMemoryBarrier) * args.memory_barrier_count; } else { memory_barriers = nullptr; } const VkBufferMemoryBarrier* buffer_memory_barriers; if (args.buffer_memory_barrier_count) { static_assert(alignof(VkBufferMemoryBarrier) <= alignof(uintmax_t)); barrier_offset_bytes = xe::align(barrier_offset_bytes, alignof(VkBufferMemoryBarrier)); buffer_memory_barriers = reinterpret_cast( reinterpret_cast(stream) + barrier_offset_bytes); barrier_offset_bytes += sizeof(VkBufferMemoryBarrier) * args.buffer_memory_barrier_count; } else { buffer_memory_barriers = nullptr; } const VkImageMemoryBarrier* image_memory_barriers; if (args.image_memory_barrier_count) { static_assert(alignof(VkImageMemoryBarrier) <= alignof(uintmax_t)); barrier_offset_bytes = xe::align(barrier_offset_bytes, alignof(VkImageMemoryBarrier)); image_memory_barriers = reinterpret_cast( reinterpret_cast(stream) + barrier_offset_bytes); barrier_offset_bytes += sizeof(VkImageMemoryBarrier) * args.image_memory_barrier_count; } else { image_memory_barriers = nullptr; } dfn.vkCmdPipelineBarrier( command_buffer, args.src_stage_mask, args.dst_stage_mask, args.dependency_flags, args.memory_barrier_count, memory_barriers, args.buffer_memory_barrier_count, buffer_memory_barriers, args.image_memory_barrier_count, image_memory_barriers); } break; default: assert_unhandled_case(header.command); break; } stream += header.arguments_size_elements; stream_remaining -= header.arguments_size_elements; } } void DeferredCommandBuffer::CmdVkPipelineBarrier( VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkDependencyFlags dependency_flags, uint32_t memory_barrier_count, const VkMemoryBarrier* memory_barriers, uint32_t buffer_memory_barrier_count, const VkBufferMemoryBarrier* buffer_memory_barriers, uint32_t image_memory_barrier_count, const VkImageMemoryBarrier* image_memory_barriers) { size_t arguments_size = sizeof(ArgsVkPipelineBarrier); size_t memory_barriers_offset; if (memory_barrier_count) { static_assert(alignof(VkMemoryBarrier) <= alignof(uintmax_t)); arguments_size = xe::align(arguments_size, alignof(VkMemoryBarrier)); memory_barriers_offset = arguments_size; arguments_size += sizeof(VkMemoryBarrier) * memory_barrier_count; } else { memory_barriers_offset = 0; } size_t buffer_memory_barriers_offset; if (buffer_memory_barrier_count) { static_assert(alignof(VkBufferMemoryBarrier) <= alignof(uintmax_t)); arguments_size = xe::align(arguments_size, alignof(VkBufferMemoryBarrier)); buffer_memory_barriers_offset = arguments_size; arguments_size += sizeof(VkBufferMemoryBarrier) * buffer_memory_barrier_count; } else { buffer_memory_barriers_offset = 0; } size_t image_memory_barriers_offset; if (image_memory_barrier_count) { static_assert(alignof(VkImageMemoryBarrier) <= alignof(uintmax_t)); arguments_size = xe::align(arguments_size, alignof(VkImageMemoryBarrier)); image_memory_barriers_offset = arguments_size; arguments_size += sizeof(VkImageMemoryBarrier) * image_memory_barrier_count; } else { image_memory_barriers_offset = 0; } uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkPipelineBarrier, arguments_size)); auto& args = *reinterpret_cast(args_ptr); args.src_stage_mask = src_stage_mask; args.dst_stage_mask = dst_stage_mask; args.dependency_flags = dependency_flags; args.memory_barrier_count = memory_barrier_count; args.buffer_memory_barrier_count = buffer_memory_barrier_count; args.image_memory_barrier_count = image_memory_barrier_count; if (memory_barrier_count) { std::memcpy(args_ptr + memory_barriers_offset, memory_barriers, sizeof(VkMemoryBarrier) * memory_barrier_count); } if (buffer_memory_barrier_count) { std::memcpy(args_ptr + buffer_memory_barriers_offset, buffer_memory_barriers, sizeof(VkBufferMemoryBarrier) * buffer_memory_barrier_count); } if (image_memory_barrier_count) { std::memcpy(args_ptr + image_memory_barriers_offset, image_memory_barriers, sizeof(VkImageMemoryBarrier) * image_memory_barrier_count); } } void* DeferredCommandBuffer::WriteCommand(Command command, size_t arguments_size_bytes) { size_t arguments_size_elements = (arguments_size_bytes + sizeof(uintmax_t) - 1) / sizeof(uintmax_t); size_t offset = command_stream_.size(); command_stream_.resize(offset + kCommandHeaderSizeElements + arguments_size_elements); CommandHeader& header = *reinterpret_cast(command_stream_.data() + offset); header.command = command; header.arguments_size_elements = uint32_t(arguments_size_elements); return command_stream_.data() + (offset + kCommandHeaderSizeElements); } } // namespace vulkan } // namespace gpu } // namespace xe