temp
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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 = {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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,
|
||||||
|
@@ -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!");
|
||||||
|
@@ -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:
|
||||||
|
@@ -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,57 +138,64 @@ 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()) {
|
SCOPE_EXIT({
|
||||||
LOG_INFO(Render_Vulkan, "Loading pipeline cache");
|
const vk::Device device = instance.GetDevice();
|
||||||
|
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
||||||
|
});
|
||||||
|
|
||||||
|
FileUtil::IOFile cache_file{cache_file_path, "rb"};
|
||||||
|
if (!cache_file.IsOpen()) {
|
||||||
|
LOG_INFO(Render_Vulkan, "No pipeline cache found for device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const u64 cache_file_size = cache_file.GetSize();
|
const u64 cache_file_size = cache_file.GetSize();
|
||||||
cache_data.resize(cache_file_size);
|
cache_data.resize(cache_file_size);
|
||||||
if (cache_file.ReadBytes(cache_data.data(), 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)) {
|
if (!IsCacheValid(cache_data)) {
|
||||||
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring");
|
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring");
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
LOG_INFO(Render_Vulkan, "Loading pipeline cache with size {} KB", cache_file_size / 1024);
|
||||||
cache_info.initialDataSize = cache_file_size;
|
cache_info.initialDataSize = cache_file_size;
|
||||||
cache_info.pInitialData = cache_data.data();
|
cache_info.pInitialData = cache_data.data();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cache_file.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Device device = instance.GetDevice();
|
|
||||||
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineCache::SaveDiskCache() {
|
void PipelineCache::SaveDiskCache() {
|
||||||
if (!Settings::values.use_disk_shader_cache || !EnsureDirectories() || !pipeline_cache) {
|
if (!Settings::values.use_disk_shader_cache || !EnsureDirectories() || !pipeline_cache) {
|
||||||
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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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();
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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]},
|
||||||
|
@@ -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:
|
||||||
|
Reference in New Issue
Block a user