/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_ #define XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_ #include #include #include #include #include #include #include #include #include #include #include "xenia/base/assert.h" #include "xenia/base/hash.h" #include "xenia/gpu/command_processor.h" #include "xenia/gpu/draw_util.h" #include "xenia/gpu/registers.h" #include "xenia/gpu/spirv_shader_translator.h" #include "xenia/gpu/vulkan/deferred_command_buffer.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" #include "xenia/gpu/vulkan/vulkan_pipeline_cache.h" #include "xenia/gpu/vulkan/vulkan_primitive_processor.h" #include "xenia/gpu/vulkan/vulkan_render_target_cache.h" #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/vulkan/vulkan_shared_memory.h" #include "xenia/gpu/vulkan/vulkan_texture_cache.h" #include "xenia/gpu/xenos.h" #include "xenia/kernel/kernel_state.h" #include "xenia/ui/vulkan/linked_type_descriptor_set_allocator.h" #include "xenia/ui/vulkan/vulkan_gpu_completion_timeline.h" #include "xenia/ui/vulkan/vulkan_presenter.h" #include "xenia/ui/vulkan/vulkan_provider.h" #include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h" namespace xe { namespace gpu { namespace vulkan { class VulkanCommandProcessor : public CommandProcessor { public: // Single-descriptor layouts for use within a single frame. enum class SingleTransientDescriptorLayout { kStorageBufferCompute, kCount, }; class ScratchBufferAcquisition { public: explicit ScratchBufferAcquisition() = default; explicit ScratchBufferAcquisition(VulkanCommandProcessor& command_processor, VkBuffer buffer, VkPipelineStageFlags stage_mask, VkAccessFlags access_mask) : command_processor_(&command_processor), buffer_(buffer), stage_mask_(stage_mask), access_mask_(access_mask) {} ScratchBufferAcquisition(const ScratchBufferAcquisition& acquisition) = delete; ScratchBufferAcquisition& operator=( const ScratchBufferAcquisition& acquisition) = delete; ScratchBufferAcquisition(ScratchBufferAcquisition&& acquisition) { command_processor_ = acquisition.command_processor_; buffer_ = acquisition.buffer_; stage_mask_ = acquisition.stage_mask_; access_mask_ = acquisition.access_mask_; acquisition.command_processor_ = nullptr; acquisition.buffer_ = VK_NULL_HANDLE; acquisition.stage_mask_ = 0; acquisition.access_mask_ = 0; } ScratchBufferAcquisition& operator=( ScratchBufferAcquisition&& acquisition) { if (this == &acquisition) { return *this; } command_processor_ = acquisition.command_processor_; buffer_ = acquisition.buffer_; stage_mask_ = acquisition.stage_mask_; access_mask_ = acquisition.access_mask_; acquisition.command_processor_ = nullptr; acquisition.buffer_ = VK_NULL_HANDLE; acquisition.stage_mask_ = 0; acquisition.access_mask_ = 0; return *this; } ~ScratchBufferAcquisition() { if (buffer_ != VK_NULL_HANDLE) { assert_true(command_processor_->scratch_buffer_used_); assert_true(command_processor_->scratch_buffer_ == buffer_); command_processor_->scratch_buffer_last_stage_mask_ = stage_mask_; command_processor_->scratch_buffer_last_access_mask_ = access_mask_; command_processor_->scratch_buffer_last_usage_submission_ = command_processor_->GetCurrentSubmission(); command_processor_->scratch_buffer_used_ = false; } } // VK_NULL_HANDLE if failed to acquire or if moved. VkBuffer buffer() const { return buffer_; } VkPipelineStageFlags GetStageMask() const { return stage_mask_; } VkPipelineStageFlags SetStageMask(VkPipelineStageFlags new_stage_mask) { VkPipelineStageFlags old_stage_mask = stage_mask_; stage_mask_ = new_stage_mask; return old_stage_mask; } VkAccessFlags GetAccessMask() const { return access_mask_; } VkAccessFlags SetAccessMask(VkAccessFlags new_access_mask) { VkAccessFlags old_access_mask = access_mask_; access_mask_ = new_access_mask; return old_access_mask; } private: VulkanCommandProcessor* command_processor_ = nullptr; VkBuffer buffer_ = VK_NULL_HANDLE; VkPipelineStageFlags stage_mask_ = 0; VkAccessFlags access_mask_ = 0; }; VulkanCommandProcessor(VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state); ~VulkanCommandProcessor(); void ClearCaches() override; void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) override; void RestoreEdramSnapshot(const void* snapshot) override; ui::vulkan::VulkanDevice* GetVulkanDevice() const { return static_cast( graphics_system_->provider()) ->vulkan_device(); } // Returns the deferred drawing command list for the currently open // submission. DeferredCommandBuffer& deferred_command_buffer() { assert_true(submission_open_); return deferred_command_buffer_; } bool submission_open() const { return submission_open_; } uint64_t GetCurrentSubmission() const { return completion_timeline_.GetUpcomingSubmission(); } uint64_t GetCompletedSubmission() const { return completion_timeline_.GetCompletedSubmissionFromLastUpdate(); } // Sparse binds are: // - In a single submission, all submitted in one vkQueueBindSparse. // - Sent to the queue without waiting for a semaphore. // Thus, multiple sparse binds between the completed and the current // submission, and within one submission, must not touch any overlapping // memory regions. void SparseBindBuffer(VkBuffer buffer, uint32_t bind_count, const VkSparseMemoryBind* binds, VkPipelineStageFlags wait_stage_mask); uint64_t GetCurrentFrame() const { return frame_current_; } uint64_t GetCompletedFrame() const { return frame_completed_; } // Submission must be open to insert barriers. If no pipeline stages access // the resource in a synchronization scope, the stage masks should be 0 (top / // bottom of pipe should be specified only if explicitly needed). Returning // true if the barrier has actually been inserted and not dropped. bool PushBufferMemoryBarrier( VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, uint32_t src_queue_family_index = VK_QUEUE_FAMILY_IGNORED, uint32_t dst_queue_family_index = VK_QUEUE_FAMILY_IGNORED, bool skip_if_equal = true); bool PushImageMemoryBarrier( VkImage image, const VkImageSubresourceRange& subresource_range, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, VkImageLayout old_layout, VkImageLayout new_layout, uint32_t src_queue_family_index = VK_QUEUE_FAMILY_IGNORED, uint32_t dst_queue_family_index = VK_QUEUE_FAMILY_IGNORED, bool skip_if_equal = true); // Returns whether any barriers have been submitted - if true is returned, the // render pass will also be closed. bool SubmitBarriers(bool force_end_render_pass); // If not started yet, begins a render pass from the render target cache. // Submission must be open. void SubmitBarriersAndEnterRenderTargetCacheRenderPass( VkRenderPass render_pass, const VulkanRenderTargetCache::Framebuffer* framebuffer); // Must be called before doing anything outside the render pass scope, // including adding pipeline barriers that are not a part of the render pass // scope. Submission must be open. void EndRenderPass(); VkDescriptorSetLayout GetSingleTransientDescriptorLayout( SingleTransientDescriptorLayout transient_descriptor_layout) const { return descriptor_set_layouts_single_transient_[size_t( transient_descriptor_layout)]; } // A frame must be open. VkDescriptorSet AllocateSingleTransientDescriptor( SingleTransientDescriptorLayout transient_descriptor_layout); // The returned reference is valid until a cache clear. VkDescriptorSetLayout GetTextureDescriptorSetLayout(bool is_vertex, size_t texture_count, size_t sampler_count); // The returned reference is valid until a cache clear. const VulkanPipelineCache::PipelineLayoutProvider* GetPipelineLayout( size_t texture_count_pixel, size_t sampler_count_pixel, size_t texture_count_vertex, size_t sampler_count_vertex); // Returns a single temporary GPU-side buffer within a submission for tasks // like texture untiling and resolving. May push a buffer memory barrier into // the initial usage. Submission must be open. ScratchBufferAcquisition AcquireScratchGpuBuffer( VkDeviceSize size, VkPipelineStageFlags initial_stage_mask, VkAccessFlags initial_access_mask); // Binds a graphics pipeline for host-specific purposes, invalidating the // affected state. keep_dynamic_* must be false (to invalidate the dynamic // state after binding the pipeline with the same state being static, or if // the caller changes the dynamic state bypassing the VulkanCommandProcessor) // unless the caller has these state variables as dynamic and uses the // tracking in VulkanCommandProcessor to modify them. void BindExternalGraphicsPipeline(VkPipeline pipeline, bool keep_dynamic_depth_bias = false, bool keep_dynamic_blend_constants = false, bool keep_dynamic_stencil_mask_ref = false); void BindExternalComputePipeline(VkPipeline pipeline); void SetViewport(const VkViewport& viewport); void SetScissor(const VkRect2D& scissor); // Returns the text to display in the GPU backend name in the window title. std::string GetWindowTitleText() const; protected: bool SetupContext() override; void ShutdownContext() override; void WriteRegister(uint32_t index, uint32_t value) override; void OnGammaRamp256EntryTableValueWritten() override; void OnGammaRampPWLValueWritten() override; void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, uint32_t frontbuffer_height) override; Shader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address, const uint32_t* host_address, uint32_t dword_count) override; bool IssueDraw(xenos::PrimitiveType prim_type, uint32_t index_count, IndexBufferInfo* index_buffer_info, bool major_mode_explicit) override; bool IssueCopy() override; void InitializeTrace() override; private: struct CommandBuffer { VkCommandPool pool; VkCommandBuffer buffer; }; struct SparseBufferBind { VkBuffer buffer; size_t bind_offset; uint32_t bind_count; }; union TextureDescriptorSetLayoutKey { uint32_t key; struct { // If texture and sampler counts are both 0, use // descriptor_set_layout_empty_ instead as these are owning references. uint32_t texture_count : 16; uint32_t sampler_count : 15; uint32_t is_vertex : 1; }; TextureDescriptorSetLayoutKey() : key(0) { static_assert_size(*this, sizeof(key)); } struct Hasher { size_t operator()(const TextureDescriptorSetLayoutKey& key) const { return std::hash{}(key.key); } }; bool operator==(const TextureDescriptorSetLayoutKey& other_key) const { return key == other_key.key; } bool operator!=(const TextureDescriptorSetLayoutKey& other_key) const { return !(*this == other_key); } }; union PipelineLayoutKey { uint64_t key; struct { // Pixel textures in the low bits since those are varied much more // commonly. uint16_t texture_count_pixel; uint16_t sampler_count_pixel; uint16_t texture_count_vertex; uint16_t sampler_count_vertex; }; PipelineLayoutKey() : key(0) { static_assert_size(*this, sizeof(key)); } struct Hasher { size_t operator()(const PipelineLayoutKey& key) const { return std::hash{}(key.key); } }; bool operator==(const PipelineLayoutKey& other_key) const { return key == other_key.key; } bool operator!=(const PipelineLayoutKey& other_key) const { return !(*this == other_key); } }; class PipelineLayout : public VulkanPipelineCache::PipelineLayoutProvider { public: explicit PipelineLayout( VkPipelineLayout pipeline_layout, VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref, VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref) : pipeline_layout_(pipeline_layout), descriptor_set_layout_textures_vertex_ref_( descriptor_set_layout_textures_vertex_ref), descriptor_set_layout_textures_pixel_ref_( descriptor_set_layout_textures_pixel_ref) {} VkPipelineLayout GetPipelineLayout() const override { return pipeline_layout_; } VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref() const { return descriptor_set_layout_textures_vertex_ref_; } VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref() const { return descriptor_set_layout_textures_pixel_ref_; } private: VkPipelineLayout pipeline_layout_; VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref_; VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref_; }; struct UsedSingleTransientDescriptor { uint64_t frame; SingleTransientDescriptorLayout layout; VkDescriptorSet set; }; struct UsedTextureTransientDescriptorSet { uint64_t frame; TextureDescriptorSetLayoutKey layout; VkDescriptorSet set; }; enum SwapApplyGammaDescriptorSet : uint32_t { kSwapApplyGammaDescriptorSetRamp, kSwapApplyGammaDescriptorSetSource, kSwapApplyGammaDescriptorSetCount, }; // Framebuffer for the current presenter's guest output image revision, and // its usage tracking. struct SwapFramebuffer { VkFramebuffer framebuffer = VK_NULL_HANDLE; uint64_t version = UINT64_MAX; uint64_t last_submission = 0; }; // BeginSubmission and EndSubmission may be called at any time. If there's an // open non-frame submission, BeginSubmission(true) will promote it to a // frame. EndSubmission(true) will close the frame no matter whether the // submission has already been closed. // Unlike on Direct3D 12, submission boundaries do not imply any memory // barriers aside from an incoming host write (but not outgoing host read) // dependency. // Rechecks submission number and reclaims per-submission resources. Pass 0 as // the submission to await to simply check status, or pass // GetCurrentSubmission() to wait for all queue operations to be completed. void CheckSubmissionCompletionAndDeviceLoss(uint64_t await_submission); // If is_guest_command is true, a new full frame - with full cleanup of // resources and, if needed, starting capturing - is opened if pending (as // opposed to simply resuming after mid-frame synchronization). Returns // whether a submission is open currently and the device is not lost. bool BeginSubmission(bool is_guest_command); // If is_swap is true, a full frame is closed - with, if needed, cache // clearing and stopping capturing. Returns whether the submission was done // successfully, if it has failed, leaves it open. bool EndSubmission(bool is_swap); bool AwaitAllQueueOperationsCompletion() { CheckSubmissionCompletionAndDeviceLoss(GetCurrentSubmission()); return !submission_open_ && GetCompletedSubmission() + 1u >= GetCurrentSubmission(); } void ClearTransientDescriptorPools(); void SplitPendingBarrier(); void DestroyScratchBuffer(); void UpdateDynamicState(const draw_util::ViewportInfo& viewport_info, bool primitive_polygonal, reg::RB_DEPTHCONTROL normalized_depth_control); void UpdateSystemConstantValues( bool primitive_polygonal, const PrimitiveProcessor::ProcessingResult& primitive_processing_result, bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask); bool UpdateBindings(const VulkanShader* vertex_shader, const VulkanShader* pixel_shader); // Allocates a descriptor set and fills one or two VkWriteDescriptorSet // structure instances (for images and samplers). // The descriptor set layout must be the one for the given is_vertex, // texture_count, sampler_count (from GetTextureDescriptorSetLayout - may be // already available at the moment of the call, no need to locate it again). // Returns how many VkWriteDescriptorSet structure instances have been // written, or 0 if there was a failure to allocate the descriptor set or no // bindings were requested. uint32_t WriteTransientTextureBindings( bool is_vertex, uint32_t texture_count, uint32_t sampler_count, VkDescriptorSetLayout descriptor_set_layout, const VkDescriptorImageInfo* texture_image_info, const VkDescriptorImageInfo* sampler_image_info, VkWriteDescriptorSet* descriptor_set_writes_out); bool device_lost_ = false; bool cache_clear_requested_ = false; // Host shader types that guest shaders can be translated into - they can // access the shared memory (via vertex fetch, memory export, or manual index // buffer reading) and textures. VkPipelineStageFlags guest_shader_pipeline_stages_ = 0; VkShaderStageFlags guest_shader_vertex_stages_ = 0; std::vector semaphores_free_; ui::vulkan::VulkanGPUCompletionTimeline completion_timeline_; bool submission_open_ = false; // In case vkQueueSubmit fails after something like a successful // vkQueueBindSparse, to wait correctly on the next attempt. std::vector current_submission_wait_semaphores_; std::vector current_submission_wait_stage_masks_; std::deque> submissions_in_flight_semaphores_; static constexpr uint32_t kMaxFramesInFlight = 3; bool frame_open_ = false; // Guest frame index, since some transient resources can be reused across // submissions. Values updated in the beginning of a frame. uint64_t frame_current_ = 1; uint64_t frame_completed_ = 0; // Submission indices of frames that have already been submitted. uint64_t closed_frame_submissions_[kMaxFramesInFlight] = {}; // , sorted by the submission number. std::deque> destroy_memory_; std::deque> destroy_buffers_; std::deque> destroy_framebuffers_; std::vector command_buffers_writable_; std::deque> command_buffers_submitted_; DeferredCommandBuffer deferred_command_buffer_; std::vector sparse_memory_binds_; std::vector sparse_buffer_binds_; // SparseBufferBind converted to VkSparseBufferMemoryBindInfo to this buffer // on submission (because pBinds should point to a place in std::vector, but // it may be reallocated). std::vector sparse_buffer_bind_infos_temp_; VkPipelineStageFlags sparse_bind_wait_stage_mask_ = 0; // Temporary storage with reusable memory for creating descriptor set layouts. std::vector descriptor_set_layout_bindings_; // Temporary storage with reusable memory for writing image and sampler // descriptors. std::vector descriptor_write_image_info_; std::unique_ptr uniform_buffer_pool_; // Descriptor set layouts used by different shaders. VkDescriptorSetLayout descriptor_set_layout_empty_ = VK_NULL_HANDLE; VkDescriptorSetLayout descriptor_set_layout_constants_ = VK_NULL_HANDLE; std::array descriptor_set_layouts_single_transient_{}; VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ = VK_NULL_HANDLE; // Descriptor set layouts are referenced by pipeline_layouts_. std::unordered_map descriptor_set_layouts_textures_; // Pipeline layouts are referenced by VulkanPipelineCache. std::unordered_map pipeline_layouts_; // No specific reason for 32768, just the "too much" descriptor count from // Direct3D 12 PIX warnings. static constexpr uint32_t kLinkedTypeDescriptorPoolSetCount = 32768; static const VkDescriptorPoolSize kDescriptorPoolSizeUniformBuffer; static const VkDescriptorPoolSize kDescriptorPoolSizeStorageBuffer; static const VkDescriptorPoolSize kDescriptorPoolSizeTextures[2]; ui::vulkan::LinkedTypeDescriptorSetAllocator transient_descriptor_allocator_uniform_buffer_; ui::vulkan::LinkedTypeDescriptorSetAllocator transient_descriptor_allocator_storage_buffer_; std::deque single_transient_descriptors_used_; std::array, size_t(SingleTransientDescriptorLayout::kCount)> single_transient_descriptors_free_; // . std::deque> constants_transient_descriptors_used_; std::vector constants_transient_descriptors_free_; ui::vulkan::LinkedTypeDescriptorSetAllocator transient_descriptor_allocator_textures_; std::deque texture_transient_descriptor_sets_used_; std::unordered_map, TextureDescriptorSetLayoutKey::Hasher> texture_transient_descriptor_sets_free_; std::unique_ptr shared_memory_; std::unique_ptr primitive_processor_; std::unique_ptr render_target_cache_; std::unique_ptr pipeline_cache_; std::unique_ptr texture_cache_; VkDescriptorPool shared_memory_and_edram_descriptor_pool_ = VK_NULL_HANDLE; VkDescriptorSet shared_memory_and_edram_descriptor_set_; // Bytes 0x0...0x3FF - 256-entry gamma ramp table with B10G10R10X2 data (read // as R10G10B10X2 with swizzle). // Bytes 0x400...0x9FF - 128-entry PWL R16G16 gamma ramp (R - base, G - delta, // low 6 bits of each are zero, 3 elements per entry). // kMaxFramesInFlight pairs of gamma ramps if in host-visible memory and // uploaded directly, one otherwise. VkDeviceMemory gamma_ramp_buffer_memory_ = VK_NULL_HANDLE; VkBuffer gamma_ramp_buffer_ = VK_NULL_HANDLE; // kMaxFramesInFlight pairs, only when the gamma ramp buffer is not // host-visible. VkDeviceMemory gamma_ramp_upload_buffer_memory_ = VK_NULL_HANDLE; VkBuffer gamma_ramp_upload_buffer_ = VK_NULL_HANDLE; VkDeviceSize gamma_ramp_upload_memory_size_; uint32_t gamma_ramp_upload_memory_type_; // Mapping of either gamma_ramp_buffer_memory_ (if it's host-visible) or // gamma_ramp_upload_buffer_memory_ (otherwise). void* gamma_ramp_upload_mapping_; std::array gamma_ramp_buffer_views_{}; // UINT32_MAX if outdated. uint32_t gamma_ramp_256_entry_table_current_frame_ = UINT32_MAX; uint32_t gamma_ramp_pwl_current_frame_ = UINT32_MAX; VkDescriptorSetLayout swap_descriptor_set_layout_sampled_image_ = VK_NULL_HANDLE; VkDescriptorSetLayout swap_descriptor_set_layout_uniform_texel_buffer_ = VK_NULL_HANDLE; // Descriptor pool for allocating descriptors needed for presentation, such as // the destination images and the gamma ramps. VkDescriptorPool swap_descriptor_pool_ = VK_NULL_HANDLE; // Interleaved 256-entry table and PWL texel buffer descriptors. // kMaxFramesInFlight pairs of gamma ramps if in host-visible memory and // uploaded directly, one otherwise. std::array swap_descriptors_gamma_ramp_; // Sampled images. std::array swap_descriptors_source_; VkPipelineLayout swap_apply_gamma_pipeline_layout_ = VK_NULL_HANDLE; // Has no dependencies on specific pipeline stages on both ends to simplify // use in different scenarios with different pipelines - use explicit barriers // for synchronization. VkRenderPass swap_apply_gamma_render_pass_ = VK_NULL_HANDLE; VkPipeline swap_apply_gamma_256_entry_table_pipeline_ = VK_NULL_HANDLE; VkPipeline swap_apply_gamma_pwl_pipeline_ = VK_NULL_HANDLE; std::array swap_framebuffers_; // Pending pipeline barriers. std::vector pending_barriers_buffer_memory_barriers_; std::vector pending_barriers_image_memory_barriers_; struct PendingBarrier { VkPipelineStageFlags src_stage_mask = 0; VkPipelineStageFlags dst_stage_mask = 0; size_t buffer_memory_barriers_offset = 0; size_t image_memory_barriers_offset = 0; }; std::vector pending_barriers_; PendingBarrier current_pending_barrier_; // GPU-local scratch buffer. static constexpr VkDeviceSize kScratchBufferSizeIncrement = 16 * 1024 * 1024; VkDeviceMemory scratch_buffer_memory_ = VK_NULL_HANDLE; VkBuffer scratch_buffer_ = VK_NULL_HANDLE; VkDeviceSize scratch_buffer_size_ = 0; VkPipelineStageFlags scratch_buffer_last_stage_mask_ = 0; VkAccessFlags scratch_buffer_last_access_mask_ = 0; uint64_t scratch_buffer_last_usage_submission_ = 0; bool scratch_buffer_used_ = false; // The current dynamic state of the graphics pipeline bind point. Note that // binding any pipeline to the bind point with static state (even if it's // unused, like depth bias being disabled, but the values themselves still not // declared as dynamic in the pipeline) invalidates such dynamic state. VkViewport dynamic_viewport_; VkRect2D dynamic_scissor_; // Dynamic fixed-function depth bias, blend constants, stencil state are // applicable only to the render target implementations where they are // actually involved. float dynamic_depth_bias_constant_factor_; float dynamic_depth_bias_slope_factor_; float dynamic_blend_constants_[4]; // The stencil values are pre-initialized (to D3D11_DEFAULT_STENCIL_*, and the // initial values for front and back are the same for portability subset // safety) because they're updated conditionally to avoid changing the back // face values when stencil is disabled and the primitive type is changed // between polygonal and non-polygonal. uint32_t dynamic_stencil_compare_mask_front_ = UINT8_MAX; uint32_t dynamic_stencil_compare_mask_back_ = UINT8_MAX; uint32_t dynamic_stencil_write_mask_front_ = UINT8_MAX; uint32_t dynamic_stencil_write_mask_back_ = UINT8_MAX; uint32_t dynamic_stencil_reference_front_ = 0; uint32_t dynamic_stencil_reference_back_ = 0; bool dynamic_viewport_update_needed_; bool dynamic_scissor_update_needed_; bool dynamic_depth_bias_update_needed_; bool dynamic_blend_constants_update_needed_; bool dynamic_stencil_compare_mask_front_update_needed_; bool dynamic_stencil_compare_mask_back_update_needed_; bool dynamic_stencil_write_mask_front_update_needed_; bool dynamic_stencil_write_mask_back_update_needed_; bool dynamic_stencil_reference_front_update_needed_; bool dynamic_stencil_reference_back_update_needed_; // Currently used samplers. std::vector> current_samplers_vertex_; std::vector> current_samplers_pixel_; // Cache render pass currently started in the command buffer with the // framebuffer. VkRenderPass current_render_pass_; const VulkanRenderTargetCache::Framebuffer* current_framebuffer_; // Currently bound graphics pipeline, either from the pipeline cache (with // potentially deferred creation - current_external_graphics_pipeline_ is // VK_NULL_HANDLE in this case) or a non-Xenos one // (current_guest_graphics_pipeline_ is VK_NULL_HANDLE in this case). // TODO(Triang3l): Change to a deferred compilation handle. VkPipeline current_guest_graphics_pipeline_; VkPipeline current_external_graphics_pipeline_; VkPipeline current_external_compute_pipeline_; // Pipeline layout of the current guest graphics pipeline. const PipelineLayout* current_guest_graphics_pipeline_layout_; VkDescriptorBufferInfo current_constant_buffer_infos_ [SpirvShaderTranslator::kConstantBufferCount]; // Whether up-to-date data has been written to constant (uniform) buffers, and // the buffer infos in current_constant_buffer_infos_ point to them. uint32_t current_constant_buffers_up_to_date_; VkDescriptorSet current_graphics_descriptor_sets_ [SpirvShaderTranslator::kDescriptorSetCount]; // Whether descriptor sets in current_graphics_descriptor_sets_ point to // up-to-date data. uint32_t current_graphics_descriptor_set_values_up_to_date_; // Whether the descriptor sets currently bound to the command buffer - only // low bits for the descriptor set layouts that remained the same are kept // when changing the pipeline layout. May be out of sync with // current_graphics_descriptor_set_values_up_to_date_, but should be ensured // to be a subset of it at some point when it becomes important; bits for // non-existent descriptor set layouts may also be set, but need to be ignored // when they start to matter. uint32_t current_graphics_descriptor_sets_bound_up_to_date_; static_assert( SpirvShaderTranslator::kDescriptorSetCount <= sizeof(current_graphics_descriptor_set_values_up_to_date_) * CHAR_BIT, "Bit fields storing descriptor set validity must be large enough"); static_assert( SpirvShaderTranslator::kDescriptorSetCount <= sizeof(current_graphics_descriptor_sets_bound_up_to_date_) * CHAR_BIT, "Bit fields storing descriptor set validity must be large enough"); // Float constant usage masks of the last draw call. uint64_t current_float_constant_map_vertex_[4]; uint64_t current_float_constant_map_pixel_[4]; // System shader constants. SpirvShaderTranslator::SystemConstants system_constants_; // Temporary storage for memexport stream constants used in the draw. std::vector memexport_ranges_; }; } // namespace vulkan } // namespace gpu } // namespace xe #endif // XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_