renderer_vulkan: Support additional dynamic states

This commit is contained in:
GPUCode
2023-01-25 16:40:02 +02:00
parent 48d3093fc8
commit b29f263e1b
4 changed files with 160 additions and 61 deletions

View File

@ -38,7 +38,7 @@ static inline u64 ComputeStructHash64(const T& data) noexcept {
* Combines the seed parameter with the provided hash, producing a new unique hash * Combines the seed parameter with the provided hash, producing a new unique hash
* Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html * Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html
*/ */
inline u64 HashCombine(std::size_t& seed, const u64 hash) { inline u64 HashCombine(std::size_t seed, const u64 hash) {
return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
} }

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <boost/container/static_vector.hpp>
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -59,6 +60,37 @@ vk::ShaderStageFlagBits MakeShaderStage(std::size_t index) {
return vk::ShaderStageFlagBits::eVertex; return vk::ShaderStageFlagBits::eVertex;
} }
u64 PipelineInfo::Hash(const Instance& instance) const {
u64 info_hash = 0;
const auto AppendHash = [&info_hash](const auto& data) {
const u64 data_hash = Common::ComputeStructHash64(data);
info_hash = Common::HashCombine(info_hash, data_hash);
};
AppendHash(vertex_layout);
AppendHash(attachments);
if (!instance.IsExtendedDynamicStateSupported()) {
AppendHash(rasterization);
AppendHash(depth_stencil);
}
if (!instance.IsExtendedDynamicState2Supported()) {
AppendHash(blending.logic_op);
}
if (!instance.IsExtendedDynamicState3LogicOpSupported() ||
!instance.IsExtendedDynamicState3BlendEnableSupported()) {
AppendHash(blending.blend_enable);
}
if (!instance.IsExtendedDynamicState3BlendEqSupported()) {
AppendHash(blending.value);
}
if (!instance.IsExtendedDynamicState3ColorMaskSupported()) {
AppendHash(blending.color_write_mask);
}
return info_hash;
}
PipelineCache::Shader::Shader(const Instance& instance) : device{instance.GetDevice()} {} PipelineCache::Shader::Shader(const Instance& instance) : device{instance.GetDevice()} {}
PipelineCache::Shader::Shader(const Instance& instance, vk::ShaderStageFlagBits stage, PipelineCache::Shader::Shader(const Instance& instance, vk::ShaderStageFlagBits stage,
@ -80,8 +112,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline(
Common::ThreadWorker* worker_) Common::ThreadWorker* worker_)
: instance{instance_}, worker{worker_}, pipeline_layout{layout_}, : instance{instance_}, worker{worker_}, pipeline_layout{layout_},
pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_}, pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_},
renderpass{ renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format,
renderpass_cache_.GetRenderpass(info.color_attachment, info.depth_attachment, false)} { info.attachments.depth_format, false)} {
// Ask the driver if it can give us the pipeline quickly // Ask the driver if it can give us the pipeline quickly
if (Build(true)) { if (Build(true)) {
@ -170,7 +202,7 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
}; };
const vk::PipelineColorBlendAttachmentState colorblend_attachment = { const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
.blendEnable = info.blending.blend_enable.Value(), .blendEnable = info.blending.blend_enable,
.srcColorBlendFactor = PicaToVK::BlendFunc(info.blending.src_color_blend_factor), .srcColorBlendFactor = PicaToVK::BlendFunc(info.blending.src_color_blend_factor),
.dstColorBlendFactor = PicaToVK::BlendFunc(info.blending.dst_color_blend_factor), .dstColorBlendFactor = PicaToVK::BlendFunc(info.blending.dst_color_blend_factor),
.colorBlendOp = PicaToVK::BlendEquation(info.blending.color_blend_eq), .colorBlendOp = PicaToVK::BlendEquation(info.blending.color_blend_eq),
@ -181,8 +213,8 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
}; };
const vk::PipelineColorBlendStateCreateInfo color_blending = { const vk::PipelineColorBlendStateCreateInfo color_blending = {
.logicOpEnable = !info.blending.blend_enable.Value() && !instance.NeedsLogicOpEmulation(), .logicOpEnable = !info.blending.blend_enable && !instance.NeedsLogicOpEmulation(),
.logicOp = PicaToVK::LogicOp(info.blending.logic_op.Value()), .logicOp = PicaToVK::LogicOp(info.blending.logic_op),
.attachmentCount = 1, .attachmentCount = 1,
.pAttachments = &colorblend_attachment, .pAttachments = &colorblend_attachment,
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
@ -209,27 +241,39 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
.pScissors = &scissor, .pScissors = &scissor,
}; };
const bool extended_dynamic_states = instance.IsExtendedDynamicStateSupported(); boost::container::static_vector<vk::DynamicState, 20> dynamic_states = {
const std::array dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor,
vk::DynamicState::eViewport, vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eScissor, vk::DynamicState::eStencilReference, vk::DynamicState::eBlendConstants,
vk::DynamicState::eStencilCompareMask,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference,
vk::DynamicState::eBlendConstants,
// VK_EXT_extended_dynamic_state
vk::DynamicState::eCullModeEXT,
vk::DynamicState::eDepthCompareOpEXT,
vk::DynamicState::eDepthTestEnableEXT,
vk::DynamicState::eDepthWriteEnableEXT,
vk::DynamicState::eFrontFaceEXT,
vk::DynamicState::ePrimitiveTopologyEXT,
vk::DynamicState::eStencilOpEXT,
vk::DynamicState::eStencilTestEnableEXT,
}; };
if (instance.IsExtendedDynamicStateSupported()) {
constexpr std::array extended = {
vk::DynamicState::eCullModeEXT, vk::DynamicState::eDepthCompareOpEXT,
vk::DynamicState::eDepthTestEnableEXT, vk::DynamicState::eDepthWriteEnableEXT,
vk::DynamicState::eFrontFaceEXT, vk::DynamicState::ePrimitiveTopologyEXT,
vk::DynamicState::eStencilOpEXT, vk::DynamicState::eStencilTestEnableEXT,
};
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
}
if (instance.IsExtendedDynamicState2Supported()) {
dynamic_states.push_back(vk::DynamicState::eLogicOpEXT);
}
if (instance.IsExtendedDynamicState3LogicOpSupported()) {
dynamic_states.push_back(vk::DynamicState::eLogicOpEnableEXT);
}
if (instance.IsExtendedDynamicState3BlendEnableSupported()) {
dynamic_states.push_back(vk::DynamicState::eColorBlendEnableEXT);
}
if (instance.IsExtendedDynamicState3BlendEqSupported()) {
dynamic_states.push_back(vk::DynamicState::eColorBlendEquationEXT);
}
if (instance.IsExtendedDynamicState3ColorMaskSupported()) {
dynamic_states.push_back(vk::DynamicState::eColorWriteMaskEXT);
}
const vk::PipelineDynamicStateCreateInfo dynamic_info = { const vk::PipelineDynamicStateCreateInfo dynamic_info = {
.dynamicStateCount = extended_dynamic_states ? static_cast<u32>(dynamic_states.size()) : 6u, .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
.pDynamicStates = dynamic_states.data(), .pDynamicStates = dynamic_states.data(),
}; };
@ -393,17 +437,14 @@ void PipelineCache::SaveDiskCache() {
MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32)); MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32));
bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
MICROPROFILE_SCOPE(Vulkan_Bind); MICROPROFILE_SCOPE(Vulkan_Bind);
std::size_t shader_hash = 0;
u64 shader_hash = 0;
for (u32 i = 0; i < MAX_SHADER_STAGES; i++) { for (u32 i = 0; i < MAX_SHADER_STAGES; i++) {
shader_hash = Common::HashCombine(shader_hash, shader_hashes[i]); shader_hash = Common::HashCombine(shader_hash, shader_hashes[i]);
} }
const u64 info_hash_size = instance.IsExtendedDynamicStateSupported() const u64 info_hash = info.Hash(instance);
? offsetof(PipelineInfo, rasterization) const u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash);
: offsetof(PipelineInfo, dynamic);
u64 info_hash = Common::ComputeHash64(&info, info_hash_size);
u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash);
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) {
@ -611,12 +652,6 @@ void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) {
} }
void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) { void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) {
if (!is_dirty && info.dynamic == current_info.dynamic &&
info.rasterization.value == current_info.rasterization.value &&
info.depth_stencil.value == current_info.depth_stencil.value) {
return;
}
scheduler.Record([is_dirty, current_dynamic = current_info.dynamic, scheduler.Record([is_dirty, current_dynamic = current_info.dynamic,
dynamic = info.dynamic](vk::CommandBuffer cmdbuf) { dynamic = info.dynamic](vk::CommandBuffer cmdbuf) {
if (dynamic.stencil_compare_mask != current_dynamic.stencil_compare_mask || is_dirty) { if (dynamic.stencil_compare_mask != current_dynamic.stencil_compare_mask || is_dirty) {
@ -689,6 +724,61 @@ void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) {
}); });
} }
if (instance.IsExtendedDynamicState2Supported()) {
scheduler.Record(
[is_dirty, logic_op = info.blending.logic_op,
current_logic_op = current_info.blending.logic_op](vk::CommandBuffer cmdbuf) {
if (logic_op != current_logic_op || is_dirty) {
cmdbuf.setLogicOpEXT(PicaToVK::LogicOp(logic_op));
}
});
}
if (instance.IsExtendedDynamicState3LogicOpSupported() && !instance.NeedsLogicOpEmulation()) {
scheduler.Record(
[is_dirty, blend_enable = info.blending.blend_enable,
current_blend_enable = current_info.blending.blend_enable](vk::CommandBuffer cmdbuf) {
if (blend_enable != current_blend_enable || is_dirty) {
cmdbuf.setLogicOpEnableEXT(!blend_enable);
}
});
}
if (instance.IsExtendedDynamicState3BlendEnableSupported()) {
scheduler.Record(
[is_dirty, blend_enable = info.blending.blend_enable,
current_blend_enable = current_info.blending.blend_enable](vk::CommandBuffer cmdbuf) {
if (blend_enable != current_blend_enable || is_dirty) {
cmdbuf.setColorBlendEnableEXT(0, blend_enable);
}
});
}
if (instance.IsExtendedDynamicState3BlendEqSupported()) {
scheduler.Record([is_dirty, blending = info.blending,
current_blending = current_info.blending](vk::CommandBuffer cmdbuf) {
if (blending.value != current_blending.value || is_dirty) {
const vk::ColorBlendEquationEXT blend_info = {
.srcColorBlendFactor = PicaToVK::BlendFunc(blending.src_color_blend_factor),
.dstColorBlendFactor = PicaToVK::BlendFunc(blending.dst_color_blend_factor),
.colorBlendOp = PicaToVK::BlendEquation(blending.color_blend_eq),
.srcAlphaBlendFactor = PicaToVK::BlendFunc(blending.src_alpha_blend_factor),
.dstAlphaBlendFactor = PicaToVK::BlendFunc(blending.dst_alpha_blend_factor),
.alphaBlendOp = PicaToVK::BlendEquation(blending.alpha_blend_eq),
};
cmdbuf.setColorBlendEquationEXT(0, blend_info);
}
});
}
if (instance.IsExtendedDynamicState3ColorMaskSupported()) {
scheduler.Record([is_dirty, color_mask = info.blending.color_write_mask,
current_color_mask =
current_info.blending.color_write_mask](vk::CommandBuffer cmdbuf) {
if (color_mask != current_color_mask || is_dirty) {
cmdbuf.setColorWriteMaskEXT(0, static_cast<vk::ColorComponentFlags>(color_mask));
}
});
}
current_info = info; current_info = info;
} }

