This commit is contained in:
GPUCode
2023-11-19 20:39:19 +02:00
parent 22dea3c194
commit 287db66914
17 changed files with 282 additions and 294 deletions

View File

@@ -176,15 +176,23 @@ struct TexturingRegs {
INSERT_PADDING_WORDS(0x9); INSERT_PADDING_WORDS(0x9);
struct FullTextureConfig { struct FullTextureConfig {
const bool enabled; u32 enabled;
const TextureConfig config; TextureConfig config;
const TextureFormat format; TextureFormat format;
bool operator==(const FullTextureConfig& other) const noexcept {
return std::memcmp(this, &other, sizeof(other)) == 0;
}
}; };
const std::array<FullTextureConfig, 3> GetTextures() const { static_assert(std::has_unique_object_representations_v<FullTextureConfig>);
using Textures = std::array<FullTextureConfig, 3>;
const Textures GetTextures() const {
return {{ return {{
{static_cast<bool>(main_config.texture0_enable), texture0, texture0_format}, {main_config.texture0_enable, texture0, texture0_format},
{static_cast<bool>(main_config.texture1_enable), texture1, texture1_format}, {main_config.texture1_enable, texture1, texture1_format},
{static_cast<bool>(main_config.texture2_enable), texture2, texture2_format}, {main_config.texture2_enable, texture2, texture2_format},
}}; }};
} }
@@ -381,11 +389,11 @@ struct TexturingRegs {
BitField<16, 2, u32> alpha_scale; BitField<16, 2, u32> alpha_scale;
}; };
inline unsigned GetColorMultiplier() const { inline u32 GetColorMultiplier() const {
return (color_scale < 3) ? (1 << color_scale) : 1; return (color_scale < 3) ? (1 << color_scale) : 1;
} }
inline unsigned GetAlphaMultiplier() const { inline u32 GetAlphaMultiplier() const {
return (alpha_scale < 3) ? (1 << alpha_scale) : 1; return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
} }
}; };

View File

