/** ****************************************************************************** * 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. * ****************************************************************************** */ #ifndef XENIA_GPU_VULKAN_DEFERRED_COMMAND_BUFFER_H_ #define XENIA_GPU_VULKAN_DEFERRED_COMMAND_BUFFER_H_ #include #include #include #include "xenia/base/assert.h" #include "xenia/base/math.h" #include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { namespace vulkan { class VulkanCommandProcessor; class DeferredCommandBuffer { public: DeferredCommandBuffer(const VulkanCommandProcessor& command_processor, size_t initial_size_bytes = 1024 * 1024); void Reset(); void Execute(VkCommandBuffer command_buffer); // render_pass_begin->pNext of all barriers must be null. void CmdVkBeginRenderPass(const VkRenderPassBeginInfo* render_pass_begin, VkSubpassContents contents) { assert_null(render_pass_begin->pNext); size_t arguments_size = sizeof(ArgsVkBeginRenderPass); uint32_t clear_value_count = render_pass_begin->clearValueCount; size_t clear_values_offset = 0; if (clear_value_count) { arguments_size = xe::align(arguments_size, alignof(VkClearValue)); clear_values_offset = arguments_size; arguments_size += sizeof(VkClearValue) * clear_value_count; } uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkBeginRenderPass, arguments_size)); auto& args = *reinterpret_cast(args_ptr); args.render_pass = render_pass_begin->renderPass; args.framebuffer = render_pass_begin->framebuffer; args.render_area = render_pass_begin->renderArea; args.clear_value_count = clear_value_count; args.contents = contents; if (clear_value_count) { std::memcpy(args_ptr + clear_values_offset, render_pass_begin->pClearValues, sizeof(VkClearValue) * clear_value_count); } } void CmdVkBindDescriptorSets(VkPipelineBindPoint pipeline_bind_point, VkPipelineLayout layout, uint32_t first_set, uint32_t descriptor_set_count, const VkDescriptorSet* descriptor_sets, uint32_t dynamic_offset_count, const uint32_t* dynamic_offsets) { size_t arguments_size = xe::align(sizeof(ArgsVkBindDescriptorSets), alignof(VkDescriptorSet)); size_t descriptor_sets_offset = arguments_size; arguments_size += sizeof(VkDescriptorSet) * descriptor_set_count; size_t dynamic_offsets_offset = 0; if (dynamic_offset_count) { arguments_size = xe::align(arguments_size, alignof(uint32_t)); dynamic_offsets_offset = arguments_size; arguments_size += sizeof(uint32_t) * dynamic_offset_count; } uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkBindDescriptorSets, arguments_size)); auto& args = *reinterpret_cast(args_ptr); args.pipeline_bind_point = pipeline_bind_point; args.layout = layout; args.first_set = first_set; args.descriptor_set_count = descriptor_set_count; args.dynamic_offset_count = dynamic_offset_count; std::memcpy(args_ptr + descriptor_sets_offset, descriptor_sets, sizeof(VkDescriptorSet) * descriptor_set_count); if (dynamic_offset_count) { std::memcpy(args_ptr + dynamic_offsets_offset, dynamic_offsets, sizeof(uint32_t) * dynamic_offset_count); } } void CmdVkBindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) { auto& args = *reinterpret_cast(WriteCommand( Command::kVkBindIndexBuffer, sizeof(ArgsVkBindIndexBuffer))); args.buffer = buffer; args.offset = offset; args.index_type = index_type; } void CmdVkBindPipeline(VkPipelineBindPoint pipeline_bind_point, VkPipeline pipeline) { auto& args = *reinterpret_cast( WriteCommand(Command::kVkBindPipeline, sizeof(ArgsVkBindPipeline))); args.pipeline_bind_point = pipeline_bind_point; args.pipeline = pipeline; } VkBufferCopy* CmdCopyBufferEmplace(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count) { const size_t header_size = xe::align(sizeof(ArgsVkCopyBuffer), alignof(VkBufferCopy)); uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkCopyBuffer, header_size + sizeof(VkBufferCopy) * region_count)); auto& args = *reinterpret_cast(args_ptr); args.src_buffer = src_buffer; args.dst_buffer = dst_buffer; args.region_count = region_count; return reinterpret_cast(args_ptr + header_size); } void CmdVkCopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count, const VkBufferCopy* regions) { std::memcpy(CmdCopyBufferEmplace(src_buffer, dst_buffer, region_count), regions, sizeof(VkBufferCopy) * region_count); } void CmdVkDraw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) { auto& args = *reinterpret_cast( WriteCommand(Command::kVkDraw, sizeof(ArgsVkDraw))); args.vertex_count = vertex_count; args.instance_count = instance_count; args.first_vertex = first_vertex; args.first_instance = first_instance; } void CmdVkDrawIndexed(uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t vertex_offset, uint32_t first_instance) { auto& args = *reinterpret_cast( WriteCommand(Command::kVkDrawIndexed, sizeof(ArgsVkDrawIndexed))); args.index_count = index_count; args.instance_count = instance_count; args.first_index = first_index; args.vertex_offset = vertex_offset; args.first_instance = first_instance; } void CmdVkEndRenderPass() { WriteCommand(Command::kVkEndRenderPass, 0); } // pNext of all barriers must be null. void 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); void CmdVkSetBlendConstants(const float* blend_constants) { auto& args = *reinterpret_cast(WriteCommand( Command::kVkSetBlendConstants, sizeof(ArgsVkSetBlendConstants))); std::memcpy(args.blend_constants, blend_constants, sizeof(float) * 4); } void CmdVkSetDepthBias(float depth_bias_constant_factor, float depth_bias_clamp, float depth_bias_slope_factor) { auto& args = *reinterpret_cast( WriteCommand(Command::kVkSetDepthBias, sizeof(ArgsVkSetDepthBias))); args.depth_bias_constant_factor = depth_bias_constant_factor; args.depth_bias_clamp = depth_bias_clamp; args.depth_bias_slope_factor = depth_bias_slope_factor; } void CmdVkSetScissor(uint32_t first_scissor, uint32_t scissor_count, const VkRect2D* scissors) { const size_t header_size = xe::align(sizeof(ArgsVkSetScissor), alignof(VkRect2D)); uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkSetScissor, header_size + sizeof(VkRect2D) * scissor_count)); auto& args = *reinterpret_cast(args_ptr); args.first_scissor = first_scissor; args.scissor_count = scissor_count; std::memcpy(args_ptr + header_size, scissors, sizeof(VkRect2D) * scissor_count); } void CmdVkSetStencilCompareMask(VkStencilFaceFlags face_mask, uint32_t compare_mask) { auto& args = *reinterpret_cast( WriteCommand(Command::kVkSetStencilCompareMask, sizeof(ArgsSetStencilMaskReference))); args.face_mask = face_mask; args.mask_reference = compare_mask; } void CmdVkSetStencilReference(VkStencilFaceFlags face_mask, uint32_t reference) { auto& args = *reinterpret_cast(WriteCommand( Command::kVkSetStencilReference, sizeof(ArgsSetStencilMaskReference))); args.face_mask = face_mask; args.mask_reference = reference; } void CmdVkSetStencilWriteMask(VkStencilFaceFlags face_mask, uint32_t write_mask) { auto& args = *reinterpret_cast(WriteCommand( Command::kVkSetStencilWriteMask, sizeof(ArgsSetStencilMaskReference))); args.face_mask = face_mask; args.mask_reference = write_mask; } void CmdVkSetViewport(uint32_t first_viewport, uint32_t viewport_count, const VkViewport* viewports) { const size_t header_size = xe::align(sizeof(ArgsVkSetViewport), alignof(VkViewport)); uint8_t* args_ptr = reinterpret_cast( WriteCommand(Command::kVkSetViewport, header_size + sizeof(VkViewport) * viewport_count)); auto& args = *reinterpret_cast(args_ptr); args.first_viewport = first_viewport; args.viewport_count = viewport_count; std::memcpy(args_ptr + header_size, viewports, sizeof(VkViewport) * viewport_count); } private: enum class Command { kVkBeginRenderPass, kVkBindDescriptorSets, kVkBindIndexBuffer, kVkBindPipeline, kVkCopyBuffer, kVkDraw, kVkDrawIndexed, kVkEndRenderPass, kVkPipelineBarrier, kVkSetBlendConstants, kVkSetDepthBias, kVkSetScissor, kVkSetStencilCompareMask, kVkSetStencilReference, kVkSetStencilWriteMask, kVkSetViewport, }; struct CommandHeader { Command command; uint32_t arguments_size_elements; }; static constexpr size_t kCommandHeaderSizeElements = (sizeof(CommandHeader) + sizeof(uintmax_t) - 1) / sizeof(uintmax_t); struct ArgsVkBeginRenderPass { VkRenderPass render_pass; VkFramebuffer framebuffer; VkRect2D render_area; uint32_t clear_value_count; VkSubpassContents contents; // Followed by aligned optional VkClearValue[]. static_assert(alignof(VkClearValue) <= alignof(uintmax_t)); }; struct ArgsVkBindDescriptorSets { VkPipelineBindPoint pipeline_bind_point; VkPipelineLayout layout; uint32_t first_set; uint32_t descriptor_set_count; uint32_t dynamic_offset_count; // Followed by aligned VkDescriptorSet[], optional uint32_t[]. static_assert(alignof(VkDescriptorSet) <= alignof(uintmax_t)); }; struct ArgsVkBindIndexBuffer { VkBuffer buffer; VkDeviceSize offset; VkIndexType index_type; }; struct ArgsVkBindPipeline { VkPipelineBindPoint pipeline_bind_point; VkPipeline pipeline; }; struct ArgsVkCopyBuffer { VkBuffer src_buffer; VkBuffer dst_buffer; uint32_t region_count; // Followed by aligned VkBufferCopy[]. static_assert(alignof(VkBufferCopy) <= alignof(uintmax_t)); }; struct ArgsVkDraw { uint32_t vertex_count; uint32_t instance_count; uint32_t first_vertex; uint32_t first_instance; }; struct ArgsVkDrawIndexed { uint32_t index_count; uint32_t instance_count; uint32_t first_index; int32_t vertex_offset; uint32_t first_instance; }; struct ArgsVkPipelineBarrier { VkPipelineStageFlags src_stage_mask; VkPipelineStageFlags dst_stage_mask; VkDependencyFlags dependency_flags; uint32_t memory_barrier_count; uint32_t buffer_memory_barrier_count; uint32_t image_memory_barrier_count; // Followed by aligned optional VkMemoryBarrier[], // optional VkBufferMemoryBarrier[], optional VkImageMemoryBarrier[]. static_assert(alignof(VkMemoryBarrier) <= alignof(uintmax_t)); static_assert(alignof(VkBufferMemoryBarrier) <= alignof(uintmax_t)); static_assert(alignof(VkImageMemoryBarrier) <= alignof(uintmax_t)); }; struct ArgsVkSetBlendConstants { float blend_constants[4]; }; struct ArgsVkSetDepthBias { float depth_bias_constant_factor; float depth_bias_clamp; float depth_bias_slope_factor; }; struct ArgsVkSetScissor { uint32_t first_scissor; uint32_t scissor_count; // Followed by aligned VkRect2D[]. static_assert(alignof(VkRect2D) <= alignof(uintmax_t)); }; struct ArgsSetStencilMaskReference { VkStencilFaceFlags face_mask; uint32_t mask_reference; }; struct ArgsVkSetViewport { uint32_t first_viewport; uint32_t viewport_count; // Followed by aligned VkViewport[]. static_assert(alignof(VkViewport) <= alignof(uintmax_t)); }; void* WriteCommand(Command command, size_t arguments_size_bytes); const VulkanCommandProcessor& command_processor_; // uintmax_t to ensure uint64_t and pointer alignment of all structures. std::vector command_stream_; }; } // namespace vulkan } // namespace gpu } // namespace xe #endif // XENIA_GPU_VULKAN_DEFERRED_COMMAND_BUFFER_H_