Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental

This commit is contained in:
Gliniak
2025-08-15 15:37:50 +02:00
78 changed files with 3438 additions and 2888 deletions

View File

@@ -46,15 +46,15 @@ VulkanPipelineCache::VulkanPipelineCache(
VulkanPipelineCache::~VulkanPipelineCache() { Shutdown(); }
bool VulkanPipelineCache::Initialize() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanDevice* const vulkan_device =
command_processor_.GetVulkanDevice();
bool edram_fragment_shader_interlock =
render_target_cache_.GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
SpirvShaderTranslator::Features(provider.device_info()),
SpirvShaderTranslator::Features(vulkan_device),
render_target_cache_.msaa_2x_attachments_supported(),
render_target_cache_.msaa_2x_no_attachments_supported(),
edram_fragment_shader_interlock);
@@ -63,7 +63,7 @@ bool VulkanPipelineCache::Initialize() {
std::vector<uint8_t> depth_only_fragment_shader_code =
shader_translator_->CreateDepthOnlyFragmentShader();
depth_only_fragment_shader_ = ui::vulkan::util::CreateShaderModule(
provider,
vulkan_device,
reinterpret_cast<const uint32_t*>(
depth_only_fragment_shader_code.data()),
depth_only_fragment_shader_code.size());
@@ -80,10 +80,10 @@ bool VulkanPipelineCache::Initialize() {
}
void VulkanPipelineCache::Shutdown() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
const ui::vulkan::VulkanDevice* const vulkan_device =
command_processor_.GetVulkanDevice();
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
const VkDevice device = vulkan_device->device();
// Destroy all pipelines.
last_pipeline_ = nullptr;
@@ -131,7 +131,7 @@ VulkanShader* VulkanPipelineCache::LoadShader(xenos::ShaderType shader_type,
// We need to track it even if it fails translation so we know not to try
// again.
VulkanShader* shader =
new VulkanShader(command_processor_.GetVulkanProvider(), shader_type,
new VulkanShader(command_processor_.GetVulkanDevice(), shader_type,
data_hash, host_address, dword_count);
shaders_.emplace(data_hash, shader);
return shader;
@@ -263,9 +263,9 @@ bool VulkanPipelineCache::ConfigurePipeline(
VulkanRenderTargetCache::RenderPassKey render_pass_key,
VkPipeline& pipeline_out,
const PipelineLayoutProvider*& pipeline_layout_out) {
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
// Ensure shaders are translated - needed now for GetCurrentStateDescription.
if (!EnsureShadersTranslated(vertex_shader, pixel_shader)) {
@@ -466,8 +466,8 @@ void VulkanPipelineCache::WritePipelineRenderTargetDescription(
render_target_out.dst_alpha_blend_factor =
kBlendFactorMap[uint32_t(blend_control.alpha_destblend)];
render_target_out.alpha_blend_op = blend_control.alpha_comb_fcn;
if (!command_processor_.GetVulkanProvider()
.device_info()
if (!command_processor_.GetVulkanDevice()
->properties()
.constantAlphaColorBlendFactors) {
if (blend_control.color_srcblend == xenos::BlendFactor::kConstantAlpha) {
render_target_out.src_color_blend_factor =
@@ -507,8 +507,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
PipelineDescription& description_out) const {
description_out.Reset();
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
command_processor_.GetVulkanProvider().device_info();
const ui::vulkan::VulkanDevice::Properties& device_properties =
command_processor_.GetVulkanDevice()->properties();
const RegisterFile& regs = register_file_;
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
@@ -543,7 +543,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
break;
case xenos::PrimitiveType::kTriangleFan:
// The check should be performed at primitive processing time.
assert_true(device_info.triangleFans);
assert_true(device_properties.triangleFans);
primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
break;
case xenos::PrimitiveType::kTriangleStrip:
@@ -567,7 +567,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
primitive_processing_result.host_primitive_reset_enabled;
description_out.depth_clamp_enable =
device_info.depthClamp && regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
device_properties.depthClamp &&
regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
// TODO(Triang3l): Tessellation.
bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);
@@ -582,7 +583,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
bool cull_back = pa_su_sc_mode_cntl.cull_back;
description_out.cull_front = cull_front;
description_out.cull_back = cull_back;
if (device_info.fillModeNonSolid) {
if (device_properties.fillModeNonSolid) {
xenos::PolygonType polygon_type = xenos::PolygonType::kTriangles;
if (!cull_front) {
polygon_type =
@@ -599,7 +600,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
case xenos::PolygonType::kPoints:
// When points are not supported, use lines instead, preserving
// debug-like purpose.
description_out.polygon_mode = device_info.pointPolygons
description_out.polygon_mode = device_properties.pointPolygons
? PipelinePolygonMode::kPoint
: PipelinePolygonMode::kLine;
break;
@@ -666,83 +667,17 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
// Color blending and write masks (filled only for the attachments present
// in the render pass object).
uint32_t render_pass_color_rts = render_pass_key.depth_and_color_used >> 1;
if (device_info.independentBlend) {
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
uint32_t color_rt_index;
while (xe::bit_scan_forward(render_pass_color_rts_remaining,
&color_rt_index)) {
render_pass_color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
WritePipelineRenderTargetDescription(
regs.Get<reg::RB_BLENDCONTROL>(
reg::RB_BLENDCONTROL::rt_register_indices[color_rt_index]),
(normalized_color_mask >> (color_rt_index * 4)) & 0b1111,
description_out.render_targets[color_rt_index]);
}
} else {
// Take the blend control for the first render target that the guest wants
// to write to (consider it the most important) and use it for all render
// targets, if any.
// TODO(Triang3l): Implement an option for independent blending via
// replaying the render pass for each set of render targets with unique
// blending parameters, with depth / stencil saved before the first and
// restored before each of the rest maybe? Though independent blending
// support is pretty wide, with a quite prominent exception of Adreno 4xx
// apparently.
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
uint32_t render_pass_first_color_rt_index;
if (xe::bit_scan_forward(render_pass_color_rts_remaining,
&render_pass_first_color_rt_index)) {
render_pass_color_rts_remaining &=
~(uint32_t(1) << render_pass_first_color_rt_index);
PipelineRenderTarget& render_pass_first_color_rt =
description_out.render_targets[render_pass_first_color_rt_index];
uint32_t common_blend_rt_index;
if (xe::bit_scan_forward(normalized_color_mask,
&common_blend_rt_index)) {
common_blend_rt_index >>= 2;
// If a common write mask will be used for multiple render targets,
// use the original RB_COLOR_MASK instead of the normalized color mask
// as the normalized color mask has non-existent components forced to
// written (don't need reading to be preserved), while the number of
// components may vary between render targets. The attachments in the
// pass that must not be written to at all will be excluded via a
// shader modification.
WritePipelineRenderTargetDescription(
regs.Get<reg::RB_BLENDCONTROL>(
reg::RB_BLENDCONTROL::rt_register_indices
[common_blend_rt_index]),
(((normalized_color_mask &
~(uint32_t(0b1111) << (4 * common_blend_rt_index)))
? regs[XE_GPU_REG_RB_COLOR_MASK]
: normalized_color_mask) >>
(4 * common_blend_rt_index)) &
0b1111,
render_pass_first_color_rt);
} else {
// No render targets are written to, though the render pass still may
// contain color attachments - set them to not written and not
// blending.
render_pass_first_color_rt.src_color_blend_factor =
PipelineBlendFactor::kOne;
render_pass_first_color_rt.dst_color_blend_factor =
PipelineBlendFactor::kZero;
render_pass_first_color_rt.color_blend_op = xenos::BlendOp::kAdd;
render_pass_first_color_rt.src_alpha_blend_factor =
PipelineBlendFactor::kOne;
render_pass_first_color_rt.dst_alpha_blend_factor =
PipelineBlendFactor::kZero;
render_pass_first_color_rt.alpha_blend_op = xenos::BlendOp::kAdd;
}
// Reuse the same blending settings for all render targets in the pass,
// for description consistency.
uint32_t color_rt_index;
while (xe::bit_scan_forward(render_pass_color_rts_remaining,
&color_rt_index)) {
render_pass_color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
description_out.render_targets[color_rt_index] =
render_pass_first_color_rt;
}
}
assert_true(device_properties.independentBlend);
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
uint32_t color_rt_index;
while (xe::bit_scan_forward(render_pass_color_rts_remaining,
&color_rt_index)) {
render_pass_color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
WritePipelineRenderTargetDescription(
regs.Get<reg::RB_BLENDCONTROL>(
reg::RB_BLENDCONTROL::rt_register_indices[color_rt_index]),
(normalized_color_mask >> (color_rt_index * 4)) & 0b1111,
description_out.render_targets[color_rt_index]);
}
}
@@ -762,65 +697,37 @@ bool VulkanPipelineCache::ArePipelineRequirementsMet(
return false;
}
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
command_processor_.GetVulkanProvider().device_info();
const ui::vulkan::VulkanDevice::Properties& device_properties =
command_processor_.GetVulkanDevice()->properties();
if (!device_info.geometryShader &&
if (!device_properties.geometryShader &&
description.geometry_shader != PipelineGeometryShader::kNone) {
return false;
}
if (!device_info.triangleFans &&
if (!device_properties.triangleFans &&
description.primitive_topology ==
PipelinePrimitiveTopology::kTriangleFan) {
return false;
}
if (!device_info.depthClamp && description.depth_clamp_enable) {
if (!device_properties.depthClamp && description.depth_clamp_enable) {
return false;
}
if (!device_info.pointPolygons &&
if (!device_properties.pointPolygons &&
description.polygon_mode == PipelinePolygonMode::kPoint) {
return false;
}
if (!device_info.fillModeNonSolid &&
if (!device_properties.fillModeNonSolid &&
description.polygon_mode != PipelinePolygonMode::kFill) {
return false;
}
if (!device_info.independentBlend) {
uint32_t color_rts_remaining =
description.render_pass_key.depth_and_color_used >> 1;
uint32_t first_color_rt_index;
if (xe::bit_scan_forward(color_rts_remaining, &first_color_rt_index)) {
color_rts_remaining &= ~(uint32_t(1) << first_color_rt_index);
const PipelineRenderTarget& first_color_rt =
description.render_targets[first_color_rt_index];
uint32_t color_rt_index;
while (xe::bit_scan_forward(color_rts_remaining, &color_rt_index)) {
color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
const PipelineRenderTarget& color_rt =
description.render_targets[color_rt_index];
if (color_rt.src_color_blend_factor !=
first_color_rt.src_color_blend_factor ||
color_rt.dst_color_blend_factor !=
first_color_rt.dst_color_blend_factor ||
color_rt.color_blend_op != first_color_rt.color_blend_op ||
color_rt.src_alpha_blend_factor !=
first_color_rt.src_alpha_blend_factor ||
color_rt.dst_alpha_blend_factor !=
first_color_rt.dst_alpha_blend_factor ||
color_rt.alpha_blend_op != first_color_rt.alpha_blend_op ||
color_rt.color_write_mask != first_color_rt.color_write_mask) {
return false;
}
}
}
}
assert_true(device_properties.independentBlend);
if (!device_info.constantAlphaColorBlendFactors) {
if (!device_properties.constantAlphaColorBlendFactors) {
uint32_t color_rts_remaining =
description.render_pass_key.depth_and_color_used >> 1;
uint32_t color_rt_index;
@@ -1844,10 +1751,9 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
// Create the shader module, and store the handle even if creation fails not
// to try to create it again later.
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanProvider();
VkShaderModule shader_module = ui::vulkan::util::CreateShaderModule(
provider, reinterpret_cast<const uint32_t*>(shader_code.data()),
command_processor_.GetVulkanDevice(),
reinterpret_cast<const uint32_t*>(shader_code.data()),
sizeof(uint32_t) * shader_code.size());
if (shader_module == VK_NULL_HANDLE) {
XELOGE(
@@ -1887,10 +1793,8 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
return false;
}
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanProvider();
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
provider.device_info();
const ui::vulkan::VulkanDevice* const vulkan_device =
command_processor_.GetVulkanDevice();
bool edram_fragment_shader_interlock =
render_target_cache_.GetPath() ==
@@ -2199,28 +2103,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
}
color_blend_attachment.colorWriteMask =
VkColorComponentFlags(color_rt.color_write_mask);
if (!device_info.independentBlend) {
// For non-independent blend, the pAttachments element for the first
// actually used color will be replicated into all.
break;
}
}
}
color_blend_state.attachmentCount = 32 - xe::lzcnt(color_rts_used);
color_blend_state.pAttachments = color_blend_attachments;
if (color_rts_used && !device_info.independentBlend) {
// "If the independent blending feature is not enabled, all elements of
// pAttachments must be identical."
uint32_t first_color_rt_index;
xe::bit_scan_forward(color_rts_used, &first_color_rt_index);
for (uint32_t i = 0; i < color_blend_state.attachmentCount; ++i) {
if (i == first_color_rt_index) {
continue;
}
color_blend_attachments[i] =
color_blend_attachments[first_color_rt_index];
}
}
}
std::array<VkDynamicState, 7> dynamic_states;
@@ -2271,8 +2157,8 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.basePipelineIndex = -1;
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
VkDevice device = provider.device();
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
const VkDevice device = vulkan_device->device();
VkPipeline pipeline;
if (dfn.vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1,
&pipeline_create_info, nullptr,