@@ -35,6 +35,7 @@ struct ScreenRectVertex {
Common::Vec2f tex_coord; Common::Vec2f tex_coord;
}; };
constexpr u32 MAX_IN_FLIGHT_FRAMES = 10;
constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192; constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192;
constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(u32 width, u32 height) { constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(u32 width, u32 height) {
@@ -50,8 +51,8 @@ RendererVulkan::RendererVulkan(Core::System& system, Frontend::EmuWindow& window
Frontend::EmuWindow* secondary_window) Frontend::EmuWindow* secondary_window)
: RendererBase{system, window, secondary_window}, memory{system.Memory()}, : RendererBase{system, window, secondary_window}, memory{system.Memory()},
instance{system.TelemetrySession(), window, Settings::values.physical_device.GetValue()}, instance{system.TelemetrySession(), window, Settings::values.physical_device.GetValue()},
scheduler{instance, renderpass_cache}, renderpass_cache{instance, scheduler}, pool{instance}, scheduler{instance, renderpass_cache}, renderpass_cache{instance, scheduler},
main_window{window, instance, scheduler}, pool{instance, scheduler.GetMasterSemaphore()}, main_window{window, instance, scheduler},
vertex_buffer{instance, scheduler, vk::BufferUsageFlagBits::eVertexBuffer, vertex_buffer{instance, scheduler, vk::BufferUsageFlagBits::eVertexBuffer,
VERTEX_BUFFER_SIZE}, VERTEX_BUFFER_SIZE},
rasterizer{memory, rasterizer{memory,
@@ -77,7 +78,6 @@ RendererVulkan::~RendererVulkan() {
device.waitIdle(); device.waitIdle();
device.destroyPipelineLayout(pipeline_layout); device.destroyPipelineLayout(pipeline_layout);
device.destroyDescriptorPool(descriptor_pool);
device.destroyDescriptorSetLayout(descriptor_set_layout); device.destroyDescriptorSetLayout(descriptor_set_layout);
device.destroyDescriptorUpdateTemplate(update_template); device.destroyDescriptorUpdateTemplate(update_template);
device.destroyShaderModule(vert_shader); device.destroyShaderModule(vert_shader);
@@ -305,29 +305,7 @@ void RendererVulkan::BuildLayoutsAndDescriptors() {
pipeline_layout = device.createPipelineLayout(pipeline_layout_info); pipeline_layout = device.createPipelineLayout(pipeline_layout_info);
const u32 image_count = main_window.ImageCount(); const u32 image_count = main_window.ImageCount();
const vk::DescriptorPoolSize pool_size = { present_sets = pool.Commit(descriptor_set_layout, image_count);
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = binding.descriptorCount * image_count,
};
const vk::DescriptorPoolCreateInfo descriptor_pool_info = {
.maxSets = image_count,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
};
descriptor_pool = device.createDescriptorPool(descriptor_pool_info);
std::array<vk::DescriptorSetLayout, 3> layouts;
layouts.fill(descriptor_set_layout);
const vk::DescriptorSetAllocateInfo alloc_info = {
.descriptorPool = descriptor_pool,
.descriptorSetCount = image_count,
.pSetLayouts = layouts.data(),
};
present_sets = device.allocateDescriptorSets(alloc_info);
} }
void RendererVulkan::BuildPipelines() { void RendererVulkan::BuildPipelines() {

View File

@@ -4,18 +4,14 @@
#pragma once #pragma once
#include <array>
#include <condition_variable>
#include <mutex>
#include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_present_window.h" #include "video_core/renderer_vulkan/vk_present_window.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
namespace Core { namespace Core {
@@ -122,7 +118,6 @@ private:
std::unique_ptr<PresentWindow> second_window; std::unique_ptr<PresentWindow> second_window;
vk::PipelineLayout pipeline_layout; vk::PipelineLayout pipeline_layout;
vk::DescriptorPool descriptor_pool;
vk::DescriptorSetLayout descriptor_set_layout; vk::DescriptorSetLayout descriptor_set_layout;
vk::DescriptorUpdateTemplate update_template; vk::DescriptorUpdateTemplate update_template;
std::vector<vk::DescriptorSet> present_sets; std::vector<vk::DescriptorSet> present_sets;

View File

@@ -177,10 +177,9 @@ constexpr vk::PipelineShaderStageCreateInfo MakeStages(vk::ShaderModule compute_
} // Anonymous namespace } // Anonymous namespace
BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, DescriptorPool& pool, BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_)
RenderpassCache& renderpass_cache_) : instance{instance_}, scheduler{scheduler_}, device{instance.GetDevice()},
: instance{instance_}, scheduler{scheduler_}, renderpass_cache{renderpass_cache_}, compute_provider{instance, pool, COMPUTE_BINDINGS},
device{instance.GetDevice()}, compute_provider{instance, pool, COMPUTE_BINDINGS},
compute_buffer_provider{instance, pool, COMPUTE_BUFFER_BINDINGS}, compute_buffer_provider{instance, pool, COMPUTE_BUFFER_BINDINGS},
two_textures_provider{instance, pool, TWO_TEXTURES_BINDINGS}, two_textures_provider{instance, pool, TWO_TEXTURES_BINDINGS},
compute_pipeline_layout{ compute_pipeline_layout{
@@ -314,7 +313,6 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
const auto descriptor_set = compute_provider.Acquire(textures); const auto descriptor_set = compute_provider.Acquire(textures);
renderpass_cache.EndRendering();
scheduler.Record([this, descriptor_set, copy, src_image = source.Image(), scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
dst_image = dest.Image()](vk::CommandBuffer cmdbuf) { dst_image = dest.Image()](vk::CommandBuffer cmdbuf) {
const std::array pre_barriers = { const std::array pre_barriers = {
@@ -437,7 +435,6 @@ bool BlitHelper::DepthToBuffer(Surface& source, vk::Buffer buffer,
const auto descriptor_set = compute_buffer_provider.Acquire(textures); const auto descriptor_set = compute_buffer_provider.Acquire(textures);
renderpass_cache.EndRendering();
scheduler.Record([this, descriptor_set, copy, src_image = source.Image(), scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
extent = source.RealExtent(false)](vk::CommandBuffer cmdbuf) { extent = source.RealExtent(false)](vk::CommandBuffer cmdbuf) {
const vk::ImageMemoryBarrier pre_barrier = { const vk::ImageMemoryBarrier pre_barrier = {

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore { namespace VideoCore {
struct TextureBlit; struct TextureBlit;
@@ -23,8 +23,7 @@ class BlitHelper {
friend class TextureRuntime; friend class TextureRuntime;
public: public:
BlitHelper(const Instance& instance, Scheduler& scheduler, DescriptorPool& pool, explicit BlitHelper(const Instance& instance, Scheduler& scheduler);
RenderpassCache& renderpass_cache);
~BlitHelper(); ~BlitHelper();
bool BlitDepthStencil(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit); bool BlitDepthStencil(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
@@ -35,23 +34,16 @@ public:
const VideoCore::BufferTextureCopy& copy); const VideoCore::BufferTextureCopy& copy);
private: private:
/// Creates compute pipelines used for blit
vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout); vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout);
/// Creates graphics pipelines used for blit
vk::Pipeline MakeDepthStencilBlitPipeline(); vk::Pipeline MakeDepthStencilBlitPipeline();
private: private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassCache& renderpass_cache;
vk::Device device; vk::Device device;
vk::RenderPass r32_renderpass; vk::RenderPass r32_renderpass;
DescriptorSetProvider compute_provider;
DescriptorSetProvider compute_buffer_provider;
DescriptorSetProvider two_textures_provider;
vk::PipelineLayout compute_pipeline_layout; vk::PipelineLayout compute_pipeline_layout;
vk::PipelineLayout compute_buffer_pipeline_layout; vk::PipelineLayout compute_buffer_pipeline_layout;
vk::PipelineLayout two_textures_pipeline_layout; vk::PipelineLayout two_textures_pipeline_layout;

View File

@@ -3,10 +3,46 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "video_core/renderer_vulkan/vk_descriptor_update.h" #include "video_core/renderer_vulkan/vk_descriptor_update.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
namespace Vulkan { namespace Vulkan {
constexpr size_t NUM_MAX_DESCRIPTORS = 5;
DescriptorSetSpec::DescriptorSetSpec(const Instance& instance,
std::span<const vk::DescriptorSetLayoutBinding> bindings) {
const vk::Device device = instance.GetDevice();
std::array<vk::DescriptorUpdateTemplateEntry, NUM_MAX_DESCRIPTORS> update_entries;
for (u32 i = 0; i < bindings.size(); i++) {
update_entries[i] = vk::DescriptorUpdateTemplateEntry{
.dstBinding = bindings[i].binding,
.dstArrayElement = 0,
.descriptorCount = bindings[i].descriptorCount,
.descriptorType = bindings[i].descriptorType,
.offset = i * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
};
}
const vk::DescriptorSetLayoutCreateInfo layout_info = {
.bindingCount = static_cast<u32>(bindings.size()),
.pBindings = bindings.data(),
};
descriptor_set_layout = device.createDescriptorSetLayoutUnique(layout_info);
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
.descriptorUpdateEntryCount = static_cast<u32>(bindings.size()),
.pDescriptorUpdateEntries = update_entries.data(),
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
.descriptorSetLayout = descriptor_set_layout.get(),
};
update_template = device.createDescriptorUpdateTemplateUnique(template_info);
}
DescriptorSetSpec::~DescriptorSetSpec() = default;
DescriptorUpdateQueue::DescriptorUpdateQueue(Scheduler& scheduler_, size_t num_frames_) DescriptorUpdateQueue::DescriptorUpdateQueue(Scheduler& scheduler_, size_t num_frames_)
: scheduler{scheduler_}, num_frames{num_frames_} { : scheduler{scheduler_}, num_frames{num_frames_} {
frame_payload_size = PAYLOAD_SIZE / num_frames; frame_payload_size = PAYLOAD_SIZE / num_frames;

View File

@@ -9,6 +9,7 @@
namespace Vulkan { namespace Vulkan {
class Instance;
class Scheduler; class Scheduler;
union DescriptorUpdateEntry { union DescriptorUpdateEntry {
@@ -17,10 +18,30 @@ union DescriptorUpdateEntry {
vk::BufferView texel_buffer; vk::BufferView texel_buffer;
}; };
class DescriptorSetSpec {
public:
explicit DescriptorSetSpec(const Instance& instance,
std::span<const vk::DescriptorSetLayoutBinding> bindings);
~DescriptorSetSpec();
vk::DescriptorSetLayout Layout() const noexcept {
return descriptor_set_layout.get();
}
vk::DescriptorUpdateTemplate Template() const noexcept {
return update_template.get();
}
private:
vk::UniqueDescriptorSetLayout descriptor_set_layout;
vk::UniqueDescriptorUpdateTemplate update_template;
};
using namespace Common::Literals; using namespace Common::Literals;
class DescriptorUpdateQueue final { class DescriptorUpdateQueue final {
static constexpr size_t PAYLOAD_SIZE = 1_MiB; static constexpr size_t PAYLOAD_SIZE = 1_MiB;
public: public:
explicit DescriptorUpdateQueue(Scheduler& scheduler, size_t num_frames); explicit DescriptorUpdateQueue(Scheduler& scheduler, size_t num_frames);
~DescriptorUpdateQueue(); ~DescriptorUpdateQueue();
@@ -33,6 +54,14 @@ public:
return upload_start; return upload_start;
} }
void AddImage(vk::ImageView image_view) {
(payload_cursor++)->image = vk::DescriptorImageInfo{
.sampler = VK_NULL_HANDLE,
.imageView = image_view,
.imageLayout = vk::ImageLayout::eGeneral,
};
}
void AddSampledImage(vk::ImageView image_view, vk::Sampler sampler) { void AddSampledImage(vk::ImageView image_view, vk::Sampler sampler) {
(payload_cursor++)->image = vk::DescriptorImageInfo{ (payload_cursor++)->image = vk::DescriptorImageInfo{
.sampler = sampler, .sampler = sampler,

View File

@@ -272,10 +272,10 @@ bool GraphicsPipeline::Build(bool fail_on_compile_required) {
pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT; pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT;
} }
auto result = device.createGraphicsPipelineUnique(pipeline_cache, pipeline_info); auto [result, handle] = device.createGraphicsPipelineUnique(pipeline_cache, pipeline_info);
if (result.result == vk::Result::eSuccess) { if (result == vk::Result::eSuccess) {
pipeline = std::move(result.value); pipeline = std::move(handle);
} else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) { } else if (result == vk::Result::eErrorPipelineCompileRequiredEXT) {
return false; return false;
} else { } else {
UNREACHABLE_MSG("Graphics pipeline creation failed!"); UNREACHABLE_MSG("Graphics pipeline creation failed!");

View File

@@ -42,22 +42,18 @@ namespace Vulkan {
class Instance; class Instance;
class RenderpassCache; class RenderpassCache;
constexpr u32 MAX_SHADER_STAGES = 3; constexpr size_t MAX_SHADER_STAGES = 3;
constexpr u32 MAX_VERTEX_ATTRIBUTES = 16; constexpr size_t MAX_VERTEX_ATTRIBUTES = 16;
constexpr u32 MAX_VERTEX_BINDINGS = 13; constexpr size_t MAX_VERTEX_BINDINGS = 13;
/**
* The pipeline state is tightly packed with bitfields to reduce
* the overhead of hashing as much as possible
*/
union RasterizationState { union RasterizationState {
u8 value = 0; u32 raw;
BitField<0, 2, Pica::PipelineRegs::TriangleTopology> topology; BitField<0, 2, Pica::PipelineRegs::TriangleTopology> topology;
BitField<4, 2, Pica::RasterizerRegs::CullMode> cull_mode; BitField<4, 2, Pica::RasterizerRegs::CullMode> cull_mode;
}; };
union DepthStencilState { union DepthStencilState {
u32 value = 0; u32 raw;
BitField<0, 1, u32> depth_test_enable; BitField<0, 1, u32> depth_test_enable;
BitField<1, 1, u32> depth_write_enable; BitField<1, 1, u32> depth_write_enable;
BitField<2, 1, u32> stencil_test_enable; BitField<2, 1, u32> stencil_test_enable;
@@ -73,7 +69,7 @@ struct BlendingState {
u16 color_write_mask; u16 color_write_mask;
Pica::FramebufferRegs::LogicOp logic_op; Pica::FramebufferRegs::LogicOp logic_op;
union { union {
u32 value = 0; u32 raw;
BitField<0, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor; BitField<0, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor;
BitField<4, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor; BitField<4, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor;
BitField<8, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq; BitField<8, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq;
@@ -84,10 +80,11 @@ struct BlendingState {
}; };
struct DynamicState { struct DynamicState {
u32 blend_color = 0; u32 blend_color;
u8 stencil_reference; u8 stencil_reference;
u8 stencil_compare_mask; u8 stencil_compare_mask;
u8 stencil_write_mask; u8 stencil_write_mask;
INSERT_PADDING_BYTES(1);
Common::Rectangle<u32> scissor; Common::Rectangle<u32> scissor;
Common::Rectangle<s32> viewport; Common::Rectangle<s32> viewport;
@@ -129,12 +126,19 @@ struct AttachmentInfo {
* Information about a graphics/compute pipeline * Information about a graphics/compute pipeline
*/ */
struct PipelineInfo { struct PipelineInfo {
BlendingState blending; BlendingState blending{};
AttachmentInfo attachments; AttachmentInfo attachments{};
RasterizationState rasterization; RasterizationState rasterization{};
DepthStencilState depth_stencil; DepthStencilState depth_stencil{};
DynamicState dynamic; DynamicState dynamic{};
VertexLayout vertex_layout; VertexLayout vertex_layout{};
enum Type : u32 {
Normal,
ShadowPlane,
ShadowCube,
};
Type type{Type::Normal};
[[nodiscard]] u64 Hash(const Instance& instance) const; [[nodiscard]] u64 Hash(const Instance& instance) const;
@@ -148,6 +152,7 @@ struct PipelineInfo {
return depth_write || stencil_write; return depth_write || stencil_write;
} }
}; };
static_assert(std::has_unique_object_representations_v<PipelineInfo>);
struct Shader : public Common::AsyncHandle { struct Shader : public Common::AsyncHandle {
explicit Shader(const Instance& instance); explicit Shader(const Instance& instance);
@@ -176,7 +181,7 @@ public:
bool Build(bool fail_on_compile_required = false); bool Build(bool fail_on_compile_required = false);
[[nodiscard]] vk::Pipeline Handle() const noexcept { [[nodiscard]] vk::Pipeline Handle() const noexcept {
return *pipeline; return pipeline.get();
} }
private: private:

View File

@@ -8,6 +8,7 @@
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "video_core/renderer_vulkan/pica_to_vk.h" #include "video_core/renderer_vulkan/pica_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
@@ -26,6 +27,12 @@ MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32))
namespace Vulkan { namespace Vulkan {
enum DescriptorSet {
Buffer,
Texture,
Utility,
};
u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) { u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) {
switch (format) { switch (format) {
case Pica::PipelineRegs::VertexAttributeFormat::FLOAT: case Pica::PipelineRegs::VertexAttributeFormat::FLOAT:
@@ -61,31 +68,33 @@ constexpr std::array<vk::DescriptorSetLayoutBinding, 6> BUFFER_BINDINGS = {{
{5, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, {5, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment},
}}; }};
template <vk::DescriptorType tex0_type, u32 num_faces>
constexpr std::array<vk::DescriptorSetLayoutBinding, 3> TEXTURE_BINDINGS = {{ constexpr std::array<vk::DescriptorSetLayoutBinding, 3> TEXTURE_BINDINGS = {{
{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, {0, tex0_type, num_faces, vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, {2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
}}; }};
// TODO: Use descriptor array for shadow cube constexpr std::array<vk::DescriptorSetLayoutBinding, 1> UTILITY_BINDINGS = {{
constexpr std::array<vk::DescriptorSetLayoutBinding, 7> SHADOW_BINDINGS = {{
{0, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment}, {0, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{2, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{3, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{4, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{5, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
{6, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment},
}}; }};
PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
RenderpassCache& renderpass_cache_, DescriptorPool& pool_) RenderpassCache& renderpass_cache_, DescriptorPool& persistent_pool)
: instance{instance_}, scheduler{scheduler_}, renderpass_cache{renderpass_cache_}, pool{pool_}, : instance{instance_}, scheduler{scheduler_},
renderpass_cache{renderpass_cache_}, pool{instance, scheduler.GetMasterSemaphore()},
num_worker_threads{std::max(std::thread::hardware_concurrency(), 2U)}, num_worker_threads{std::max(std::thread::hardware_concurrency(), 2U)},
workers{num_worker_threads, "Pipeline workers"}, workers{num_worker_threads, "Pipeline workers"}, buffer_set_spec{instance, BUFFER_BINDINGS},
utility_set_spec{instance, UTILITY_BINDINGS},
texture_set_specs{
DescriptorSetSpec{instance,
TEXTURE_BINDINGS<vk::DescriptorType::eCombinedImageSampler, 1>},
DescriptorSetSpec{instance, TEXTURE_BINDINGS<vk::DescriptorType::eStorageImage, 1>},
DescriptorSetSpec{instance, TEXTURE_BINDINGS<vk::DescriptorType::eStorageImage, 6>}},
trivial_vertex_shader{ trivial_vertex_shader{
instance, vk::ShaderStageFlagBits::eVertex, instance, vk::ShaderStageFlagBits::eVertex,
GLSL::GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported(), true)} { GLSL::GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported(), true)} {
// Create profile for driver assisted shader features.
profile = Pica::Shader::Profile{ profile = Pica::Shader::Profile{
.has_separable_shaders = true, .has_separable_shaders = true,
.has_clip_planes = instance.IsShaderClipDistanceSupported(), .has_clip_planes = instance.IsShaderClipDistanceSupported(),
@@ -98,14 +107,10 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
.has_logic_op = !instance.NeedsLogicOpEmulation(), .has_logic_op = !instance.NeedsLogicOpEmulation(),
.is_vulkan = true, .is_vulkan = true,
}; };
BuildLayout();
}
void PipelineCache::BuildLayout() {
std::array<vk::DescriptorSetLayout, NUM_RASTERIZER_SETS> descriptor_set_layouts; std::array<vk::DescriptorSetLayout, NUM_RASTERIZER_SETS> descriptor_set_layouts;
std::transform(descriptor_set_providers.begin(), descriptor_set_providers.end(), descriptor_set_layouts[DescriptorSet::Buffer] = buffer_set_spec.Layout();
descriptor_set_layouts.begin(), descriptor_set_layouts[DescriptorSet::Utility] = utility_set_spec.Layout();
[](const auto& provider) { return provider.Layout(); });
const vk::PipelineLayoutCreateInfo layout_info = { const vk::PipelineLayoutCreateInfo layout_info = {
.setLayoutCount = NUM_RASTERIZER_SETS, .setLayoutCount = NUM_RASTERIZER_SETS,
@@ -113,7 +118,15 @@ void PipelineCache::BuildLayout() {
.pushConstantRangeCount = 0, .pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr, .pPushConstantRanges = nullptr,
}; };
pipeline_layout = instance.GetDevice().createPipelineLayoutUnique(layout_info);
// Create rasterizer pipeline layouts.
for (size_t i = 0; i < NUM_PIPELINE_CONFIGS; i++) {
descriptor_set_layouts[DescriptorSet::Texture] = texture_set_specs[i].Layout();
pipeline_layouts[i] = instance.GetDevice().createPipelineLayoutUnique(layout_info);
}
// Allocate buffer descriptor set from the persistent pool
bound_descriptor_sets[DescriptorSet::Buffer] = persistent_pool.Commit(buffer_set_spec.Layout());
} }
PipelineCache::~PipelineCache() { PipelineCache::~PipelineCache() {
@@ -125,34 +138,40 @@ void PipelineCache::LoadDiskCache() {
return; return;
} }
const std::string cache_file_path = fmt::format("{}{:x}{:x}.bin", GetPipelineCacheDir(), const auto cache_dir = GetPipelineCacheDir();
instance.GetVendorID(), instance.GetDeviceID()); const u32 vendor_id = instance.GetVendorID();
vk::PipelineCacheCreateInfo cache_info = { const u32 device_id = instance.GetDeviceID();
.initialDataSize = 0, const auto cache_file_path = fmt::format("{}{:x}{:x}.bin", cache_dir, vendor_id, device_id);
.pInitialData = nullptr,
};
vk::PipelineCacheCreateInfo cache_info{};
std::vector<u8> cache_data; std::vector<u8> cache_data;
FileUtil::IOFile cache_file{cache_file_path, "r"};
if (cache_file.IsOpen()) {
LOG_INFO(Render_Vulkan, "Loading pipeline cache");
const u64 cache_file_size = cache_file.GetSize(); SCOPE_EXIT({
cache_data.resize(cache_file_size); const vk::Device device = instance.GetDevice();
if (cache_file.ReadBytes(cache_data.data(), cache_file_size)) { pipeline_cache = device.createPipelineCacheUnique(cache_info);
if (!IsCacheValid(cache_data)) { });
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring");
} else {
cache_info.initialDataSize = cache_file_size;
cache_info.pInitialData = cache_data.data();
}
}
cache_file.Close(); FileUtil::IOFile cache_file{cache_file_path, "rb"};
if (!cache_file.IsOpen()) {
LOG_INFO(Render_Vulkan, "No pipeline cache found for device");
return;
} }
vk::Device device = instance.GetDevice(); const u64 cache_file_size = cache_file.GetSize();
pipeline_cache = device.createPipelineCacheUnique(cache_info); cache_data.resize(cache_file_size);
if (cache_file.ReadBytes(cache_data.data(), cache_file_size) != cache_file_size) {
LOG_ERROR(Render_Vulkan, "Error during pipeline cache read, removing");
FileUtil::Delete(cache_file_path);
return;
}
if (!IsCacheValid(cache_data)) {
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring");
}
LOG_INFO(Render_Vulkan, "Loading pipeline cache with size {} KB", cache_file_size / 1024);
cache_info.initialDataSize = cache_file_size;
cache_info.pInitialData = cache_data.data();
} }
void PipelineCache::SaveDiskCache() { void PipelineCache::SaveDiskCache() {
@@ -160,22 +179,23 @@ void PipelineCache::SaveDiskCache() {
return; return;
} }
const std::string cache_file_path = fmt::format("{}{:x}{:x}.bin", GetPipelineCacheDir(), const auto cache_dir = GetPipelineCacheDir();
instance.GetVendorID(), instance.GetDeviceID()); const u32 vendor_id = instance.GetVendorID();
const u32 device_id = instance.GetDeviceID();
const auto cache_file_path = fmt::format("{}{:x}{:x}.bin", cache_dir, vendor_id, device_id);
FileUtil::IOFile cache_file{cache_file_path, "wb"}; FileUtil::IOFile cache_file{cache_file_path, "wb"};
if (!cache_file.IsOpen()) { if (!cache_file.IsOpen()) {
LOG_ERROR(Render_Vulkan, "Unable to open pipeline cache for writing"); LOG_ERROR(Render_Vulkan, "Unable to open pipeline cache for writing");
return; return;
} }
vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
auto cache_data = device.getPipelineCacheData(*pipeline_cache); const auto cache_data = device.getPipelineCacheData(*pipeline_cache);
if (!cache_file.WriteBytes(cache_data.data(), cache_data.size())) { if (cache_file.WriteBytes(cache_data.data(), cache_data.size()) != cache_data.size()) {
LOG_ERROR(Render_Vulkan, "Error during pipeline cache write"); LOG_ERROR(Render_Vulkan, "Error during pipeline cache write, removing");
return; FileUtil::Delete(cache_file_path);
} }
cache_file.Close();
} }
bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
@@ -191,12 +211,14 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
auto [it, new_pipeline] = graphics_pipelines.try_emplace(pipeline_hash); auto [it, new_pipeline] = graphics_pipelines.try_emplace(pipeline_hash);
if (new_pipeline) { if (new_pipeline) {
it.value() = const auto pipeline_layout = pipeline_layouts[info.type].get();
std::make_unique<GraphicsPipeline>(instance, renderpass_cache, info, *pipeline_cache, auto pipeline = std::make_unique<GraphicsPipeline>(instance, renderpass_cache, info,
*pipeline_layout, current_shaders, &workers); pipeline_cache.get(), pipeline_layout,
current_shaders, &workers);
it.value() = std::move(pipeline);
} }
GraphicsPipeline* const pipeline{it->second.get()}; GraphicsPipeline* const pipeline = it->second.get();
if (!pipeline->IsDone() && !pipeline->TryBuild(wait_built)) { if (!pipeline->IsDone() && !pipeline->TryBuild(wait_built)) {
return false; return false;
} }
@@ -399,7 +421,7 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
} }
auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance); auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance);
auto& shader = iter->second; auto& shader = iter.value();
if (new_program) { if (new_program) {
shader.program = std::move(program); shader.program = std::move(program);
@@ -410,7 +432,7 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
}); });
} }
it->second = &shader; it.value() = &shader;
} }
Shader* const shader{it->second}; Shader* const shader{it->second};
@@ -438,7 +460,7 @@ bool PipelineCache::UseFixedGeometryShader(const Pica::Regs& regs) {
const PicaFixedGSConfig gs_config{regs, instance.IsShaderClipDistanceSupported()}; const PicaFixedGSConfig gs_config{regs, instance.IsShaderClipDistanceSupported()};
auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance); auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance);
auto& shader = it->second; auto& shader = it.value();
if (new_shader) { if (new_shader) {
workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() { workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() {
@@ -463,7 +485,7 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs,
const Pica::Shader::UserConfig& user) { const Pica::Shader::UserConfig& user) {
const FSConfig fs_config{regs, user, profile}; const FSConfig fs_config{regs, user, profile};
const auto [it, new_shader] = fragment_shaders.try_emplace(fs_config, instance); const auto [it, new_shader] = fragment_shaders.try_emplace(fs_config, instance);
auto& shader = it->second; auto& shader = it.value();
if (new_shader) { if (new_shader) {
const bool use_spirv = Settings::values.spirv_shader_gen.GetValue(); const bool use_spirv = Settings::values.spirv_shader_gen.GetValue();
@@ -485,52 +507,6 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs,
shader_hashes[ProgramType::FS] = fs_config.Hash(); shader_hashes[ProgramType::FS] = fs_config.Hash();
} }
void PipelineCache::BindTexture(u32 binding, vk::ImageView image_view, vk::Sampler sampler) {
auto& info = update_data[1][binding].image_info;
if (info.imageView == image_view && info.sampler == sampler) {
return;
}
set_dirty[1] = true;
info = vk::DescriptorImageInfo{
.sampler = sampler,
.imageView = image_view,
.imageLayout = vk::ImageLayout::eGeneral,
};
}
void PipelineCache::BindStorageImage(u32 binding, vk::ImageView image_view) {
auto& info = update_data[2][binding].image_info;
if (info.imageView == image_view) {
return;
}
set_dirty[2] = true;
info = vk::DescriptorImageInfo{
.imageView = image_view,
.imageLayout = vk::ImageLayout::eGeneral,
};
}
void PipelineCache::BindBuffer(u32 binding, vk::Buffer buffer, u32 offset, u32 size) {
auto& info = update_data[0][binding].buffer_info;
if (info.buffer == buffer && info.offset == offset && info.range == size) {
return;
}
set_dirty[0] = true;
info = vk::DescriptorBufferInfo{
.buffer = buffer,
.offset = offset,
.range = size,
};
}
void PipelineCache::BindTexelBuffer(u32 binding, vk::BufferView buffer_view) {
auto& view = update_data[0][binding].buffer_view;
if (view != buffer_view) {
set_dirty[0] = true;
view = buffer_view;
}
}
void PipelineCache::SetBufferOffset(u32 binding, size_t offset) { void PipelineCache::SetBufferOffset(u32 binding, size_t offset) {
if (offsets[binding] != static_cast<u32>(offset)) { if (offsets[binding] != static_cast<u32>(offset)) {
offsets[binding] = static_cast<u32>(offset); offsets[binding] = static_cast<u32>(offset);

View File

@@ -8,7 +8,9 @@
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include "common/thread_worker.h" #include "common/thread_worker.h"
#include "video_core/renderer_vulkan/vk_descriptor_update.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/shader/generator/pica_fs_config.h" #include "video_core/shader/generator/pica_fs_config.h"
#include "video_core/shader/generator/profile.h" #include "video_core/shader/generator/profile.h"
#include "video_core/shader/generator/shader_gen.h" #include "video_core/shader/generator/shader_gen.h"
@@ -28,13 +30,20 @@ class Scheduler;
class RenderpassCache; class RenderpassCache;
class DescriptorPool; class DescriptorPool;
constexpr u32 NUM_RASTERIZER_SETS = 3; enum class PipelineConfig {
constexpr u32 NUM_DYNAMIC_OFFSETS = 3; Normal,
ShadowPlane,
ShadowCube,
};
/** /**
* Stores a collection of rasterizer pipelines used during rendering. * Stores a collection of rasterizer pipelines used during rendering.
*/ */
class PipelineCache { class PipelineCache {
static constexpr size_t NUM_RASTERIZER_SETS = 3;
static constexpr size_t NUM_PIPELINE_CONFIGS = 3;
static constexpr size_t NUM_DYNAMIC_OFFSETS = 3;
public: public:
explicit PipelineCache(const Instance& instance, Scheduler& scheduler, explicit PipelineCache(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorPool& pool); RenderpassCache& renderpass_cache, DescriptorPool& pool);
@@ -65,25 +74,10 @@ public:
/// Binds a fragment shader generated from PICA state /// Binds a fragment shader generated from PICA state
void UseFragmentShader(const Pica::Regs& regs, const Pica::Shader::UserConfig& user); void UseFragmentShader(const Pica::Regs& regs, const Pica::Shader::UserConfig& user);
/// Binds a texture to the specified binding
void BindTexture(u32 binding, vk::ImageView image_view, vk::Sampler sampler);
/// Binds a storage image to the specified binding
void BindStorageImage(u32 binding, vk::ImageView image_view);
/// Binds a buffer to the specified binding
void BindBuffer(u32 binding, vk::Buffer buffer, u32 offset, u32 size);
/// Binds a buffer to the specified binding
void BindTexelBuffer(u32 binding, vk::BufferView buffer_view);
/// Sets the dynamic offset for the uniform buffer at binding /// Sets the dynamic offset for the uniform buffer at binding
void SetBufferOffset(u32 binding, size_t offset); void BindBufferRange(u32 binding, size_t offset);
private: private:
/// Builds the rasterizer pipeline layout
void BuildLayout();
/// Returns true when the disk data can be used by the current driver /// Returns true when the disk data can be used by the current driver
bool IsCacheValid(std::span<const u8> cache_data) const; bool IsCacheValid(std::span<const u8> cache_data) const;
@@ -97,11 +91,10 @@ private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassCache& renderpass_cache; RenderpassCache& renderpass_cache;
DescriptorPool& pool; DescriptorPool pool;
Pica::Shader::Profile profile{}; Pica::Shader::Profile profile{};
vk::UniquePipelineCache pipeline_cache; vk::UniquePipelineCache pipeline_cache;
vk::UniquePipelineLayout pipeline_layout;
std::size_t num_worker_threads; std::size_t num_worker_threads;
Common::ThreadWorker workers; Common::ThreadWorker workers;
PipelineInfo current_info{}; PipelineInfo current_info{};
@@ -109,8 +102,13 @@ private:
tsl::robin_map<u64, std::unique_ptr<GraphicsPipeline>, Common::IdentityHash<u64>> tsl::robin_map<u64, std::unique_ptr<GraphicsPipeline>, Common::IdentityHash<u64>>
graphics_pipelines; graphics_pipelines;
DescriptorSetSpec buffer_set_spec;
DescriptorSetSpec utility_set_spec;
std::array<DescriptorSetSpec, NUM_PIPELINE_CONFIGS> texture_set_specs;
std::array<vk::UniquePipelineLayout, NUM_PIPELINE_CONFIGS> pipeline_layouts;
std::array<vk::DescriptorSet, NUM_RASTERIZER_SETS> bound_descriptor_sets;
std::array<u32, NUM_DYNAMIC_OFFSETS> offsets{}; std::array<u32, NUM_DYNAMIC_OFFSETS> offsets{};
std::array<vk::DescriptorSet, NUM_RASTERIZER_SETS> bound_descriptor_sets{};
std::bitset<NUM_RASTERIZER_SETS> set_dirty{}; std::bitset<NUM_RASTERIZER_SETS> set_dirty{};
std::array<u64, MAX_SHADER_STAGES> shader_hashes; std::array<u64, MAX_SHADER_STAGES> shader_hashes;

View File

@@ -106,8 +106,7 @@ PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& i
vsync_enabled{Settings::values.use_vsync_new.GetValue()}, vsync_enabled{Settings::values.use_vsync_new.GetValue()},
blit_supported{ blit_supported{
CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)}, CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)},
use_present_thread{Settings::values.async_presentation.GetValue()}, use_present_thread{Settings::values.async_presentation.GetValue()} {
last_render_surface{emu_window.GetWindowInfo().render_surface} {
const u32 num_images = swapchain.GetImageCount(); const u32 num_images = swapchain.GetImageCount();
const vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
@@ -156,7 +155,7 @@ PresentWindow::~PresentWindow() {
} }
void PresentWindow::RecreateFrame(Frame* frame, u32 width, u32 height) { void PresentWindow::RecreateFrame(Frame* frame, u32 width, u32 height) {
vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
if (frame->framebuffer) { if (frame->framebuffer) {
device.destroyFramebuffer(frame->framebuffer); device.destroyFramebuffer(frame->framebuffer);
} }
@@ -237,7 +236,7 @@ Frame* PresentWindow::GetRenderFrame() {
Frame* frame = free_queue.front(); Frame* frame = free_queue.front();
free_queue.pop(); free_queue.pop();
vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
vk::Result result{}; vk::Result result{};
const auto wait = [&]() { const auto wait = [&]() {
@@ -453,7 +452,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
const vk::Semaphore image_acquired = swapchain.GetImageAcquiredSemaphore(); const vk::Semaphore image_acquired = swapchain.GetImageAcquiredSemaphore();
const std::array wait_semaphores = {image_acquired, frame->render_ready}; const std::array wait_semaphores = {image_acquired, frame->render_ready};
vk::SubmitInfo submit_info = { const vk::SubmitInfo submit_info = {
.waitSemaphoreCount = static_cast<u32>(wait_semaphores.size()), .waitSemaphoreCount = static_cast<u32>(wait_semaphores.size()),
.pWaitSemaphores = wait_semaphores.data(), .pWaitSemaphores = wait_semaphores.data(),
.pWaitDstStageMask = wait_stage_masks.data(), .pWaitDstStageMask = wait_stage_masks.data(),
@@ -468,8 +467,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
try { try {
graphics_queue.submit(submit_info, frame->present_done); graphics_queue.submit(submit_info, frame->present_done);
} catch (vk::DeviceLostError& err) { } catch (vk::DeviceLostError& err) {
LOG_CRITICAL(Render_Vulkan, "Device lost during present submit: {}", err.what()); UNREACHABLE_MSG("Device lost during present submit: {}", err.what());
UNREACHABLE();
} }
swapchain.Present(); swapchain.Present();

View File

@@ -65,7 +65,9 @@ public:
private: private:
void PresentThread(std::stop_token token); void PresentThread(std::stop_token token);
void CopyToSwapchain(Frame* frame); void CopyToSwapchain(Frame* frame);
vk::RenderPass CreateRenderpass(); vk::RenderPass CreateRenderpass();
private: private:
@@ -92,7 +94,6 @@ private:
bool vsync_enabled{}; bool vsync_enabled{};
bool blit_supported; bool blit_supported;
bool use_present_thread{true}; bool use_present_thread{true};
void* last_render_surface{};
}; };
} // namespace Vulkan } // namespace Vulkan

View File

@@ -89,6 +89,7 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
MakeSoftwareVertexLayout(); MakeSoftwareVertexLayout();
pipeline_info.vertex_layout = software_layout; pipeline_info.vertex_layout = software_layout;
// Create texture buffer views for the lighting LUTs.
const vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
texture_lf_view = device.createBufferViewUnique({ texture_lf_view = device.createBufferViewUnique({
.buffer = texture_lf_buffer.Handle(), .buffer = texture_lf_buffer.Handle(),
@@ -109,24 +110,14 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
.range = VK_WHOLE_SIZE, .range = VK_WHOLE_SIZE,
}); });
// Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize // Update persistent buffer descriptor set with our rasterizer buffers.
// all descriptor sets even the ones we don't use. update_queue.Acquire();
pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData)); update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(VSUniformData)); update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
pipeline_cache.BindBuffer(2, uniform_buffer.Handle(), 0, sizeof(FSUniformData)); update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSUniformData));
pipeline_cache.BindTexelBuffer(3, *texture_lf_view); update_queue.AddTexelBuffer(texture_lf_view.get());
pipeline_cache.BindTexelBuffer(4, *texture_rg_view); update_queue.AddTexelBuffer(texture_rg_view.get());
pipeline_cache.BindTexelBuffer(5, *texture_rgba_view); update_queue.AddTexelBuffer(texture_rgba_view.get());
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
for (u32 i = 0; i < 3; i++) {
pipeline_cache.BindTexture(i, null_surface.ImageView(), null_sampler.Handle());
}
for (u32 i = 0; i < 7; i++) {
pipeline_cache.BindStorageImage(i, null_surface.StorageView());
}
SyncEntireState(); SyncEntireState();
} }
@@ -476,16 +467,10 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
return true; return true;
} }
// Update attachment formats
pipeline_info.attachments.color = framebuffer->Format(SurfaceType::Color); pipeline_info.attachments.color = framebuffer->Format(SurfaceType::Color);
pipeline_info.attachments.depth = framebuffer->Format(SurfaceType::Depth); pipeline_info.attachments.depth = framebuffer->Format(SurfaceType::Depth);
if (shadow_rendering) {
pipeline_cache.BindStorageImage(6, framebuffer->ImageView(SurfaceType::Color));
} else {
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
pipeline_cache.BindStorageImage(6, null_surface.StorageView());
}
// Update scissor uniforms // Update scissor uniforms
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 || if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 ||
@@ -503,6 +488,11 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// Sync and bind the texture surfaces // Sync and bind the texture surfaces
SyncTextureUnits(framebuffer); SyncTextureUnits(framebuffer);
// Attach the framebuffer as storage image during shadow rendering.
if (shadow_rendering) {
update_queue.AddImage(framebuffer->ImageView(SurfaceType::Color));
}
// Sync and bind the shader // Sync and bind the shader
if (shader_dirty) { if (shader_dirty) {
pipeline_cache.UseFragmentShader(regs, user_config); pipeline_cache.UseFragmentShader(regs, user_config);
@@ -555,7 +545,17 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) { void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
// Check if the PICA texture configuration changed.
const auto pica_textures = regs.texturing.GetTextures(); const auto pica_textures = regs.texturing.GetTextures();
if (pica_textures == textures && textures[0].config.type != TextureType::TextureCube) {
return;
}
// Reserve space in the queue for incoming texture data.
// We will write the image data in the same order as defined
// in the rasterizer descriptor texture sets.
update_queue.Acquire();
for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index]; const auto& texture = pica_textures[texture_index];
@@ -563,8 +563,7 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
if (!texture.enabled) { if (!texture.enabled) {
const Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID); const Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID); const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
pipeline_cache.BindTexture(texture_index, null_surface.ImageView(), update_queue.AddSampledImage(null_surface.ImageView(), null_sampler.Handle());
null_sampler.Handle());
continue; continue;
} }
@@ -574,7 +573,7 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
case TextureType::Shadow2D: { case TextureType::Shadow2D: {
Surface& surface = res_cache.GetTextureSurface(texture); Surface& surface = res_cache.GetTextureSurface(texture);
surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap; surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap;
pipeline_cache.BindStorageImage(0, surface.StorageView()); update_queue.AddImage(surface.StorageView());
continue; continue;
} }
case TextureType::ShadowCube: { case TextureType::ShadowCube: {
@@ -586,7 +585,6 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
continue; continue;
} }
default: default:
UnbindSpecial();
break; break;
} }
} }
@@ -594,10 +592,12 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
// Bind the texture provided by the rasterizer cache // Bind the texture provided by the rasterizer cache
Surface& surface = res_cache.GetTextureSurface(texture); Surface& surface = res_cache.GetTextureSurface(texture);
Sampler& sampler = res_cache.GetSampler(texture.config); Sampler& sampler = res_cache.GetSampler(texture.config);
if (!IsFeedbackLoop(texture_index, framebuffer, surface, sampler)) { if (!IsFeedbackLoop(framebuffer, surface, sampler)) {
pipeline_cache.BindTexture(texture_index, surface.ImageView(), sampler.Handle()); update_queue.AddSampledImage(surface.ImageView(), sampler.Handle());
} }
} }
textures = pica_textures;
} }
void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) { void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
@@ -609,13 +609,11 @@ void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConf
}; };
for (CubeFace face : faces) { for (CubeFace face : faces) {
const u32 binding = static_cast<u32>(face);
info.physical_address = regs.texturing.GetCubePhysicalAddress(face); info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
const auto surface_id = res_cache.GetTextureSurface(info);
const VideoCore::SurfaceId surface_id = res_cache.GetTextureSurface(info);
Surface& surface = res_cache.GetSurface(surface_id); Surface& surface = res_cache.GetSurface(surface_id);
surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap; surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap;
pipeline_cache.BindStorageImage(binding, surface.StorageView()); update_queue.AddImage(surface.StorageView());
} }
} }
@@ -633,13 +631,13 @@ void RasterizerVulkan::BindTextureCube(const Pica::TexturingRegs::FullTextureCon
.format = texture.format, .format = texture.format,
}; };
Surface& surface = res_cache.GetTextureCube(config); const Surface& surface = res_cache.GetTextureCube(config);
Sampler& sampler = res_cache.GetSampler(texture.config); const Sampler& sampler = res_cache.GetSampler(texture.config);
pipeline_cache.BindTexture(0, surface.ImageView(), sampler.Handle()); update_queue.AddSampledImage(surface.ImageView(), sampler.Handle());
} }
bool RasterizerVulkan::IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer, bool RasterizerVulkan::IsFeedbackLoop(const Framebuffer* framebuffer, Surface& surface,
Surface& surface, Sampler& sampler) { Sampler& sampler) {
const vk::ImageView color_view = framebuffer->ImageView(SurfaceType::Color); const vk::ImageView color_view = framebuffer->ImageView(SurfaceType::Color);
const bool is_feedback_loop = color_view == surface.ImageView(); const bool is_feedback_loop = color_view == surface.ImageView();
if (!is_feedback_loop) { if (!is_feedback_loop) {
@@ -647,17 +645,10 @@ bool RasterizerVulkan::IsFeedbackLoop(u32 texture_index, const Framebuffer* fram
} }
// Make a temporary copy of the framebuffer to sample from // Make a temporary copy of the framebuffer to sample from
pipeline_cache.BindTexture(texture_index, surface.CopyImageView(), sampler.Handle()); update_queue.AddSampledImage(surface.CopyImageView(), sampler.Handle());
return true; return true;
} }
void RasterizerVulkan::UnbindSpecial() {
Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
for (u32 i = 0; i < 6; i++) {
pipeline_cache.BindStorageImage(i, null_surface.StorageView());
}
}
void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) { void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) {
switch (id) { switch (id) {
// Culling // Culling
@@ -1098,24 +1089,24 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
auto [uniforms, offset, invalidate] = auto [uniforms, offset, invalidate] =
uniform_buffer.Map(uniform_size, uniform_buffer_alignment); uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
u32 used_bytes = 0; size_t used_bytes = 0;
if (sync_vs || invalidate) { if (sync_vs || invalidate) {
std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data, std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data,
sizeof(vs_uniform_block_data.data)); sizeof(vs_uniform_block_data.data));
pipeline_cache.SetBufferOffset(1, offset + used_bytes); pipeline_cache.BindBufferRange(1, offset + used_bytes);
vs_uniform_block_data.dirty = false; vs_uniform_block_data.dirty = false;
used_bytes += static_cast<u32>(uniform_size_aligned_vs); used_bytes += uniform_size_aligned_vs;
} }
if (sync_fs || invalidate) { if (sync_fs || invalidate) {
std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data, std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data,
sizeof(fs_uniform_block_data.data)); sizeof(fs_uniform_block_data.data));
pipeline_cache.SetBufferOffset(2, offset + used_bytes); pipeline_cache.BindBufferRange(2, offset + used_bytes);
fs_uniform_block_data.dirty = false; fs_uniform_block_data.dirty = false;
used_bytes += static_cast<u32>(uniform_size_aligned_fs); used_bytes += uniform_size_aligned_fs;
} }
if (sync_vs_pica) { if (sync_vs_pica) {
@@ -1123,8 +1114,8 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
pipeline_cache.SetBufferOffset(0, offset + used_bytes); pipeline_cache.BindBufferRange(0, offset + used_bytes);
used_bytes += static_cast<u32>(uniform_size_aligned_vs_pica); used_bytes += uniform_size_aligned_vs_pica;
} }
uniform_buffer.Commit(used_bytes); uniform_buffer.Commit(used_bytes);

View File

@@ -6,6 +6,7 @@
#include "core/hw/gpu.h" #include "core/hw/gpu.h"
#include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_accelerated.h"
#include "video_core/renderer_vulkan/vk_descriptor_update.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_stream_buffer.h" #include "video_core/renderer_vulkan/vk_stream_buffer.h"
@@ -104,11 +105,7 @@ private:
void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture); void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture);
/// Makes a temporary copy of the framebuffer if a feedback loop is detected /// Makes a temporary copy of the framebuffer if a feedback loop is detected
bool IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer, Surface& surface, bool IsFeedbackLoop(const Framebuffer* framebuffer, Surface& surface, Sampler& sampler);
Sampler& sampler);
/// Unbinds all special texture unit 0 texture configurations
void UnbindSpecial();
/// Upload the uniform blocks to the uniform buffer object /// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw); void UploadUniforms(bool accelerate_draw);
@@ -144,6 +141,7 @@ private:
PipelineCache pipeline_cache; PipelineCache pipeline_cache;
TextureRuntime runtime; TextureRuntime runtime;
RasterizerCache res_cache; RasterizerCache res_cache;
DescriptorUpdateQueue update_queue;
VertexLayout software_layout; VertexLayout software_layout;
std::array<u32, 16> binding_offsets{}; std::array<u32, 16> binding_offsets{};
@@ -159,6 +157,7 @@ private:
vk::UniqueBufferView texture_lf_view; vk::UniqueBufferView texture_lf_view;
vk::UniqueBufferView texture_rg_view; vk::UniqueBufferView texture_rg_view;
vk::UniqueBufferView texture_rgba_view; vk::UniqueBufferView texture_rgba_view;
Pica::TexturingRegs::Textures textures{};
u64 uniform_buffer_alignment; u64 uniform_buffer_alignment;
u64 uniform_size_aligned_vs_pica; u64 uniform_size_aligned_vs_pica;
u64 uniform_size_aligned_vs; u64 uniform_size_aligned_vs;

View File

@@ -11,7 +11,6 @@
#include "video_core/rasterizer_cache/texture_codec.h" #include "video_core/rasterizer_cache/texture_codec.h"
#include "video_core/rasterizer_cache/utils.h" #include "video_core/rasterizer_cache/utils.h"
#include "video_core/renderer_vulkan/pica_to_vk.h" #include "video_core/renderer_vulkan/pica_to_vk.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -253,9 +252,9 @@ constexpr u64 DOWNLOAD_BUFFER_SIZE = 16_MiB;
TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler, TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorPool& pool, RenderpassCache& renderpass_cache, DescriptorPool& pool,
DescriptorSetProvider& texture_provider_, u32 num_swapchain_images_) u32 num_swapchain_images_)
: instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache}, : instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache},
texture_provider{texture_provider_}, blit_helper{instance, scheduler, pool, renderpass_cache}, blit_helper{instance, scheduler, pool, renderpass_cache},
upload_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferSrc, UPLOAD_BUFFER_SIZE, upload_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferSrc, UPLOAD_BUFFER_SIZE,
BufferType::Upload}, BufferType::Upload},
download_buffer{instance, scheduler, download_buffer{instance, scheduler,
@@ -697,13 +696,6 @@ bool TextureRuntime::NeedsConversion(VideoCore::PixelFormat format) const {
traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil); traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil);
} }
void TextureRuntime::FreeDescriptorSetsWithImage(vk::ImageView image_view) {
texture_provider.FreeWithImage(image_view);
blit_helper.compute_provider.FreeWithImage(image_view);
blit_helper.compute_buffer_provider.FreeWithImage(image_view);
blit_helper.two_textures_provider.FreeWithImage(image_view);
}
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params) Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params)
: SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, : SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()},
scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} { scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} {
@@ -803,9 +795,6 @@ Surface::~Surface() {
return; return;
} }
for (const auto& [alloc, image, image_view] : handles) { for (const auto& [alloc, image, image_view] : handles) {
if (image_view) {
runtime->FreeDescriptorSetsWithImage(*image_view);
}
if (image) { if (image) {
vmaDestroyImage(instance->GetAllocator(), image, alloc); vmaDestroyImage(instance->GetAllocator(), image, alloc);
} }
@@ -1558,13 +1547,13 @@ Sampler::Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params
Sampler::~Sampler() = default; Sampler::~Sampler() = default;
DebugScope::DebugScope(TextureRuntime& runtime, Common::Vec4f color, std::string_view label) DebugScope::DebugScope(TextureRuntime& runtime, Common::Vec4f color, std::string&& label)
: scheduler{runtime.GetScheduler()}, has_debug_tool{ : scheduler{runtime.GetScheduler()}, has_debug_tool{
runtime.GetInstance().HasDebuggingToolAttached()} { runtime.GetInstance().HasDebuggingToolAttached()} {
if (!has_debug_tool) { if (!has_debug_tool) {
return; return;
} }
scheduler.Record([color, label = std::string(label)](vk::CommandBuffer cmdbuf) { scheduler.Record([color, label = std::move(label)](vk::CommandBuffer cmdbuf) {
const vk::DebugUtilsLabelEXT debug_label = { const vk::DebugUtilsLabelEXT debug_label = {
.pLabelName = label.data(), .pLabelName = label.data(),
.color = std::array{color[0], color[1], color[2], color[3]}, .color = std::array{color[0], color[1], color[2], color[3]},

View File

@@ -42,8 +42,8 @@ class TextureRuntime {
public: public:
explicit TextureRuntime(const Instance& instance, Scheduler& scheduler, explicit TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorPool& pool, RenderpassCache& renderpass_cache,
DescriptorSetProvider& texture_provider, u32 num_swapchain_images); u32 num_swapchain_images);
~TextureRuntime(); ~TextureRuntime();
const Instance& GetInstance() const { const Instance& GetInstance() const {
@@ -85,9 +85,6 @@ public:
/// Returns true if the provided pixel format needs convertion /// Returns true if the provided pixel format needs convertion
bool NeedsConversion(VideoCore::PixelFormat format) const; bool NeedsConversion(VideoCore::PixelFormat format) const;
/// Removes any descriptor sets that contain the provided image view.
void FreeDescriptorSetsWithImage(vk::ImageView image_view);
private: private:
/// Clears a partial texture rect using a clear rectangle /// Clears a partial texture rect using a clear rectangle
void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear); void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear);
@@ -96,7 +93,6 @@ private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassCache& renderpass_cache; RenderpassCache& renderpass_cache;
DescriptorSetProvider& texture_provider;
BlitHelper blit_helper; BlitHelper blit_helper;
StreamBuffer upload_buffer; StreamBuffer upload_buffer;
StreamBuffer download_buffer; StreamBuffer download_buffer;
@@ -277,7 +273,7 @@ public:
explicit DebugScope(TextureRuntime& runtime, Common::Vec4f color, explicit DebugScope(TextureRuntime& runtime, Common::Vec4f color,
fmt::format_string<T...> format, T... args) fmt::format_string<T...> format, T... args)
: DebugScope{runtime, color, fmt::format(format, std::forward<T>(args)...)} {} : DebugScope{runtime, color, fmt::format(format, std::forward<T>(args)...)} {}
explicit DebugScope(TextureRuntime& runtime, Common::Vec4f color, std::string_view label); explicit DebugScope(TextureRuntime& runtime, Common::Vec4f color, std::string&& label);
~DebugScope(); ~DebugScope();
private: private: