912 lines
43 KiB
C++
912 lines
43 KiB
C++
#pragma once
|
|
/**
|
|
******************************************************************************
|
|
* 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. *
|
|
******************************************************************************
|
|
*
|
|
* @modified Tom Clay, 2026 - Adapted for ReXGlue runtime
|
|
*/
|
|
|
|
#include <array>
|
|
#include <climits>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <rex/assert.h>
|
|
#include <rex/graphics/command_processor.h>
|
|
#include <rex/graphics/pipeline/shader/spirv_translator.h>
|
|
#include <rex/graphics/registers.h>
|
|
#include <rex/graphics/util/draw.h>
|
|
#include <rex/graphics/vulkan/deferred_command_buffer.h>
|
|
#include <rex/graphics/vulkan/graphics_system.h>
|
|
#include <rex/graphics/vulkan/pipeline_cache.h>
|
|
#include <rex/graphics/vulkan/primitive_processor.h>
|
|
#include <rex/graphics/vulkan/render_target_cache.h>
|
|
#include <rex/graphics/vulkan/shader.h>
|
|
#include <rex/graphics/vulkan/shared_memory.h>
|
|
#include <rex/graphics/vulkan/texture_cache.h>
|
|
#include <rex/graphics/xenos.h>
|
|
#include <rex/hash.h>
|
|
#include <rex/perf/counter.h>
|
|
#include <rex/system/kernel_state.h>
|
|
#include <rex/ui/vulkan/linked_type_descriptor_set_allocator.h>
|
|
#include <rex/ui/vulkan/presenter.h>
|
|
#include <rex/ui/vulkan/provider.h>
|
|
#include <rex/ui/vulkan/upload_buffer_pool.h>
|
|
|
|
namespace rex::graphics::vulkan {
|
|
|
|
class VulkanCommandProcessor : public CommandProcessor {
|
|
public:
|
|
// Single-descriptor layouts for use within a single frame.
|
|
enum class SingleTransientDescriptorLayout {
|
|
kStorageBufferCompute,
|
|
kStorageBufferPairCompute,
|
|
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, system::KernelState* kernel_state);
|
|
~VulkanCommandProcessor();
|
|
|
|
void ClearCaches() override;
|
|
void InvalidateGpuMemory() override;
|
|
void InitializeShaderStorage(const std::filesystem::path& cache_root, uint32_t title_id,
|
|
bool blocking) 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();
|
|
}
|
|
|
|
bool CompileGlslToSpirv(VkShaderStageFlagBits stage, std::string_view source,
|
|
std::vector<uint32_t>& spirv_out, std::string& error_out) const;
|
|
|
|
// 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);
|
|
// Overload for transfer operations with dynamic rendering.
|
|
void SubmitBarriersAndEnterRenderTargetCacheRenderPass(
|
|
VkRenderPass render_pass, const VulkanRenderTargetCache::Framebuffer* framebuffer,
|
|
VkImageView transfer_dest_view, bool transfer_dest_is_depth);
|
|
// 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);
|
|
uint32_t BeginGpuTimestampedRegion(rex::perf::DrawBucket bucket) {
|
|
return BeginGpuTimestampedDraw(bucket);
|
|
}
|
|
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 WriteRegistersFromMem(uint32_t start_index, uint32_t* base, uint32_t num_registers) override;
|
|
bool ExecutePacketType3_EVENT_WRITE_ZPD(memory::RingBuffer* reader, uint32_t packet,
|
|
uint32_t count) override;
|
|
|
|
void OnGammaRamp256EntryTableValueWritten() override;
|
|
void OnGammaRampPWLValueWritten() override;
|
|
void OnPrimaryBufferEnd() 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:
|
|
friend class VulkanTextureCache;
|
|
|
|
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,
|
|
};
|
|
enum SwapApplyGammaComputeDescriptorSet : uint32_t {
|
|
kSwapApplyGammaComputeDescriptorSetRamp,
|
|
kSwapApplyGammaComputeDescriptorSetSource,
|
|
kSwapApplyGammaComputeDescriptorSetDestination,
|
|
|
|
kSwapApplyGammaComputeDescriptorSetCount,
|
|
};
|
|
struct SwapApplyGammaConstants {
|
|
uint32_t size[2];
|
|
};
|
|
enum SwapFxaaDescriptorSet : uint32_t {
|
|
kSwapFxaaDescriptorSetSource,
|
|
kSwapFxaaDescriptorSetDestination,
|
|
|
|
kSwapFxaaDescriptorSetCount,
|
|
};
|
|
struct SwapFxaaConstants {
|
|
uint32_t size[2];
|
|
float size_inv[2];
|
|
};
|
|
|
|
// 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();
|
|
}
|
|
// Keep primary-buffer-end submit behavior aligned with D3D12: only submit
|
|
// when immediate submission is safe.
|
|
bool CanEndSubmissionImmediately() const;
|
|
void DebugRecordResolve(uint32_t address, uint32_t length, bool scaled);
|
|
|
|
void ClearTransientDescriptorPools();
|
|
bool IssueCopy_ReadbackResolvePath();
|
|
bool DownscaleScaledResolveToSharedMemory(uint32_t start_unscaled, uint32_t length_unscaled,
|
|
uint32_t pixel_size_log2);
|
|
bool IssueDraw_MemexportReadbackFullPath(uint32_t total_size);
|
|
bool IssueDraw_MemexportReadbackFastPath(uint32_t total_size);
|
|
|
|
void SplitPendingBarrier();
|
|
|
|
void DestroyScratchBuffer();
|
|
bool InitializeOcclusionQueryResources();
|
|
void ShutdownOcclusionQueryResources();
|
|
bool BeginGuestOcclusionQuery(uint32_t sample_count_address);
|
|
bool EndGuestOcclusionQuery(uint32_t sample_count_address);
|
|
bool AcquireOcclusionQueryIndex(uint32_t& host_index_out);
|
|
void DisableHostOcclusionQueries();
|
|
uint64_t NormalizeOcclusionSamples(uint64_t samples) const;
|
|
void WriteGuestOcclusionResult(uint32_t sample_count_address, uint64_t samples);
|
|
bool InitializeGpuTimestampResources();
|
|
void ShutdownGpuTimestampResources();
|
|
void BeginGpuTimestampFrame();
|
|
void EndGpuTimestampFrame();
|
|
void ResolveGpuTimestampFrame(VkCommandBuffer command_buffer);
|
|
void ProcessGpuTimestampResults();
|
|
uint32_t BeginGpuTimestampedDraw(rex::perf::DrawBucket bucket);
|
|
void EndGpuTimestampedDraw(uint32_t start_query);
|
|
void InvalidateAllVertexBufferResidency();
|
|
void InvalidateVertexBufferResidency(uint32_t vfetch_index);
|
|
void InvalidateVertexBufferResidencyRange(uint32_t first_vfetch, uint32_t last_vfetch);
|
|
struct ReadbackBuffer {
|
|
VkBuffer buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
|
VkDeviceMemory memories[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
|
void* mapped_data[2] = {nullptr, nullptr};
|
|
uint32_t sizes[2] = {0, 0};
|
|
uint64_t submission_written[2] = {0, 0};
|
|
uint32_t written_size[2] = {0, 0};
|
|
uint32_t current_index = 0;
|
|
uint64_t last_used_frame = 0;
|
|
};
|
|
void EvictOldReadbackBuffers(std::unordered_map<uint64_t, ReadbackBuffer>& buffer_map);
|
|
static constexpr uint32_t kReadbackBufferSizeIncrement = 16 * 1024 * 1024;
|
|
static constexpr size_t kMaxReadbackBuffers = 256;
|
|
static constexpr uint64_t kReadbackBufferEvictionAgeFrames = 60;
|
|
static inline uint32_t AlignReadbackBufferSize(uint32_t size) {
|
|
if (size < 1 * 1024 * 1024) {
|
|
return rex::align(size, 256u * 1024u);
|
|
}
|
|
if (size < 4 * 1024 * 1024) {
|
|
return rex::align(size, 1u * 1024u * 1024u);
|
|
}
|
|
return rex::align(size, kReadbackBufferSizeIncrement);
|
|
}
|
|
static inline uint64_t MakeReadbackResolveKey(uint32_t address, uint32_t length) {
|
|
return (uint64_t(address) << 32) | uint64_t(length);
|
|
}
|
|
static inline uint64_t MakeMemexportReadbackKey(uint32_t first_base_address_dwords,
|
|
uint32_t total_size) {
|
|
return (uint64_t(first_base_address_dwords) << 32) | uint64_t(total_size);
|
|
}
|
|
struct ResolveDownscaleConstants {
|
|
uint32_t scale_x;
|
|
uint32_t scale_y;
|
|
uint32_t pixel_size_log2;
|
|
uint32_t tile_count;
|
|
uint32_t source_offset_bytes;
|
|
uint32_t half_pixel_offset;
|
|
};
|
|
bool EnsureSwapFxaaSourceImage(uint32_t width, uint32_t height);
|
|
void DestroySwapFxaaSourceImage();
|
|
|
|
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, uint32_t compute_memexport_vertex_count,
|
|
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
|
|
uint64_t vertex_shader_hash, uint64_t pixel_shader_hash,
|
|
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;
|
|
// Tracks whether any draw in the current frame used an async placeholder
|
|
// graphics pipeline and may have produced incomplete output.
|
|
bool frame_used_async_placeholder_pipeline_ = false;
|
|
uint32_t async_placeholder_consecutive_frame_skips_ = 0;
|
|
bool async_placeholder_skip_fallback_logged_ = false;
|
|
uint32_t debug_frame_draws_ = 0;
|
|
uint32_t debug_frame_copy_resolves_ = 0;
|
|
uint32_t debug_frame_raster_draws_ = 0;
|
|
uint32_t debug_frame_color_draws_ = 0;
|
|
uint32_t debug_frame_depth_stencil_draws_ = 0;
|
|
uint32_t debug_frame_texture_draws_ = 0;
|
|
uint32_t debug_frame_placeholder_draws_ = 0;
|
|
uint32_t debug_frame_no_effect_draws_ = 0;
|
|
uint32_t debug_frame_memexport_draws_ = 0;
|
|
uint32_t debug_frame_resolve_ranges_ = 0;
|
|
uint32_t debug_frame_scaled_resolve_ranges_ = 0;
|
|
uint64_t debug_frame_resolve_bytes_ = 0;
|
|
static constexpr uint32_t kDebugFrameResolveRangeSamples = 6;
|
|
std::array<uint32_t, kDebugFrameResolveRangeSamples> debug_frame_resolve_addresses_ = {};
|
|
std::array<uint32_t, kDebugFrameResolveRangeSamples> debug_frame_resolve_lengths_ = {};
|
|
std::array<bool, kDebugFrameResolveRangeSamples> debug_frame_resolve_scaled_ = {};
|
|
// 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, VkImageView>> destroy_image_views_;
|
|
std::deque<std::pair<uint64_t, VkImage>> destroy_images_;
|
|
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_combined_image_sampler_ = VK_NULL_HANDLE;
|
|
VkDescriptorSetLayout swap_descriptor_set_layout_storage_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_;
|
|
// Combined image sampler descriptors for FXAA.
|
|
std::array<VkDescriptorSet, kMaxFramesInFlight> swap_descriptors_fxaa_source_;
|
|
// Storage image descriptors for the apply-gamma compute pass destination.
|
|
std::array<VkDescriptorSet, kMaxFramesInFlight> swap_descriptors_destination_storage_;
|
|
// Separate storage image descriptors for the FXAA compute destination.
|
|
std::array<VkDescriptorSet, kMaxFramesInFlight> swap_descriptors_fxaa_destination_storage_;
|
|
|
|
VkSampler swap_sampler_linear_clamp_ = VK_NULL_HANDLE;
|
|
|
|
VkPipelineLayout swap_apply_gamma_pipeline_layout_ = VK_NULL_HANDLE;
|
|
VkPipelineLayout swap_apply_gamma_compute_pipeline_layout_ = VK_NULL_HANDLE;
|
|
VkPipelineLayout swap_fxaa_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;
|
|
VkPipeline swap_apply_gamma_256_entry_table_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_pwl_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_256_entry_table_fxaa_luma_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_pwl_fxaa_luma_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_256_entry_table_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_pwl_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_256_entry_table_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_pwl_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_256_entry_table_fxaa_luma_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_pwl_fxaa_luma_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_256_entry_table_fxaa_luma_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_apply_gamma_compute_pwl_fxaa_luma_rb_swap_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_fxaa_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipeline swap_fxaa_extreme_pipeline_ = VK_NULL_HANDLE;
|
|
VkPipelineLayout resolve_downscale_pipeline_layout_ = VK_NULL_HANDLE;
|
|
VkPipeline resolve_downscale_pipeline_ = VK_NULL_HANDLE;
|
|
VkBuffer resolve_downscale_buffer_ = VK_NULL_HANDLE;
|
|
VkDeviceMemory resolve_downscale_buffer_memory_ = VK_NULL_HANDLE;
|
|
uint32_t resolve_downscale_buffer_size_ = 0;
|
|
|
|
VkImage swap_fxaa_source_image_ = VK_NULL_HANDLE;
|
|
VkDeviceMemory swap_fxaa_source_image_memory_ = VK_NULL_HANDLE;
|
|
VkImageView swap_fxaa_source_image_view_ = VK_NULL_HANDLE;
|
|
uint32_t swap_fxaa_source_image_width_ = 0;
|
|
uint32_t swap_fxaa_source_image_height_ = 0;
|
|
uint64_t swap_fxaa_source_image_submission_ = 0;
|
|
VkPipelineStageFlags swap_fxaa_source_stage_mask_ = 0;
|
|
VkAccessFlags swap_fxaa_source_access_mask_ = 0;
|
|
VkImageLayout swap_fxaa_source_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
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;
|
|
|
|
static constexpr uint32_t kMaxOcclusionQueries = 8192;
|
|
VkQueryPool occlusion_query_pool_ = VK_NULL_HANDLE;
|
|
VkBuffer occlusion_query_readback_buffer_ = VK_NULL_HANDLE;
|
|
VkDeviceMemory occlusion_query_readback_memory_ = VK_NULL_HANDLE;
|
|
uint32_t occlusion_query_readback_memory_type_ = UINT32_MAX;
|
|
VkDeviceSize occlusion_query_readback_memory_size_ = 0;
|
|
uint8_t* occlusion_query_readback_mapping_ = nullptr;
|
|
uint32_t occlusion_query_cursor_ = 0;
|
|
bool occlusion_query_resources_available_ = false;
|
|
struct ActiveOcclusionQuery {
|
|
uint32_t sample_count_address = 0;
|
|
uint32_t host_index = UINT32_MAX;
|
|
bool valid = false;
|
|
} active_occlusion_query_;
|
|
// MoltenVK maps timestamp queries to Metal counter sample buffers with a
|
|
// small per-pool limit, so keep the per-frame query budget modest.
|
|
static constexpr uint32_t kMaxGpuTimestampQueriesPerFrame = 1024;
|
|
VkQueryPool gpu_timestamp_query_pool_ = VK_NULL_HANDLE;
|
|
VkBuffer gpu_timestamp_readback_buffer_ = VK_NULL_HANDLE;
|
|
VkDeviceMemory gpu_timestamp_readback_memory_ = VK_NULL_HANDLE;
|
|
uint32_t gpu_timestamp_readback_memory_type_ = UINT32_MAX;
|
|
VkDeviceSize gpu_timestamp_readback_memory_size_ = 0;
|
|
uint64_t* gpu_timestamp_readback_mapping_ = nullptr;
|
|
bool gpu_timestamp_resources_available_ = false;
|
|
struct GpuTimestampFrame {
|
|
uint64_t frame = 0;
|
|
uint64_t submission = 0;
|
|
uint32_t query_base = 0;
|
|
uint32_t query_count = 0;
|
|
uint32_t frame_start_query = UINT32_MAX;
|
|
uint32_t frame_end_query = UINT32_MAX;
|
|
bool pending = false;
|
|
bool active_bucket_valid = false;
|
|
rex::perf::DrawBucket active_bucket = rex::perf::DrawBucket::kMainColorDepth;
|
|
uint32_t active_bucket_index = UINT32_MAX;
|
|
std::vector<rex::perf::DrawBucket> buckets;
|
|
std::vector<uint32_t> bucket_start_queries;
|
|
std::vector<uint32_t> bucket_end_queries;
|
|
};
|
|
std::array<GpuTimestampFrame, kMaxFramesInFlight> gpu_timestamp_frames_;
|
|
struct VertexBufferState {
|
|
uint32_t address = UINT32_MAX;
|
|
uint32_t size = UINT32_MAX;
|
|
};
|
|
std::array<VertexBufferState, 96> vertex_buffer_states_{};
|
|
uint64_t vertex_buffers_in_sync_[2] = {};
|
|
std::unordered_map<uint64_t, ReadbackBuffer> readback_buffers_;
|
|
std::unordered_map<uint64_t, ReadbackBuffer> memexport_readback_buffers_;
|
|
|
|
// 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. For dynamic rendering, current_render_pass_ is VK_NULL_HANDLE
|
|
// but in_render_pass_ is true.
|
|
VkRenderPass current_render_pass_;
|
|
const VulkanRenderTargetCache::Framebuffer* current_framebuffer_;
|
|
bool in_render_pass_ = false;
|
|
|
|
// 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 rex::graphics::vulkan
|