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
* 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);
}

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/container/static_vector.hpp>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
@ -59,6 +60,37 @@ vk::ShaderStageFlagBits MakeShaderStage(std::size_t index) {
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, vk::ShaderStageFlagBits stage,
@ -80,8 +112,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline(
Common::ThreadWorker* worker_)
: instance{instance_}, worker{worker_}, pipeline_layout{layout_},
pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_},
renderpass{
renderpass_cache_.GetRenderpass(info.color_attachment, info.depth_attachment, false)} {
renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format,
info.attachments.depth_format, false)} {
// Ask the driver if it can give us the pipeline quickly
if (Build(true)) {
@ -170,7 +202,7 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
};
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),
.dstColorBlendFactor = PicaToVK::BlendFunc(info.blending.dst_color_blend_factor),
.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 = {
.logicOpEnable = !info.blending.blend_enable.Value() && !instance.NeedsLogicOpEmulation(),
.logicOp = PicaToVK::LogicOp(info.blending.logic_op.Value()),
.logicOpEnable = !info.blending.blend_enable && !instance.NeedsLogicOpEmulation(),
.logicOp = PicaToVK::LogicOp(info.blending.logic_op),
.attachmentCount = 1,
.pAttachments = &colorblend_attachment,
.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,
};
const bool extended_dynamic_states = instance.IsExtendedDynamicStateSupported();
const std::array dynamic_states = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
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,
boost::container::static_vector<vk::DynamicState, 20> dynamic_states = {
vk::DynamicState::eViewport, vk::DynamicState::eScissor,
vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference, vk::DynamicState::eBlendConstants,
};
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 = {
.dynamicStateCount = extended_dynamic_states ? static_cast<u32>(dynamic_states.size()) : 6u,
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
.pDynamicStates = dynamic_states.data(),
};
@ -393,17 +437,14 @@ void PipelineCache::SaveDiskCache() {
MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32));
bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
MICROPROFILE_SCOPE(Vulkan_Bind);
std::size_t shader_hash = 0;
u64 shader_hash = 0;
for (u32 i = 0; i < MAX_SHADER_STAGES; i++) {
shader_hash = Common::HashCombine(shader_hash, shader_hashes[i]);
}
const u64 info_hash_size = instance.IsExtendedDynamicStateSupported()
? offsetof(PipelineInfo, rasterization)
: offsetof(PipelineInfo, dynamic);
u64 info_hash = Common::ComputeHash64(&info, info_hash_size);
u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash);
const u64 info_hash = info.Hash(instance);
const u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash);
auto [it, new_pipeline] = graphics_pipelines.try_emplace(pipeline_hash);
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) {
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,
dynamic = info.dynamic](vk::CommandBuffer cmdbuf) {
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;
}

View File

@ -45,17 +45,19 @@ union DepthStencilState {
BitField<15, 3, Pica::FramebufferRegs::CompareFunc> stencil_compare_op;
};
union BlendingState {
u32 value = 0;
BitField<0, 1, u32> blend_enable;
BitField<1, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor;
BitField<5, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor;
BitField<9, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq;
BitField<12, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor;
BitField<16, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor;
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 BlendingState {
u16 blend_enable;
u16 color_write_mask;
Pica::FramebufferRegs::LogicOp logic_op;
union {
u32 value = 0;
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;
BitField<11, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor;
BitField<15, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor;
BitField<19, 3, Pica::FramebufferRegs::BlendEquation> alpha_blend_eq;
};
};
struct DynamicState {
@ -90,20 +92,27 @@ struct VertexLayout {
std::array<VertexAttribute, MAX_VERTEX_ATTRIBUTES> attributes;
};
struct AttachmentInfo {
VideoCore::PixelFormat color_format;
VideoCore::PixelFormat depth_format;
};
/**
* Information about a graphics/compute pipeline
*/
struct PipelineInfo {
VertexLayout vertex_layout{};
BlendingState blending{};
VideoCore::PixelFormat color_attachment = VideoCore::PixelFormat::RGBA8;
VideoCore::PixelFormat depth_attachment = VideoCore::PixelFormat::D24S8;
AttachmentInfo attachments{};
RasterizationState rasterization{};
DepthStencilState depth_stencil{};
DynamicState dynamic;
/// Returns the hash of the info structure
u64 Hash(const Instance& instance) const;
[[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 =
depth_stencil.depth_test_enable && depth_stencil.depth_write_enable;
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 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 using_color_fb =
regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && write_color_fb;
@ -485,9 +485,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
return true;
}
pipeline_info.color_attachment =
pipeline_info.attachments.color_format =
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;
const u16 res_scale = color_surface != nullptr
@ -693,8 +693,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
const FramebufferInfo framebuffer_info = {
.color = color_surface ? color_surface->GetFramebufferView() : VK_NULL_HANDLE,
.depth = depth_surface ? depth_surface->GetFramebufferView() : VK_NULL_HANDLE,
.renderpass = renderpass_cache.GetRenderpass(pipeline_info.color_attachment,
pipeline_info.depth_attachment, false),
.renderpass = renderpass_cache.GetRenderpass(pipeline_info.attachments.color_format,
pipeline_info.attachments.depth_format, false),
.width = width,
.height = height,
};
@ -1149,8 +1149,8 @@ void RasterizerVulkan::SyncCullMode() {
}
void RasterizerVulkan::SyncBlendEnabled() {
pipeline_info.blending.blend_enable.Assign(
Pica::g_state.regs.framebuffer.output_merger.alphablend_enable);
pipeline_info.blending.blend_enable =
Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
}
void RasterizerVulkan::SyncBlendFuncs() {
@ -1182,7 +1182,7 @@ void RasterizerVulkan::SyncLogicOp() {
}
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 =
instance.NeedsLogicOpEmulation() && !regs.framebuffer.output_merger.alphablend_enable;
@ -1191,7 +1191,7 @@ void RasterizerVulkan::SyncLogicOp() {
if (is_logic_op_emulated && is_logic_op_noop) {
// Color output is disabled by logic operation. We use color write mask to skip
// 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;
}
pipeline_info.blending.color_write_mask.Assign(color_mask);
pipeline_info.blending.color_write_mask = color_mask;
}
void RasterizerVulkan::SyncStencilWriteMask() {