Files
Xenia-Canary/src/xenia/gpu/vulkan/vulkan_command_processor.h
chss95cs@gmail.com 8f7f7dc6ad fixed wine crash from use of NtSetEventPriorityBoost
add xe::clear_lowest_bit, use it in place of shift-andnot in some bit iteration code
make is_allocated_ and is_enabled_ volatile in xma_context
preallocate avpacket buffer in XMAContext::Setup, the reallocations of the buffer in ffmpeg were showing up on profiles
check is_enabled and is_allocated BEFORE locking an xmacontext. XMA worker was spending most of its time locking and unlocking contexts
Removed XeDMAC, dma:: namespace. It was a bad idea and I couldn't make it work in the end. Kept vastcpy and moved it to the memory namespace instead
Made the rest of global_critical_region's members static. They never needed an instance.
Removed ifdef'ed out code from ring_buffer.h
Added EventInfo struct to threading, added Event::Query to aid with implementing NtQueryEvent.
Removed vector from WaitMultiple, instead use a fixed array of 64 handles that we populate. WaitForMultipleObjects cannot handle more than 64 objects.
Remove XE_MSVC_OPTIMIZE_SMALL() use in x64_sequences, x64 backend is now always size optimized because of premake
Make global_critical_region_ static constexpr in shared_memory.h to get rid of wasteage of 8 bytes (empty class=1byte, +alignment for next member=8)
Move trace-related data to the tail of SharedMemory to keep more important data together
In IssueDraw build an array of fetch constant addresses/sizes, then pre-lock the global lock before doing requestrange for each instead of individually locking within requestrange for each of them
Consistent access specifier protected for pm4_command_processor_declare
Devirtualize WriteOneRegisterFromRing.
Move ExecutePacket and ExecutePrimaryBuffer to pm4_command_buffer_x
Remove many redundant header inclusions access xenia-gpu
Minor microoptimization of ExecutePacketType0

Add TextureCache::RequestTextures for batch invocation of LoadTexturesData

Add TextureCache::LoadTexturesData for reducing the number of times we release and reacquire the global lock.
Ideally you should hold the global lock for as little time as possible, but if you are constantly acquiring and releasing it you are actually more likely to have contention
Add already_locked param to ObjectTable::LookupObject to help with reducing lock acquire/release pairs
Add missing checks to XAudioRegisterRenderDriverClient_entry. this is unlikely to fix anything, it was just an easy thing to do
Add NtQueryEvent system call implementation. I don't actually know of any games that need it.
Instead of using std::vector + push_back in KeWaitForMultipleObjects and xeNtWaitForMultipleObjectsEx use a fixed size array of 64 and track the count. More than 64 objects is not permitted by the kernel. The repeated reallocations from push_back were appearing unusually high on the profiler, but were masked until now by waitformultipleobjects natural overhead
Pre-lock the global lock before looking up each handle for xeNtWaitForMultipleObjectsEx and KeWaitForMultipleObjects.
Pre-lock before looking up the signal and waiter in NtSignalAndWaitForSingleObjectEx
add missing checks to NtWaitForMultipleObjectsEx
Support pre-locking in XObject::GetNativeObject
2022-10-08 09:55:17 -07:00

745 lines
32 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 <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 final : public CommandProcessor {
protected:
#include "../pm4_command_processor_declare.h"
public:
// Single-descriptor layouts for use within a single frame.
enum class SingleTransientDescriptorLayout {
kUniformBufferCompute,
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::VulkanProvider& GetVulkanProvider() const {
return *static_cast<ui::vulkan::VulkanProvider*>(
graphics_system_->provider());
}
// 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);
// Allocates a descriptor, space in the uniform buffer pool, and fills the
// VkWriteDescriptorSet structure and VkDescriptorBufferInfo referenced by it.
// Returns null in case of failure.
uint8_t* WriteTransientUniformBufferBinding(
size_t size, SingleTransientDescriptorLayout transient_descriptor_layout,
VkDescriptorBufferInfo& descriptor_buffer_info_out,
VkWriteDescriptorSet& write_descriptor_set_out);
uint8_t* WriteTransientUniformBufferBinding(
size_t size, SingleTransientDescriptorLayout transient_descriptor_layout,
VkDescriptorSet& descriptor_set_out);
// 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);
protected:
bool SetupContext() override;
void ShutdownContext() override;
XE_FORCEINLINE
void WriteRegister(uint32_t index, uint32_t value) override;
XE_FORCEINLINE
virtual void WriteRegistersFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers) 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);
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_shared_memory_and_edram_ =
VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout_constants_ = VK_NULL_HANDLE;
std::array<VkDescriptorSetLayout,
size_t(SingleTransientDescriptorLayout::kCount)>
descriptor_set_layouts_single_transient_{};
// 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_;
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_;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_