Simplify the code, eliminating the need for supporting requesting cbuffers for anything other than guest draw command execution.
740 lines
31 KiB
C++
740 lines
31 KiB
C++
/**
|
|
******************************************************************************
|
|
* 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 <array>
|
|
#include <climits>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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_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<const ui::vulkan::VulkanProvider*>(
|
|
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 submission_completed_ +
|
|
uint64_t(submissions_in_flight_fences_.size()) + 1;
|
|
}
|
|
uint64_t GetCompletedSubmission() const { return submission_completed_; }
|
|
|
|
// 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<decltype(key.key)>{}(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<decltype(key.key)>{}(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 CheckSubmissionFenceAndDeviceLoss(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() {
|
|
CheckSubmissionFenceAndDeviceLoss(GetCurrentSubmission());
|
|
return !submission_open_ && submissions_in_flight_fences_.empty();
|
|
}
|
|
|
|
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<VkFence> fences_free_;
|
|
std::vector<VkSemaphore> semaphores_free_;
|
|
|
|
bool submission_open_ = false;
|
|
uint64_t submission_completed_ = 0;
|
|
// In case vkQueueSubmit fails after something like a successful
|
|
// vkQueueBindSparse, to wait correctly on the next attempt.
|
|
std::vector<VkSemaphore> current_submission_wait_semaphores_;
|
|
std::vector<VkPipelineStageFlags> current_submission_wait_stage_masks_;
|
|
std::vector<VkFence> submissions_in_flight_fences_;
|
|
std::deque<std::pair<uint64_t, VkSemaphore>>
|
|
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] = {};
|
|
|
|
// <Submission where last used, resource>, sorted by the submission number.
|
|
std::deque<std::pair<uint64_t, VkDeviceMemory>> destroy_memory_;
|
|
std::deque<std::pair<uint64_t, VkBuffer>> destroy_buffers_;
|
|
std::deque<std::pair<uint64_t, VkFramebuffer>> destroy_framebuffers_;
|
|
|
|
std::vector<CommandBuffer> command_buffers_writable_;
|
|
std::deque<std::pair<uint64_t, CommandBuffer>> command_buffers_submitted_;
|
|
DeferredCommandBuffer deferred_command_buffer_;
|
|
|
|
std::vector<VkSparseMemoryBind> sparse_memory_binds_;
|
|
std::vector<SparseBufferBind> 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<VkSparseBufferMemoryBindInfo> sparse_buffer_bind_infos_temp_;
|
|
VkPipelineStageFlags sparse_bind_wait_stage_mask_ = 0;
|
|
|
|
// Temporary storage with reusable memory for creating descriptor set layouts.
|
|
std::vector<VkDescriptorSetLayoutBinding> descriptor_set_layout_bindings_;
|
|
// Temporary storage with reusable memory for writing image and sampler
|
|
// descriptors.
|
|
std::vector<VkDescriptorImageInfo> descriptor_write_image_info_;
|
|
|
|
std::unique_ptr<ui::vulkan::VulkanUploadBufferPool> 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<VkDescriptorSetLayout,
|
|
size_t(SingleTransientDescriptorLayout::kCount)>
|
|
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<TextureDescriptorSetLayoutKey, VkDescriptorSetLayout,
|
|
TextureDescriptorSetLayoutKey::Hasher>
|
|
descriptor_set_layouts_textures_;
|
|
// Pipeline layouts are referenced by VulkanPipelineCache.
|
|
std::unordered_map<PipelineLayoutKey, PipelineLayout,
|
|
PipelineLayoutKey::Hasher>
|
|
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<UsedSingleTransientDescriptor> single_transient_descriptors_used_;
|
|
std::array<std::vector<VkDescriptorSet>,
|
|
size_t(SingleTransientDescriptorLayout::kCount)>
|
|
single_transient_descriptors_free_;
|
|
// <Usage frame, set>.
|
|
std::deque<std::pair<uint64_t, VkDescriptorSet>>
|
|
constants_transient_descriptors_used_;
|
|
std::vector<VkDescriptorSet> constants_transient_descriptors_free_;
|
|
|
|
ui::vulkan::LinkedTypeDescriptorSetAllocator
|
|
transient_descriptor_allocator_textures_;
|
|
std::deque<UsedTextureTransientDescriptorSet>
|
|
texture_transient_descriptor_sets_used_;
|
|
std::unordered_map<TextureDescriptorSetLayoutKey,
|
|
std::vector<VkDescriptorSet>,
|
|
TextureDescriptorSetLayoutKey::Hasher>
|
|
texture_transient_descriptor_sets_free_;
|
|
|
|
std::unique_ptr<VulkanSharedMemory> shared_memory_;
|
|
|
|
std::unique_ptr<VulkanPrimitiveProcessor> primitive_processor_;
|
|
|
|
std::unique_ptr<VulkanRenderTargetCache> render_target_cache_;
|
|
|
|
std::unique_ptr<VulkanPipelineCache> pipeline_cache_;
|
|
|
|
std::unique_ptr<VulkanTextureCache> 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<VkBufferView, 2 * kMaxFramesInFlight> 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<VkDescriptorSet, 2 * kMaxFramesInFlight>
|
|
swap_descriptors_gamma_ramp_;
|
|
// Sampled images.
|
|
std::array<VkDescriptorSet, kMaxFramesInFlight> 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<SwapFramebuffer,
|
|
ui::vulkan::VulkanPresenter::kMaxActiveGuestOutputImageVersions>
|
|
swap_framebuffers_;
|
|
|
|
// Pending pipeline barriers.
|
|
std::vector<VkBufferMemoryBarrier> pending_barriers_buffer_memory_barriers_;
|
|
std::vector<VkImageMemoryBarrier> 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<PendingBarrier> 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<std::pair<VulkanTextureCache::SamplerParameters, VkSampler>>
|
|
current_samplers_vertex_;
|
|
std::vector<std::pair<VulkanTextureCache::SamplerParameters, VkSampler>>
|
|
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<draw_util::MemExportRange> memexport_ranges_;
|
|
};
|
|
|
|
} // namespace vulkan
|
|
} // namespace gpu
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_
|