renderer_vulkan: Support additional dynamic states
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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() {
|
||||
|
Reference in New Issue
Block a user