View File

@ -45,17 +45,19 @@ union DepthStencilState {
BitField<15, 3, Pica::FramebufferRegs::CompareFunc> stencil_compare_op; BitField<15, 3, Pica::FramebufferRegs::CompareFunc> stencil_compare_op;
}; };
union BlendingState { struct BlendingState {
u16 blend_enable;
u16 color_write_mask;
Pica::FramebufferRegs::LogicOp logic_op;
union {
u32 value = 0; u32 value = 0;
BitField<0, 1, u32> blend_enable; BitField<0, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor;
BitField<1, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor; BitField<4, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor;
BitField<5, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor; BitField<8, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq;
BitField<9, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq; BitField<11, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor;
BitField<12, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor; BitField<15, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor;
BitField<16, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor; BitField<19, 3, Pica::FramebufferRegs::BlendEquation> alpha_blend_eq;
BitField<20, 3, Pica::FramebufferRegs::BlendEquation> alpha_blend_eq; };
BitField<23, 4, u32> color_write_mask;
BitField<27, 4, Pica::FramebufferRegs::LogicOp> logic_op;
}; };
struct DynamicState { struct DynamicState {
@ -90,20 +92,27 @@ struct VertexLayout {
std::array<VertexAttribute, MAX_VERTEX_ATTRIBUTES> attributes; std::array<VertexAttribute, MAX_VERTEX_ATTRIBUTES> attributes;
}; };
struct AttachmentInfo {
VideoCore::PixelFormat color_format;
VideoCore::PixelFormat depth_format;
};
/** /**
* Information about a graphics/compute pipeline * Information about a graphics/compute pipeline
*/ */
struct PipelineInfo { struct PipelineInfo {
VertexLayout vertex_layout{}; VertexLayout vertex_layout{};
BlendingState blending{}; BlendingState blending{};
VideoCore::PixelFormat color_attachment = VideoCore::PixelFormat::RGBA8; AttachmentInfo attachments{};
VideoCore::PixelFormat depth_attachment = VideoCore::PixelFormat::D24S8;
RasterizationState rasterization{}; RasterizationState rasterization{};
DepthStencilState depth_stencil{}; DepthStencilState depth_stencil{};
DynamicState dynamic; DynamicState dynamic;
/// Returns the hash of the info structure
u64 Hash(const Instance& instance) const;
[[nodiscard]] bool IsDepthWriteEnabled() const noexcept { [[nodiscard]] bool IsDepthWriteEnabled() const noexcept {
const bool has_stencil = depth_attachment == VideoCore::PixelFormat::D24S8; const bool has_stencil = attachments.depth_format == VideoCore::PixelFormat::D24S8;
const bool depth_write = const bool depth_write =
depth_stencil.depth_test_enable && depth_stencil.depth_write_enable; depth_stencil.depth_test_enable && depth_stencil.depth_write_enable;
const bool stencil_write = const bool stencil_write =

View File

@ -467,7 +467,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
const bool shadow_rendering = regs.framebuffer.IsShadowRendering(); const bool shadow_rendering = regs.framebuffer.IsShadowRendering();
const bool has_stencil = regs.framebuffer.HasStencil(); const bool has_stencil = regs.framebuffer.HasStencil();
const bool write_color_fb = shadow_rendering || pipeline_info.blending.color_write_mask.Value(); const bool write_color_fb = shadow_rendering || pipeline_info.blending.color_write_mask;
const bool write_depth_fb = pipeline_info.IsDepthWriteEnabled(); const bool write_depth_fb = pipeline_info.IsDepthWriteEnabled();
const bool using_color_fb = const bool using_color_fb =
regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && write_color_fb; regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && write_color_fb;
@ -485,9 +485,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
return true; return true;
} }
pipeline_info.color_attachment = pipeline_info.attachments.color_format =
color_surface ? color_surface->pixel_format : VideoCore::PixelFormat::Invalid; color_surface ? color_surface->pixel_format : VideoCore::PixelFormat::Invalid;
pipeline_info.depth_attachment = pipeline_info.attachments.depth_format =
depth_surface ? depth_surface->pixel_format : VideoCore::PixelFormat::Invalid; depth_surface ? depth_surface->pixel_format : VideoCore::PixelFormat::Invalid;
const u16 res_scale = color_surface != nullptr const u16 res_scale = color_surface != nullptr
@ -693,8 +693,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
const FramebufferInfo framebuffer_info = { const FramebufferInfo framebuffer_info = {
.color = color_surface ? color_surface->GetFramebufferView() : VK_NULL_HANDLE, .color = color_surface ? color_surface->GetFramebufferView() : VK_NULL_HANDLE,
.depth = depth_surface ? depth_surface->GetFramebufferView() : VK_NULL_HANDLE, .depth = depth_surface ? depth_surface->GetFramebufferView() : VK_NULL_HANDLE,
.renderpass = renderpass_cache.GetRenderpass(pipeline_info.color_attachment, .renderpass = renderpass_cache.GetRenderpass(pipeline_info.attachments.color_format,
pipeline_info.depth_attachment, false), pipeline_info.attachments.depth_format, false),
.width = width, .width = width,
.height = height, .height = height,
}; };
@ -1149,8 +1149,8 @@ void RasterizerVulkan::SyncCullMode() {
} }
void RasterizerVulkan::SyncBlendEnabled() { void RasterizerVulkan::SyncBlendEnabled() {
pipeline_info.blending.blend_enable.Assign( pipeline_info.blending.blend_enable =
Pica::g_state.regs.framebuffer.output_merger.alphablend_enable); Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
} }
void RasterizerVulkan::SyncBlendFuncs() { void RasterizerVulkan::SyncBlendFuncs() {
@ -1182,7 +1182,7 @@ void RasterizerVulkan::SyncLogicOp() {
} }
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
pipeline_info.blending.logic_op.Assign(regs.framebuffer.output_merger.logic_op); pipeline_info.blending.logic_op = regs.framebuffer.output_merger.logic_op;
const bool is_logic_op_emulated = const bool is_logic_op_emulated =
instance.NeedsLogicOpEmulation() && !regs.framebuffer.output_merger.alphablend_enable; instance.NeedsLogicOpEmulation() && !regs.framebuffer.output_merger.alphablend_enable;
@ -1191,7 +1191,7 @@ void RasterizerVulkan::SyncLogicOp() {
if (is_logic_op_emulated && is_logic_op_noop) { if (is_logic_op_emulated && is_logic_op_noop) {
// Color output is disabled by logic operation. We use color write mask to skip // Color output is disabled by logic operation. We use color write mask to skip
// color but allow depth write. // color but allow depth write.
pipeline_info.blending.color_write_mask.Assign(0); pipeline_info.blending.color_write_mask = 0;
} }
} }
@ -1211,7 +1211,7 @@ void RasterizerVulkan::SyncColorWriteMask() {
return; return;
} }
pipeline_info.blending.color_write_mask.Assign(color_mask); pipeline_info.blending.color_write_mask = color_mask;
} }
void RasterizerVulkan::SyncStencilWriteMask() { void RasterizerVulkan::SyncStencilWriteMask() {