temp
This commit is contained in:
parent
22dea3c194
commit
287db66914
@ -176,15 +176,23 @@ struct TexturingRegs {
|
||||
INSERT_PADDING_WORDS(0x9);
|
||||
|
||||
struct FullTextureConfig {
|
||||
const bool enabled;
|
||||
const TextureConfig config;
|
||||
const TextureFormat format;
|
||||
u32 enabled;
|
||||
TextureConfig config;
|
||||
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 {{
|
||||
{static_cast<bool>(main_config.texture0_enable), texture0, texture0_format},
|
||||
{static_cast<bool>(main_config.texture1_enable), texture1, texture1_format},
|
||||
{static_cast<bool>(main_config.texture2_enable), texture2, texture2_format},
|
||||
{main_config.texture0_enable, texture0, texture0_format},
|
||||
{main_config.texture1_enable, texture1, texture1_format},
|
||||
{main_config.texture2_enable, texture2, texture2_format},
|
||||
}};
|
||||
}
|
||||
|
||||
@ -381,11 +389,11 @@ struct TexturingRegs {
|
||||
BitField<16, 2, u32> alpha_scale;
|
||||
};
|
||||
|
||||
inline unsigned GetColorMultiplier() const {
|
||||
inline u32 GetColorMultiplier() const {
|
||||
return (color_scale < 3) ? (1 << color_scale) : 1;
|
||||
}
|
||||
|
||||
inline unsigned GetAlphaMultiplier() const {
|
||||
inline u32 GetAlphaMultiplier() const {
|
||||
return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
|
||||
}
|
||||
};
|
||||
|
@ -35,6 +35,7 @@ struct ScreenRectVertex {
|
||||
Common::Vec2f tex_coord;
|
||||
};
|
||||
|
||||
constexpr u32 MAX_IN_FLIGHT_FRAMES = 10;
|
||||
constexpr u32 VERTEX_BUFFER_SIZE = sizeof(ScreenRectVertex) * 8192;
|
||||
|
||||
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)
|
||||
: RendererBase{system, window, secondary_window}, memory{system.Memory()},
|
||||
instance{system.TelemetrySession(), window, Settings::values.physical_device.GetValue()},
|
||||
scheduler{instance, renderpass_cache}, renderpass_cache{instance, scheduler}, pool{instance},
|
||||
main_window{window, instance, scheduler},
|
||||
scheduler{instance, renderpass_cache}, renderpass_cache{instance, scheduler},
|
||||
pool{instance, scheduler.GetMasterSemaphore()}, main_window{window, instance, scheduler},
|
||||
vertex_buffer{instance, scheduler, vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
VERTEX_BUFFER_SIZE},
|
||||
rasterizer{memory,
|
||||
@ -77,7 +78,6 @@ RendererVulkan::~RendererVulkan() {
|
||||
device.waitIdle();
|
||||
|
||||
device.destroyPipelineLayout(pipeline_layout);
|
||||
device.destroyDescriptorPool(descriptor_pool);
|
||||
device.destroyDescriptorSetLayout(descriptor_set_layout);
|
||||
device.destroyDescriptorUpdateTemplate(update_template);
|
||||
device.destroyShaderModule(vert_shader);
|
||||
@ -305,29 +305,7 @@ void RendererVulkan::BuildLayoutsAndDescriptors() {
|
||||
pipeline_layout = device.createPipelineLayout(pipeline_layout_info);
|
||||
|
||||
const u32 image_count = main_window.ImageCount();
|
||||
const vk::DescriptorPoolSize pool_size = {
|
||||
.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);
|
||||
present_sets = pool.Commit(descriptor_set_layout, image_count);
|
||||
}
|
||||
|
||||
void RendererVulkan::BuildPipelines() {
|
||||
|
@ -4,18 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hw/gpu.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_present_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.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"
|
||||
|
||||
namespace Core {
|
||||
@ -122,7 +118,6 @@ private:
|
||||
std::unique_ptr<PresentWindow> second_window;
|
||||
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
vk::DescriptorPool descriptor_pool;
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
vk::DescriptorUpdateTemplate update_template;
|
||||
std::vector<vk::DescriptorSet> present_sets;
|
||||
|
@ -177,10 +177,9 @@ constexpr vk::PipelineShaderStageCreateInfo MakeStages(vk::ShaderModule compute_
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, DescriptorPool& pool,
|
||||
RenderpassCache& renderpass_cache_)
|
||||
: instance{instance_}, scheduler{scheduler_}, renderpass_cache{renderpass_cache_},
|
||||
device{instance.GetDevice()}, compute_provider{instance, pool, COMPUTE_BINDINGS},
|
||||
BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_)
|
||||
: instance{instance_}, scheduler{scheduler_}, device{instance.GetDevice()},
|
||||
compute_provider{instance, pool, COMPUTE_BINDINGS},
|
||||
compute_buffer_provider{instance, pool, COMPUTE_BUFFER_BINDINGS},
|
||||
two_textures_provider{instance, pool, TWO_TEXTURES_BINDINGS},
|
||||
compute_pipeline_layout{
|
||||
@ -314,7 +313,6 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
|
||||
|
||||
const auto descriptor_set = compute_provider.Acquire(textures);
|
||||
|
||||
renderpass_cache.EndRendering();
|
||||
scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
|
||||
dst_image = dest.Image()](vk::CommandBuffer cmdbuf) {
|
||||
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);
|
||||
|
||||
renderpass_cache.EndRendering();
|
||||
scheduler.Record([this, descriptor_set, copy, src_image = source.Image(),
|
||||
extent = source.RealExtent(false)](vk::CommandBuffer cmdbuf) {
|
||||
const vk::ImageMemoryBarrier pre_barrier = {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
struct TextureBlit;
|
||||
@ -23,8 +23,7 @@ class BlitHelper {
|
||||
friend class TextureRuntime;
|
||||
|
||||
public:
|
||||
BlitHelper(const Instance& instance, Scheduler& scheduler, DescriptorPool& pool,
|
||||
RenderpassCache& renderpass_cache);
|
||||
explicit BlitHelper(const Instance& instance, Scheduler& scheduler);
|
||||
~BlitHelper();
|
||||
|
||||
bool BlitDepthStencil(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
@ -35,23 +34,16 @@ public:
|
||||
const VideoCore::BufferTextureCopy& copy);
|
||||
|
||||
private:
|
||||
/// Creates compute pipelines used for blit
|
||||
vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout);
|
||||
|
||||
/// Creates graphics pipelines used for blit
|
||||
vk::Pipeline MakeDepthStencilBlitPipeline();
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
RenderpassCache& renderpass_cache;
|
||||
|
||||
vk::Device device;
|
||||
vk::RenderPass r32_renderpass;
|
||||
|
||||
DescriptorSetProvider compute_provider;
|
||||
DescriptorSetProvider compute_buffer_provider;
|
||||
DescriptorSetProvider two_textures_provider;
|
||||
vk::PipelineLayout compute_pipeline_layout;
|
||||
vk::PipelineLayout compute_buffer_pipeline_layout;
|
||||
vk::PipelineLayout two_textures_pipeline_layout;
|
||||
|
@ -3,10 +3,46 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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"
|
||||
|
||||
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_)
|
||||
: scheduler{scheduler_}, num_frames{num_frames_} {
|
||||
frame_payload_size = PAYLOAD_SIZE / num_frames;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Instance;
|
||||
class Scheduler;
|
||||
|
||||
union DescriptorUpdateEntry {
|
||||
@ -17,10 +18,30 @@ union DescriptorUpdateEntry {
|
||||
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;
|
||||
|
||||
class DescriptorUpdateQueue final {
|
||||
static constexpr size_t PAYLOAD_SIZE = 1_MiB;
|
||||
|
||||
public:
|
||||
explicit DescriptorUpdateQueue(Scheduler& scheduler, size_t num_frames);
|
||||
~DescriptorUpdateQueue();
|
||||
@ -33,6 +54,14 @@ public:
|
||||
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) {
|
||||
(payload_cursor++)->image = vk::DescriptorImageInfo{
|
||||
.sampler = sampler,
|
||||
|
@ -272,10 +272,10 @@ bool GraphicsPipeline::Build(bool fail_on_compile_required) {
|
||||
pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT;
|
||||
}
|
||||
|
||||
auto result = device.createGraphicsPipelineUnique(pipeline_cache, pipeline_info);
|
||||
if (result.result == vk::Result::eSuccess) {
|
||||
pipeline = std::move(result.value);
|
||||
} else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) {
|
||||
auto [result, handle] = device.createGraphicsPipelineUnique(pipeline_cache, pipeline_info);
|
||||
if (result == vk::Result::eSuccess) {
|
||||
pipeline = std::move(handle);
|
||||
} else if (result == vk::Result::eErrorPipelineCompileRequiredEXT) {
|
||||
return false;
|
||||
} else {
|
||||
UNREACHABLE_MSG("Graphics pipeline creation failed!");
|
||||
|
@ -42,22 +42,18 @@ namespace Vulkan {
|
||||
class Instance;
|
||||
class RenderpassCache;
|
||||
|
||||
constexpr u32 MAX_SHADER_STAGES = 3;
|
||||
constexpr u32 MAX_VERTEX_ATTRIBUTES = 16;
|
||||
constexpr u32 MAX_VERTEX_BINDINGS = 13;
|
||||
constexpr size_t MAX_SHADER_STAGES = 3;
|
||||
constexpr size_t MAX_VERTEX_ATTRIBUTES = 16;
|
||||
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 {
|
||||
u8 value = 0;
|
||||
u32 raw;
|
||||
BitField<0, 2, Pica::PipelineRegs::TriangleTopology> topology;
|
||||
BitField<4, 2, Pica::RasterizerRegs::CullMode> cull_mode;
|
||||
};
|
||||
|
||||
union DepthStencilState {
|
||||
u32 value = 0;
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> depth_test_enable;
|
||||
BitField<1, 1, u32> depth_write_enable;
|
||||
BitField<2, 1, u32> stencil_test_enable;
|
||||
@ -73,7 +69,7 @@ struct BlendingState {
|
||||
u16 color_write_mask;
|
||||
Pica::FramebufferRegs::LogicOp logic_op;
|
||||
union {
|
||||
u32 value = 0;
|
||||
u32 raw;
|
||||
BitField<0, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor;
|
||||
BitField<4, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor;
|
||||
BitField<8, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq;
|
||||
@ -84,10 +80,11 @@ struct BlendingState {
|
||||
};
|
||||
|
||||
struct DynamicState {
|
||||
u32 blend_color = 0;
|
||||
u32 blend_color;
|
||||
u8 stencil_reference;
|
||||
u8 stencil_compare_mask;
|
||||
u8 stencil_write_mask;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
Common::Rectangle<u32> scissor;
|
||||
Common::Rectangle<s32> viewport;
|
||||
@ -129,12 +126,19 @@ struct AttachmentInfo {
|
||||
* Information about a graphics/compute pipeline
|
||||
*/
|
||||
struct PipelineInfo {
|
||||
BlendingState blending;
|
||||
AttachmentInfo attachments;
|
||||
RasterizationState rasterization;
|
||||
DepthStencilState depth_stencil;
|
||||
DynamicState dynamic;
|
||||
VertexLayout vertex_layout;
|
||||
BlendingState blending{};
|
||||
AttachmentInfo attachments{};
|
||||
RasterizationState rasterization{};
|
||||
DepthStencilState depth_stencil{};
|
||||
DynamicState dynamic{};
|
||||
VertexLayout vertex_layout{};
|
||||
|
||||
enum Type : u32 {
|
||||
Normal,
|
||||
ShadowPlane,
|
||||
ShadowCube,
|
||||
};
|
||||
Type type{Type::Normal};
|
||||
|
||||
[[nodiscard]] u64 Hash(const Instance& instance) const;
|
||||
|
||||
@ -148,6 +152,7 @@ struct PipelineInfo {
|
||||
return depth_write || stencil_write;
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<PipelineInfo>);
|
||||
|
||||
struct Shader : public Common::AsyncHandle {
|
||||
explicit Shader(const Instance& instance);
|
||||
@ -176,7 +181,7 @@ public:
|
||||
bool Build(bool fail_on_compile_required = false);
|
||||
|
||||
[[nodiscard]] vk::Pipeline Handle() const noexcept {
|
||||
return *pipeline;
|
||||
return pipeline.get();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/renderer_vulkan/pica_to_vk.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 {
|
||||
|
||||
enum DescriptorSet {
|
||||
Buffer,
|
||||
Texture,
|
||||
Utility,
|
||||
};
|
||||
|
||||
u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) {
|
||||
switch (format) {
|
||||
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},
|
||||
}};
|
||||
|
||||
template <vk::DescriptorType tex0_type, u32 num_faces>
|
||||
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},
|
||||
{2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
}};
|
||||
|
||||
// TODO: Use descriptor array for shadow cube
|
||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 7> SHADOW_BINDINGS = {{
|
||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 1> UTILITY_BINDINGS = {{
|
||||
{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_,
|
||||
RenderpassCache& renderpass_cache_, DescriptorPool& pool_)
|
||||
: instance{instance_}, scheduler{scheduler_}, renderpass_cache{renderpass_cache_}, pool{pool_},
|
||||
RenderpassCache& renderpass_cache_, DescriptorPool& persistent_pool)
|
||||
: instance{instance_}, scheduler{scheduler_},
|
||||
renderpass_cache{renderpass_cache_}, pool{instance, scheduler.GetMasterSemaphore()},
|
||||
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{
|
||||
instance, vk::ShaderStageFlagBits::eVertex,
|
||||
GLSL::GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported(), true)} {
|
||||
// Create profile for driver assisted shader features.
|
||||
profile = Pica::Shader::Profile{
|
||||
.has_separable_shaders = true,
|
||||
.has_clip_planes = instance.IsShaderClipDistanceSupported(),
|
||||
@ -98,14 +107,10 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
||||
.has_logic_op = !instance.NeedsLogicOpEmulation(),
|
||||
.is_vulkan = true,
|
||||
};
|
||||
BuildLayout();
|
||||
}
|
||||
|
||||
void PipelineCache::BuildLayout() {
|
||||
std::array<vk::DescriptorSetLayout, NUM_RASTERIZER_SETS> descriptor_set_layouts;
|
||||
std::transform(descriptor_set_providers.begin(), descriptor_set_providers.end(),
|
||||
descriptor_set_layouts.begin(),
|
||||
[](const auto& provider) { return provider.Layout(); });
|
||||
descriptor_set_layouts[DescriptorSet::Buffer] = buffer_set_spec.Layout();
|
||||
descriptor_set_layouts[DescriptorSet::Utility] = utility_set_spec.Layout();
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = NUM_RASTERIZER_SETS,
|
||||
@ -113,7 +118,15 @@ void PipelineCache::BuildLayout() {
|
||||
.pushConstantRangeCount = 0,
|
||||
.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() {
|
||||
@ -125,34 +138,40 @@ void PipelineCache::LoadDiskCache() {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string cache_file_path = fmt::format("{}{:x}{:x}.bin", GetPipelineCacheDir(),
|
||||
instance.GetVendorID(), instance.GetDeviceID());
|
||||
vk::PipelineCacheCreateInfo cache_info = {
|
||||
.initialDataSize = 0,
|
||||
.pInitialData = nullptr,
|
||||
};
|
||||
const auto cache_dir = GetPipelineCacheDir();
|
||||
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);
|
||||
|
||||
vk::PipelineCacheCreateInfo cache_info{};
|
||||
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();
|
||||
cache_data.resize(cache_file_size);
|
||||
if (cache_file.ReadBytes(cache_data.data(), cache_file_size)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
SCOPE_EXIT({
|
||||
const vk::Device device = instance.GetDevice();
|
||||
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
||||
});
|
||||
|
||||
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();
|
||||
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
||||
const u64 cache_file_size = cache_file.GetSize();
|
||||
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() {
|
||||
@ -160,22 +179,23 @@ void PipelineCache::SaveDiskCache() {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string cache_file_path = fmt::format("{}{:x}{:x}.bin", GetPipelineCacheDir(),
|
||||
instance.GetVendorID(), instance.GetDeviceID());
|
||||
const auto cache_dir = GetPipelineCacheDir();
|
||||
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"};
|
||||
if (!cache_file.IsOpen()) {
|
||||
LOG_ERROR(Render_Vulkan, "Unable to open pipeline cache for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
auto cache_data = device.getPipelineCacheData(*pipeline_cache);
|
||||
if (!cache_file.WriteBytes(cache_data.data(), cache_data.size())) {
|
||||
LOG_ERROR(Render_Vulkan, "Error during pipeline cache write");
|
||||
return;
|
||||
const vk::Device device = instance.GetDevice();
|
||||
const auto cache_data = device.getPipelineCacheData(*pipeline_cache);
|
||||
if (cache_file.WriteBytes(cache_data.data(), cache_data.size()) != cache_data.size()) {
|
||||
LOG_ERROR(Render_Vulkan, "Error during pipeline cache write, removing");
|
||||
FileUtil::Delete(cache_file_path);
|
||||
}
|
||||
|
||||
cache_file.Close();
|
||||
}
|
||||
|
||||
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);
|
||||
if (new_pipeline) {
|
||||
it.value() =
|
||||
std::make_unique<GraphicsPipeline>(instance, renderpass_cache, info, *pipeline_cache,
|
||||
*pipeline_layout, current_shaders, &workers);
|
||||
const auto pipeline_layout = pipeline_layouts[info.type].get();
|
||||
auto pipeline = std::make_unique<GraphicsPipeline>(instance, renderpass_cache, info,
|
||||
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)) {
|
||||
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& shader = iter->second;
|
||||
auto& shader = iter.value();
|
||||
|
||||
if (new_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};
|
||||
@ -438,7 +460,7 @@ bool PipelineCache::UseFixedGeometryShader(const Pica::Regs& regs) {
|
||||
|
||||
const PicaFixedGSConfig gs_config{regs, instance.IsShaderClipDistanceSupported()};
|
||||
auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance);
|
||||
auto& shader = it->second;
|
||||
auto& shader = it.value();
|
||||
|
||||
if (new_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 FSConfig fs_config{regs, user, profile};
|
||||
const auto [it, new_shader] = fragment_shaders.try_emplace(fs_config, instance);
|
||||
auto& shader = it->second;
|
||||
auto& shader = it.value();
|
||||
|
||||
if (new_shader) {
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
if (offsets[binding] != static_cast<u32>(offset)) {
|
||||
offsets[binding] = static_cast<u32>(offset);
|
||||
|
@ -8,7 +8,9 @@
|
||||
#include <tsl/robin_map.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_resource_pool.h"
|
||||
#include "video_core/shader/generator/pica_fs_config.h"
|
||||
#include "video_core/shader/generator/profile.h"
|
||||
#include "video_core/shader/generator/shader_gen.h"
|
||||
@ -28,13 +30,20 @@ class Scheduler;
|
||||
class RenderpassCache;
|
||||
class DescriptorPool;
|
||||
|
||||
constexpr u32 NUM_RASTERIZER_SETS = 3;
|
||||
constexpr u32 NUM_DYNAMIC_OFFSETS = 3;
|
||||
enum class PipelineConfig {
|
||||
Normal,
|
||||
ShadowPlane,
|
||||
ShadowCube,
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a collection of rasterizer pipelines used during rendering.
|
||||
*/
|
||||
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:
|
||||
explicit PipelineCache(const Instance& instance, Scheduler& scheduler,
|
||||
RenderpassCache& renderpass_cache, DescriptorPool& pool);
|
||||
@ -65,25 +74,10 @@ public:
|
||||
/// Binds a fragment shader generated from PICA state
|
||||
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
|
||||
void SetBufferOffset(u32 binding, size_t offset);
|
||||
void BindBufferRange(u32 binding, size_t offset);
|
||||
|
||||
private:
|
||||
/// Builds the rasterizer pipeline layout
|
||||
void BuildLayout();
|
||||
|
||||
/// Returns true when the disk data can be used by the current driver
|
||||
bool IsCacheValid(std::span<const u8> cache_data) const;
|
||||
|
||||
@ -97,11 +91,10 @@ private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
RenderpassCache& renderpass_cache;
|
||||
DescriptorPool& pool;
|
||||
DescriptorPool pool;
|
||||
|
||||
Pica::Shader::Profile profile{};
|
||||
vk::UniquePipelineCache pipeline_cache;
|
||||
vk::UniquePipelineLayout pipeline_layout;
|
||||
std::size_t num_worker_threads;
|
||||
Common::ThreadWorker workers;
|
||||
PipelineInfo current_info{};
|
||||
@ -109,8 +102,13 @@ private:
|
||||
tsl::robin_map<u64, std::unique_ptr<GraphicsPipeline>, Common::IdentityHash<u64>>
|
||||
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<vk::DescriptorSet, NUM_RASTERIZER_SETS> bound_descriptor_sets{};
|
||||
std::bitset<NUM_RASTERIZER_SETS> set_dirty{};
|
||||
|
||||
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()},
|
||||
blit_supported{
|
||||
CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)},
|
||||
use_present_thread{Settings::values.async_presentation.GetValue()},
|
||||
last_render_surface{emu_window.GetWindowInfo().render_surface} {
|
||||
use_present_thread{Settings::values.async_presentation.GetValue()} {
|
||||
|
||||
const u32 num_images = swapchain.GetImageCount();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
@ -156,7 +155,7 @@ PresentWindow::~PresentWindow() {
|
||||
}
|
||||
|
||||
void PresentWindow::RecreateFrame(Frame* frame, u32 width, u32 height) {
|
||||
vk::Device device = instance.GetDevice();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
if (frame->framebuffer) {
|
||||
device.destroyFramebuffer(frame->framebuffer);
|
||||
}
|
||||
@ -237,7 +236,7 @@ Frame* PresentWindow::GetRenderFrame() {
|
||||
Frame* frame = free_queue.front();
|
||||
free_queue.pop();
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
vk::Result result{};
|
||||
|
||||
const auto wait = [&]() {
|
||||
@ -453,7 +452,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
|
||||
const vk::Semaphore image_acquired = swapchain.GetImageAcquiredSemaphore();
|
||||
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()),
|
||||
.pWaitSemaphores = wait_semaphores.data(),
|
||||
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||
@ -468,8 +467,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
|
||||
try {
|
||||
graphics_queue.submit(submit_info, frame->present_done);
|
||||
} catch (vk::DeviceLostError& err) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Device lost during present submit: {}", err.what());
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Device lost during present submit: {}", err.what());
|
||||
}
|
||||
|
||||
swapchain.Present();
|
||||
|
@ -65,7 +65,9 @@ public:
|
||||
|
||||
private:
|
||||
void PresentThread(std::stop_token token);
|
||||
|
||||
void CopyToSwapchain(Frame* frame);
|
||||
|
||||
vk::RenderPass CreateRenderpass();
|
||||
|
||||
private:
|
||||
@ -92,7 +94,6 @@ private:
|
||||
bool vsync_enabled{};
|
||||
bool blit_supported;
|
||||
bool use_present_thread{true};
|
||||
void* last_render_surface{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -89,6 +89,7 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
|
||||
MakeSoftwareVertexLayout();
|
||||
pipeline_info.vertex_layout = software_layout;
|
||||
|
||||
// Create texture buffer views for the lighting LUTs.
|
||||
const vk::Device device = instance.GetDevice();
|
||||
texture_lf_view = device.createBufferViewUnique({
|
||||
.buffer = texture_lf_buffer.Handle(),
|
||||
@ -109,24 +110,14 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory,
|
||||
.range = VK_WHOLE_SIZE,
|
||||
});
|
||||
|
||||
// Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize
|
||||
// all descriptor sets even the ones we don't use.
|
||||
pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
|
||||
pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(VSUniformData));
|
||||
pipeline_cache.BindBuffer(2, uniform_buffer.Handle(), 0, sizeof(FSUniformData));
|
||||
pipeline_cache.BindTexelBuffer(3, *texture_lf_view);
|
||||
pipeline_cache.BindTexelBuffer(4, *texture_rg_view);
|
||||
pipeline_cache.BindTexelBuffer(5, *texture_rgba_view);
|
||||
|
||||
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());
|
||||
}
|
||||
// Update persistent buffer descriptor set with our rasterizer buffers.
|
||||
update_queue.Acquire();
|
||||
update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
|
||||
update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData));
|
||||
update_queue.AddBuffer(uniform_buffer.Handle(), 0, sizeof(VSUniformData));
|
||||
update_queue.AddTexelBuffer(texture_lf_view.get());
|
||||
update_queue.AddTexelBuffer(texture_rg_view.get());
|
||||
update_queue.AddTexelBuffer(texture_rgba_view.get());
|
||||
|
||||
SyncEntireState();
|
||||
}
|
||||
@ -476,16 +467,10 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update attachment formats
|
||||
pipeline_info.attachments.color = framebuffer->Format(SurfaceType::Color);
|
||||
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
|
||||
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor();
|
||||
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
|
||||
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
|
||||
if (shader_dirty) {
|
||||
pipeline_cache.UseFragmentShader(regs, user_config);
|
||||
@ -555,7 +545,17 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
|
||||
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
|
||||
|
||||
// Check if the PICA texture configuration changed.
|
||||
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) {
|
||||
const auto& texture = pica_textures[texture_index];
|
||||
|
||||
@ -563,8 +563,7 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
|
||||
if (!texture.enabled) {
|
||||
const Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID);
|
||||
const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID);
|
||||
pipeline_cache.BindTexture(texture_index, null_surface.ImageView(),
|
||||
null_sampler.Handle());
|
||||
update_queue.AddSampledImage(null_surface.ImageView(), null_sampler.Handle());
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -574,7 +573,7 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
|
||||
case TextureType::Shadow2D: {
|
||||
Surface& surface = res_cache.GetTextureSurface(texture);
|
||||
surface.flags |= VideoCore::SurfaceFlagBits::ShadowMap;
|
||||
pipeline_cache.BindStorageImage(0, surface.StorageView());
|
||||
update_queue.AddImage(surface.StorageView());
|
||||
continue;
|
||||
}
|
||||
case TextureType::ShadowCube: {
|
||||
@ -586,7 +585,6 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
UnbindSpecial();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -594,10 +592,12 @@ void RasterizerVulkan::SyncTextureUnits(const Framebuffer* framebuffer) {
|
||||
// Bind the texture provided by the rasterizer cache
|
||||
Surface& surface = res_cache.GetTextureSurface(texture);
|
||||
Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||
if (!IsFeedbackLoop(texture_index, framebuffer, surface, sampler)) {
|
||||
pipeline_cache.BindTexture(texture_index, surface.ImageView(), sampler.Handle());
|
||||
if (!IsFeedbackLoop(framebuffer, surface, sampler)) {
|
||||
update_queue.AddSampledImage(surface.ImageView(), sampler.Handle());
|
||||
}
|
||||
}
|
||||
|
||||
textures = pica_textures;
|
||||
}
|
||||
|
||||
void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) {
|
||||
@ -609,13 +609,11 @@ void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConf
|
||||
};
|
||||
|
||||
for (CubeFace face : faces) {
|
||||
const u32 binding = static_cast<u32>(face);
|
||||
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
|
||||
|
||||
const VideoCore::SurfaceId surface_id = res_cache.GetTextureSurface(info);
|
||||
const auto surface_id = res_cache.GetTextureSurface(info);
|
||||
Surface& surface = res_cache.GetSurface(surface_id);
|
||||
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,
|
||||
};
|
||||
|
||||
Surface& surface = res_cache.GetTextureCube(config);
|
||||
Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||
pipeline_cache.BindTexture(0, surface.ImageView(), sampler.Handle());
|
||||
const Surface& surface = res_cache.GetTextureCube(config);
|
||||
const Sampler& sampler = res_cache.GetSampler(texture.config);
|
||||
update_queue.AddSampledImage(surface.ImageView(), sampler.Handle());
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer,
|
||||
Surface& surface, Sampler& sampler) {
|
||||
bool RasterizerVulkan::IsFeedbackLoop(const Framebuffer* framebuffer, Surface& surface,
|
||||
Sampler& sampler) {
|
||||
const vk::ImageView color_view = framebuffer->ImageView(SurfaceType::Color);
|
||||
const bool is_feedback_loop = color_view == surface.ImageView();
|
||||
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
|
||||
pipeline_cache.BindTexture(texture_index, surface.CopyImageView(), sampler.Handle());
|
||||
update_queue.AddSampledImage(surface.CopyImageView(), sampler.Handle());
|
||||
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) {
|
||||
switch (id) {
|
||||
// Culling
|
||||
@ -1098,24 +1089,24 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
||||
auto [uniforms, offset, invalidate] =
|
||||
uniform_buffer.Map(uniform_size, uniform_buffer_alignment);
|
||||
|
||||
u32 used_bytes = 0;
|
||||
size_t used_bytes = 0;
|
||||
|
||||
if (sync_vs || invalidate) {
|
||||
std::memcpy(uniforms + used_bytes, &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;
|
||||
used_bytes += static_cast<u32>(uniform_size_aligned_vs);
|
||||
used_bytes += uniform_size_aligned_vs;
|
||||
}
|
||||
|
||||
if (sync_fs || invalidate) {
|
||||
std::memcpy(uniforms + used_bytes, &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;
|
||||
used_bytes += static_cast<u32>(uniform_size_aligned_fs);
|
||||
used_bytes += uniform_size_aligned_fs;
|
||||
}
|
||||
|
||||
if (sync_vs_pica) {
|
||||
@ -1123,8 +1114,8 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
||||
vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs);
|
||||
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
||||
|
||||
pipeline_cache.SetBufferOffset(0, offset + used_bytes);
|
||||
used_bytes += static_cast<u32>(uniform_size_aligned_vs_pica);
|
||||
pipeline_cache.BindBufferRange(0, offset + used_bytes);
|
||||
used_bytes += uniform_size_aligned_vs_pica;
|
||||
}
|
||||
|
||||
uniform_buffer.Commit(used_bytes);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/hw/gpu.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_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
@ -104,11 +105,7 @@ private:
|
||||
void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture);
|
||||
|
||||
/// Makes a temporary copy of the framebuffer if a feedback loop is detected
|
||||
bool IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer, Surface& surface,
|
||||
Sampler& sampler);
|
||||
|
||||
/// Unbinds all special texture unit 0 texture configurations
|
||||
void UnbindSpecial();
|
||||
bool IsFeedbackLoop(const Framebuffer* framebuffer, Surface& surface, Sampler& sampler);
|
||||
|
||||
/// Upload the uniform blocks to the uniform buffer object
|
||||
void UploadUniforms(bool accelerate_draw);
|
||||
@ -144,6 +141,7 @@ private:
|
||||
PipelineCache pipeline_cache;
|
||||
TextureRuntime runtime;
|
||||
RasterizerCache res_cache;
|
||||
DescriptorUpdateQueue update_queue;
|
||||
|
||||
VertexLayout software_layout;
|
||||
std::array<u32, 16> binding_offsets{};
|
||||
@ -159,6 +157,7 @@ private:
|
||||
vk::UniqueBufferView texture_lf_view;
|
||||
vk::UniqueBufferView texture_rg_view;
|
||||
vk::UniqueBufferView texture_rgba_view;
|
||||
Pica::TexturingRegs::Textures textures{};
|
||||
u64 uniform_buffer_alignment;
|
||||
u64 uniform_size_aligned_vs_pica;
|
||||
u64 uniform_size_aligned_vs;
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "video_core/rasterizer_cache/texture_codec.h"
|
||||
#include "video_core/rasterizer_cache/utils.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_renderpass_cache.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,
|
||||
RenderpassCache& renderpass_cache, DescriptorPool& pool,
|
||||
DescriptorSetProvider& texture_provider_, u32 num_swapchain_images_)
|
||||
u32 num_swapchain_images_)
|
||||
: 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,
|
||||
BufferType::Upload},
|
||||
download_buffer{instance, scheduler,
|
||||
@ -697,13 +696,6 @@ bool TextureRuntime::NeedsConversion(VideoCore::PixelFormat format) const {
|
||||
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)
|
||||
: SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()},
|
||||
scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} {
|
||||
@ -803,9 +795,6 @@ Surface::~Surface() {
|
||||
return;
|
||||
}
|
||||
for (const auto& [alloc, image, image_view] : handles) {
|
||||
if (image_view) {
|
||||
runtime->FreeDescriptorSetsWithImage(*image_view);
|
||||
}
|
||||
if (image) {
|
||||
vmaDestroyImage(instance->GetAllocator(), image, alloc);
|
||||
}
|
||||
@ -1558,13 +1547,13 @@ Sampler::Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params
|
||||
|
||||
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{
|
||||
runtime.GetInstance().HasDebuggingToolAttached()} {
|
||||
if (!has_debug_tool) {
|
||||
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 = {
|
||||
.pLabelName = label.data(),
|
||||
.color = std::array{color[0], color[1], color[2], color[3]},
|
||||
|
@ -42,8 +42,8 @@ class TextureRuntime {
|
||||
|
||||
public:
|
||||
explicit TextureRuntime(const Instance& instance, Scheduler& scheduler,
|
||||
RenderpassCache& renderpass_cache, DescriptorPool& pool,
|
||||
DescriptorSetProvider& texture_provider, u32 num_swapchain_images);
|
||||
RenderpassCache& renderpass_cache,
|
||||
u32 num_swapchain_images);
|
||||
~TextureRuntime();
|
||||
|
||||
const Instance& GetInstance() const {
|
||||
@ -85,9 +85,6 @@ public:
|
||||
/// Returns true if the provided pixel format needs convertion
|
||||
bool NeedsConversion(VideoCore::PixelFormat format) const;
|
||||
|
||||
/// Removes any descriptor sets that contain the provided image view.
|
||||
void FreeDescriptorSetsWithImage(vk::ImageView image_view);
|
||||
|
||||
private:
|
||||
/// Clears a partial texture rect using a clear rectangle
|
||||
void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear);
|
||||
@ -96,7 +93,6 @@ private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
RenderpassCache& renderpass_cache;
|
||||
DescriptorSetProvider& texture_provider;
|
||||
BlitHelper blit_helper;
|
||||
StreamBuffer upload_buffer;
|
||||
StreamBuffer download_buffer;
|
||||
@ -277,7 +273,7 @@ public:
|
||||
explicit DebugScope(TextureRuntime& runtime, Common::Vec4f color,
|
||||
fmt::format_string<T...> format, 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();
|
||||
|
||||
private:
|
||||
|
Loading…
x
Reference in New Issue
Block a user