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

This commit is contained in:
Gliniak
2022-10-14 20:13:03 +02:00
24 changed files with 6168 additions and 1531 deletions

View File

@@ -68,9 +68,6 @@ const VkDescriptorPoolSize
{VK_DESCRIPTOR_TYPE_SAMPLER, kLinkedTypeDescriptorPoolSetCount},
};
// No specific reason for 32768 descriptors, just the "too much" amount from
// Direct3D 12 PIX warnings. 2x descriptors for textures because of unsigned and
// signed bindings.
VulkanCommandProcessor::VulkanCommandProcessor(
VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state)
: CommandProcessor(graphics_system, kernel_state),
@@ -107,6 +104,32 @@ void VulkanCommandProcessor::TracePlaybackWroteMemory(uint32_t base_ptr,
void VulkanCommandProcessor::RestoreEdramSnapshot(const void* snapshot) {}
std::string VulkanCommandProcessor::GetWindowTitleText() const {
std::ostringstream title;
title << "Vulkan";
if (render_target_cache_) {
switch (render_target_cache_->GetPath()) {
case RenderTargetCache::Path::kHostRenderTargets:
title << " - FBO";
break;
case RenderTargetCache::Path::kPixelShaderInterlock:
title << " - FSI";
break;
default:
break;
}
uint32_t draw_resolution_scale_x =
texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1;
uint32_t draw_resolution_scale_y =
texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1;
if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) {
title << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y;
}
}
title << " - HEAVILY INCOMPLETE, early development";
return title.str();
}
bool VulkanCommandProcessor::SetupContext() {
if (!CommandProcessor::SetupContext()) {
XELOGE("Failed to initialize base command processor context");
@@ -147,7 +170,7 @@ bool VulkanCommandProcessor::SetupContext() {
size_t(16384)),
size_t(uniform_buffer_alignment)));
// Descriptor set layouts.
// Descriptor set layouts that don't depend on the setup of other subsystems.
VkShaderStageFlags guest_shader_stages =
guest_shader_vertex_stages_ | VK_SHADER_STAGE_FRAGMENT_BIT;
// Empty.
@@ -164,37 +187,6 @@ bool VulkanCommandProcessor::SetupContext() {
XELOGE("Failed to create an empty Vulkan descriptor set layout");
return false;
}
// Shared memory and EDRAM.
uint32_t shared_memory_binding_count_log2 =
SpirvShaderTranslator::GetSharedMemoryStorageBufferCountLog2(
provider.device_properties().limits.maxStorageBufferRange);
uint32_t shared_memory_binding_count = UINT32_C(1)
<< shared_memory_binding_count_log2;
VkDescriptorSetLayoutBinding
descriptor_set_layout_bindings_shared_memory_and_edram[1];
descriptor_set_layout_bindings_shared_memory_and_edram[0].binding = 0;
descriptor_set_layout_bindings_shared_memory_and_edram[0].descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptor_set_layout_bindings_shared_memory_and_edram[0].descriptorCount =
shared_memory_binding_count;
descriptor_set_layout_bindings_shared_memory_and_edram[0].stageFlags =
guest_shader_stages;
descriptor_set_layout_bindings_shared_memory_and_edram[0].pImmutableSamplers =
nullptr;
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
// interlocks case.
descriptor_set_layout_create_info.bindingCount = uint32_t(
xe::countof(descriptor_set_layout_bindings_shared_memory_and_edram));
descriptor_set_layout_create_info.pBindings =
descriptor_set_layout_bindings_shared_memory_and_edram;
if (dfn.vkCreateDescriptorSetLayout(
device, &descriptor_set_layout_create_info, nullptr,
&descriptor_set_layout_shared_memory_and_edram_) != VK_SUCCESS) {
XELOGE(
"Failed to create a Vulkan descriptor set layout for the shared memory "
"and the EDRAM");
return false;
}
// Guest draw constants.
VkDescriptorSetLayoutBinding descriptor_set_layout_bindings_constants
[SpirvShaderTranslator::kConstantBufferCount] = {};
@@ -290,16 +282,70 @@ bool VulkanCommandProcessor::SetupContext() {
return false;
}
uint32_t shared_memory_binding_count_log2 =
SpirvShaderTranslator::GetSharedMemoryStorageBufferCountLog2(
provider.device_properties().limits.maxStorageBufferRange);
uint32_t shared_memory_binding_count = UINT32_C(1)
<< shared_memory_binding_count_log2;
// Requires the transient descriptor set layouts.
// TODO(Triang3l): Get the actual draw resolution scale when the texture cache
// supports resolution scaling.
render_target_cache_ = std::make_unique<VulkanRenderTargetCache>(
*register_file_, *memory_, trace_writer_, 1, 1, *this);
if (!render_target_cache_->Initialize()) {
if (!render_target_cache_->Initialize(shared_memory_binding_count)) {
XELOGE("Failed to initialize the render target cache");
return false;
}
// Shared memory and EDRAM descriptor set layout.
bool edram_fragment_shader_interlock =
render_target_cache_->GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
VkDescriptorSetLayoutBinding
shared_memory_and_edram_descriptor_set_layout_bindings[2];
shared_memory_and_edram_descriptor_set_layout_bindings[0].binding = 0;
shared_memory_and_edram_descriptor_set_layout_bindings[0].descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
shared_memory_and_edram_descriptor_set_layout_bindings[0].descriptorCount =
shared_memory_binding_count;
shared_memory_and_edram_descriptor_set_layout_bindings[0].stageFlags =
guest_shader_stages;
shared_memory_and_edram_descriptor_set_layout_bindings[0].pImmutableSamplers =
nullptr;
VkDescriptorSetLayoutCreateInfo
shared_memory_and_edram_descriptor_set_layout_create_info;
shared_memory_and_edram_descriptor_set_layout_create_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
shared_memory_and_edram_descriptor_set_layout_create_info.pNext = nullptr;
shared_memory_and_edram_descriptor_set_layout_create_info.flags = 0;
shared_memory_and_edram_descriptor_set_layout_create_info.pBindings =
shared_memory_and_edram_descriptor_set_layout_bindings;
if (edram_fragment_shader_interlock) {
// EDRAM.
shared_memory_and_edram_descriptor_set_layout_bindings[1].binding = 1;
shared_memory_and_edram_descriptor_set_layout_bindings[1].descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
shared_memory_and_edram_descriptor_set_layout_bindings[1].descriptorCount =
1;
shared_memory_and_edram_descriptor_set_layout_bindings[1].stageFlags =
VK_SHADER_STAGE_FRAGMENT_BIT;
shared_memory_and_edram_descriptor_set_layout_bindings[1]
.pImmutableSamplers = nullptr;
shared_memory_and_edram_descriptor_set_layout_create_info.bindingCount = 2;
} else {
shared_memory_and_edram_descriptor_set_layout_create_info.bindingCount = 1;
}
if (dfn.vkCreateDescriptorSetLayout(
device, &shared_memory_and_edram_descriptor_set_layout_create_info,
nullptr,
&descriptor_set_layout_shared_memory_and_edram_) != VK_SUCCESS) {
XELOGE(
"Failed to create a Vulkan descriptor set layout for the shared memory "
"and the EDRAM");
return false;
}
pipeline_cache_ = std::make_unique<VulkanPipelineCache>(
*this, *register_file_, *render_target_cache_,
guest_shader_vertex_stages_);
@@ -321,9 +367,8 @@ bool VulkanCommandProcessor::SetupContext() {
// Shared memory and EDRAM common bindings.
VkDescriptorPoolSize descriptor_pool_sizes[1];
descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptor_pool_sizes[0].descriptorCount = shared_memory_binding_count;
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
// interlocks case.
descriptor_pool_sizes[0].descriptorCount =
shared_memory_binding_count + uint32_t(edram_fragment_shader_interlock);
VkDescriptorPoolCreateInfo descriptor_pool_create_info;
descriptor_pool_create_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
@@ -370,20 +415,45 @@ bool VulkanCommandProcessor::SetupContext() {
shared_memory_binding_range * i;
shared_memory_descriptor_buffer_info.range = shared_memory_binding_range;
}
VkWriteDescriptorSet write_descriptor_sets[1];
write_descriptor_sets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_sets[0].pNext = nullptr;
write_descriptor_sets[0].dstSet = shared_memory_and_edram_descriptor_set_;
write_descriptor_sets[0].dstBinding = 0;
write_descriptor_sets[0].dstArrayElement = 0;
write_descriptor_sets[0].descriptorCount = shared_memory_binding_count;
write_descriptor_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write_descriptor_sets[0].pImageInfo = nullptr;
write_descriptor_sets[0].pBufferInfo = shared_memory_descriptor_buffers_info;
write_descriptor_sets[0].pTexelBufferView = nullptr;
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
// interlocks case.
dfn.vkUpdateDescriptorSets(device, 1, write_descriptor_sets, 0, nullptr);
VkWriteDescriptorSet write_descriptor_sets[2];
VkWriteDescriptorSet& write_descriptor_set_shared_memory =
write_descriptor_sets[0];
write_descriptor_set_shared_memory.sType =
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set_shared_memory.pNext = nullptr;
write_descriptor_set_shared_memory.dstSet =
shared_memory_and_edram_descriptor_set_;
write_descriptor_set_shared_memory.dstBinding = 0;
write_descriptor_set_shared_memory.dstArrayElement = 0;
write_descriptor_set_shared_memory.descriptorCount =
shared_memory_binding_count;
write_descriptor_set_shared_memory.descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write_descriptor_set_shared_memory.pImageInfo = nullptr;
write_descriptor_set_shared_memory.pBufferInfo =
shared_memory_descriptor_buffers_info;
write_descriptor_set_shared_memory.pTexelBufferView = nullptr;
VkDescriptorBufferInfo edram_descriptor_buffer_info;
if (edram_fragment_shader_interlock) {
edram_descriptor_buffer_info.buffer = render_target_cache_->edram_buffer();
edram_descriptor_buffer_info.offset = 0;
edram_descriptor_buffer_info.range = VK_WHOLE_SIZE;
VkWriteDescriptorSet& write_descriptor_set_edram = write_descriptor_sets[1];
write_descriptor_set_edram.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set_edram.pNext = nullptr;
write_descriptor_set_edram.dstSet = shared_memory_and_edram_descriptor_set_;
write_descriptor_set_edram.dstBinding = 1;
write_descriptor_set_edram.dstArrayElement = 0;
write_descriptor_set_edram.descriptorCount = 1;
write_descriptor_set_edram.descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write_descriptor_set_edram.pImageInfo = nullptr;
write_descriptor_set_edram.pBufferInfo = &edram_descriptor_buffer_info;
write_descriptor_set_edram.pTexelBufferView = nullptr;
}
dfn.vkUpdateDescriptorSets(device,
1 + uint32_t(edram_fragment_shader_interlock),
write_descriptor_sets, 0, nullptr);
// Swap objects.
@@ -1042,6 +1112,9 @@ void VulkanCommandProcessor::ShutdownContext() {
}
descriptor_set_layouts_textures_.clear();
ui::vulkan::util::DestroyAndNullHandle(
dfn.vkDestroyDescriptorSetLayout, device,
descriptor_set_layout_shared_memory_and_edram_);
for (VkDescriptorSetLayout& descriptor_set_layout_single_transient :
descriptor_set_layouts_single_transient_) {
ui::vulkan::util::DestroyAndNullHandle(
@@ -1051,9 +1124,6 @@ void VulkanCommandProcessor::ShutdownContext() {
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout,
device,
descriptor_set_layout_constants_);
ui::vulkan::util::DestroyAndNullHandle(
dfn.vkDestroyDescriptorSetLayout, device,
descriptor_set_layout_shared_memory_and_edram_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout,
device, descriptor_set_layout_empty_);
@@ -2415,7 +2485,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// Update system constants before uploading them.
UpdateSystemConstantValues(primitive_polygonal, primitive_processing_result,
shader_32bit_index_dma, viewport_info,
used_texture_mask);
used_texture_mask, normalized_depth_control,
normalized_color_mask);
// Update uniform buffers and descriptor sets after binding the pipeline with
// the new layout.
@@ -2475,6 +2546,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// After all commands that may dispatch, copy or insert barriers, submit the
// barriers (may end the render pass), and (re)enter the render pass before
// drawing.
// TODO(Triang3l): Handle disabled variableMultisampleRate by restarting the
// render pass with no attachments if the sample count becomes different.
SubmitBarriersAndEnterRenderTargetCacheRenderPass(
render_target_cache_->last_update_render_pass(),
render_target_cache_->last_update_framebuffer());
@@ -3194,175 +3267,180 @@ void VulkanCommandProcessor::UpdateDynamicState(
scissor_rect.extent.height = scissor.extent[1];
SetScissor(scissor_rect);
// Depth bias.
// TODO(Triang3l): Disable the depth bias for the fragment shader interlock RB
// implementation.
float depth_bias_constant_factor, depth_bias_slope_factor;
draw_util::GetPreferredFacePolygonOffset(regs, primitive_polygonal,
depth_bias_slope_factor,
depth_bias_constant_factor);
depth_bias_constant_factor *=
regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24S8
? draw_util::kD3D10PolygonOffsetFactorUnorm24
: draw_util::kD3D10PolygonOffsetFactorFloat24;
// With non-square resolution scaling, make sure the worst-case impact is
// reverted (slope only along the scaled axis), thus max. More bias is better
// than less bias, because less bias means Z fighting with the background is
// more likely.
depth_bias_slope_factor *=
xenos::kPolygonOffsetScaleSubpixelUnit *
float(std::max(render_target_cache_->draw_resolution_scale_x(),
render_target_cache_->draw_resolution_scale_y()));
// std::memcmp instead of != so in case of NaN, every draw won't be
// invalidating it.
dynamic_depth_bias_update_needed_ |=
std::memcmp(&dynamic_depth_bias_constant_factor_,
&depth_bias_constant_factor, sizeof(float)) != 0;
dynamic_depth_bias_update_needed_ |=
std::memcmp(&dynamic_depth_bias_slope_factor_, &depth_bias_slope_factor,
sizeof(float)) != 0;
if (dynamic_depth_bias_update_needed_) {
dynamic_depth_bias_constant_factor_ = depth_bias_constant_factor;
dynamic_depth_bias_slope_factor_ = depth_bias_slope_factor;
deferred_command_buffer_.CmdVkSetDepthBias(
dynamic_depth_bias_constant_factor_, 0.0f,
dynamic_depth_bias_slope_factor_);
dynamic_depth_bias_update_needed_ = false;
}
if (render_target_cache_->GetPath() ==
RenderTargetCache::Path::kHostRenderTargets) {
// Depth bias.
float depth_bias_constant_factor, depth_bias_slope_factor;
draw_util::GetPreferredFacePolygonOffset(regs, primitive_polygonal,
depth_bias_slope_factor,
depth_bias_constant_factor);
depth_bias_constant_factor *=
regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24S8
? draw_util::kD3D10PolygonOffsetFactorUnorm24
: draw_util::kD3D10PolygonOffsetFactorFloat24;
// With non-square resolution scaling, make sure the worst-case impact is
// reverted (slope only along the scaled axis), thus max. More bias is
// better than less bias, because less bias means Z fighting with the
// background is more likely.
depth_bias_slope_factor *=
xenos::kPolygonOffsetScaleSubpixelUnit *
float(std::max(render_target_cache_->draw_resolution_scale_x(),
render_target_cache_->draw_resolution_scale_y()));
// std::memcmp instead of != so in case of NaN, every draw won't be
// invalidating it.
dynamic_depth_bias_update_needed_ |=
std::memcmp(&dynamic_depth_bias_constant_factor_,
&depth_bias_constant_factor, sizeof(float)) != 0;
dynamic_depth_bias_update_needed_ |=
std::memcmp(&dynamic_depth_bias_slope_factor_, &depth_bias_slope_factor,
sizeof(float)) != 0;
if (dynamic_depth_bias_update_needed_) {
dynamic_depth_bias_constant_factor_ = depth_bias_constant_factor;
dynamic_depth_bias_slope_factor_ = depth_bias_slope_factor;
deferred_command_buffer_.CmdVkSetDepthBias(
dynamic_depth_bias_constant_factor_, 0.0f,
dynamic_depth_bias_slope_factor_);
dynamic_depth_bias_update_needed_ = false;
}
// Blend constants.
float blend_constants[] = {
regs[XE_GPU_REG_RB_BLEND_RED].f32,
regs[XE_GPU_REG_RB_BLEND_GREEN].f32,
regs[XE_GPU_REG_RB_BLEND_BLUE].f32,
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32,
};
dynamic_blend_constants_update_needed_ |=
std::memcmp(dynamic_blend_constants_, blend_constants,
sizeof(float) * 4) != 0;
if (dynamic_blend_constants_update_needed_) {
std::memcpy(dynamic_blend_constants_, blend_constants, sizeof(float) * 4);
deferred_command_buffer_.CmdVkSetBlendConstants(dynamic_blend_constants_);
dynamic_blend_constants_update_needed_ = false;
}
// Blend constants.
float blend_constants[] = {
regs[XE_GPU_REG_RB_BLEND_RED].f32,
regs[XE_GPU_REG_RB_BLEND_GREEN].f32,
regs[XE_GPU_REG_RB_BLEND_BLUE].f32,
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32,
};
dynamic_blend_constants_update_needed_ |=
std::memcmp(dynamic_blend_constants_, blend_constants,
sizeof(float) * 4) != 0;
if (dynamic_blend_constants_update_needed_) {
std::memcpy(dynamic_blend_constants_, blend_constants, sizeof(float) * 4);
deferred_command_buffer_.CmdVkSetBlendConstants(dynamic_blend_constants_);
dynamic_blend_constants_update_needed_ = false;
}
// Stencil masks and references.
// Due to pretty complex conditions involving registers not directly related
// to stencil (primitive type, culling), changing the values only when stencil
// is actually needed. However, due to the way dynamic state needs to be set
// in Vulkan, which doesn't take into account whether the state actually has
// effect on drawing, and because the masks and the references are always
// dynamic in Xenia guest pipelines, they must be set in the command buffer
// before any draw.
if (normalized_depth_control.stencil_enable) {
Register stencil_ref_mask_front_reg, stencil_ref_mask_back_reg;
if (primitive_polygonal && normalized_depth_control.backface_enable) {
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
device_portability_subset_features =
provider.device_portability_subset_features();
if (!device_portability_subset_features ||
device_portability_subset_features->separateStencilMaskRef) {
// Choose the back face values only if drawing only back faces.
stencil_ref_mask_front_reg =
regs.Get<reg::PA_SU_SC_MODE_CNTL>().cull_front
? XE_GPU_REG_RB_STENCILREFMASK_BF
: XE_GPU_REG_RB_STENCILREFMASK;
stencil_ref_mask_back_reg = stencil_ref_mask_front_reg;
// Stencil masks and references.
// Due to pretty complex conditions involving registers not directly related
// to stencil (primitive type, culling), changing the values only when
// stencil is actually needed. However, due to the way dynamic state needs
// to be set in Vulkan, which doesn't take into account whether the state
// actually has effect on drawing, and because the masks and the references
// are always dynamic in Xenia guest pipelines, they must be set in the
// command buffer before any draw.
if (normalized_depth_control.stencil_enable) {
Register stencil_ref_mask_front_reg, stencil_ref_mask_back_reg;
if (primitive_polygonal && normalized_depth_control.backface_enable) {
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
device_portability_subset_features =
provider.device_portability_subset_features();
if (!device_portability_subset_features ||
device_portability_subset_features->separateStencilMaskRef) {
// Choose the back face values only if drawing only back faces.
stencil_ref_mask_front_reg =
regs.Get<reg::PA_SU_SC_MODE_CNTL>().cull_front
? XE_GPU_REG_RB_STENCILREFMASK_BF
: XE_GPU_REG_RB_STENCILREFMASK;
stencil_ref_mask_back_reg = stencil_ref_mask_front_reg;
} else {
stencil_ref_mask_front_reg = XE_GPU_REG_RB_STENCILREFMASK;
stencil_ref_mask_back_reg = XE_GPU_REG_RB_STENCILREFMASK_BF;
}
} else {
stencil_ref_mask_front_reg = XE_GPU_REG_RB_STENCILREFMASK;
stencil_ref_mask_back_reg = XE_GPU_REG_RB_STENCILREFMASK_BF;
stencil_ref_mask_back_reg = XE_GPU_REG_RB_STENCILREFMASK;
}
} else {
stencil_ref_mask_front_reg = XE_GPU_REG_RB_STENCILREFMASK;
stencil_ref_mask_back_reg = XE_GPU_REG_RB_STENCILREFMASK;
auto stencil_ref_mask_front =
regs.Get<reg::RB_STENCILREFMASK>(stencil_ref_mask_front_reg);
auto stencil_ref_mask_back =
regs.Get<reg::RB_STENCILREFMASK>(stencil_ref_mask_back_reg);
// Compare mask.
dynamic_stencil_compare_mask_front_update_needed_ |=
dynamic_stencil_compare_mask_front_ !=
stencil_ref_mask_front.stencilmask;
dynamic_stencil_compare_mask_front_ = stencil_ref_mask_front.stencilmask;
dynamic_stencil_compare_mask_back_update_needed_ |=
dynamic_stencil_compare_mask_back_ !=
stencil_ref_mask_back.stencilmask;
dynamic_stencil_compare_mask_back_ = stencil_ref_mask_back.stencilmask;
// Write mask.
dynamic_stencil_write_mask_front_update_needed_ |=
dynamic_stencil_write_mask_front_ !=
stencil_ref_mask_front.stencilwritemask;
dynamic_stencil_write_mask_front_ =
stencil_ref_mask_front.stencilwritemask;
dynamic_stencil_write_mask_back_update_needed_ |=
dynamic_stencil_write_mask_back_ !=
stencil_ref_mask_back.stencilwritemask;
dynamic_stencil_write_mask_back_ = stencil_ref_mask_back.stencilwritemask;
// Reference.
dynamic_stencil_reference_front_update_needed_ |=
dynamic_stencil_reference_front_ != stencil_ref_mask_front.stencilref;
dynamic_stencil_reference_front_ = stencil_ref_mask_front.stencilref;
dynamic_stencil_reference_back_update_needed_ |=
dynamic_stencil_reference_back_ != stencil_ref_mask_back.stencilref;
dynamic_stencil_reference_back_ = stencil_ref_mask_back.stencilref;
}
auto stencil_ref_mask_front =
regs.Get<reg::RB_STENCILREFMASK>(stencil_ref_mask_front_reg);
auto stencil_ref_mask_back =
regs.Get<reg::RB_STENCILREFMASK>(stencil_ref_mask_back_reg);
// Compare mask.
dynamic_stencil_compare_mask_front_update_needed_ |=
dynamic_stencil_compare_mask_front_ !=
stencil_ref_mask_front.stencilmask;
dynamic_stencil_compare_mask_front_ = stencil_ref_mask_front.stencilmask;
dynamic_stencil_compare_mask_back_update_needed_ |=
dynamic_stencil_compare_mask_back_ != stencil_ref_mask_back.stencilmask;
dynamic_stencil_compare_mask_back_ = stencil_ref_mask_back.stencilmask;
// Write mask.
dynamic_stencil_write_mask_front_update_needed_ |=
dynamic_stencil_write_mask_front_ !=
stencil_ref_mask_front.stencilwritemask;
dynamic_stencil_write_mask_front_ = stencil_ref_mask_front.stencilwritemask;
dynamic_stencil_write_mask_back_update_needed_ |=
dynamic_stencil_write_mask_back_ !=
stencil_ref_mask_back.stencilwritemask;
dynamic_stencil_write_mask_back_ = stencil_ref_mask_back.stencilwritemask;
// Reference.
dynamic_stencil_reference_front_update_needed_ |=
dynamic_stencil_reference_front_ != stencil_ref_mask_front.stencilref;
dynamic_stencil_reference_front_ = stencil_ref_mask_front.stencilref;
dynamic_stencil_reference_back_update_needed_ |=
dynamic_stencil_reference_back_ != stencil_ref_mask_back.stencilref;
dynamic_stencil_reference_back_ = stencil_ref_mask_back.stencilref;
}
// Using VK_STENCIL_FACE_FRONT_AND_BACK for higher safety when running on the
// Vulkan portability subset without separateStencilMaskRef.
if (dynamic_stencil_compare_mask_front_update_needed_ ||
dynamic_stencil_compare_mask_back_update_needed_) {
if (dynamic_stencil_compare_mask_front_ ==
dynamic_stencil_compare_mask_back_) {
deferred_command_buffer_.CmdVkSetStencilCompareMask(
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_compare_mask_front_);
} else {
if (dynamic_stencil_compare_mask_front_update_needed_) {
// Using VK_STENCIL_FACE_FRONT_AND_BACK for higher safety when running on
// the Vulkan portability subset without separateStencilMaskRef.
if (dynamic_stencil_compare_mask_front_update_needed_ ||
dynamic_stencil_compare_mask_back_update_needed_) {
if (dynamic_stencil_compare_mask_front_ ==
dynamic_stencil_compare_mask_back_) {
deferred_command_buffer_.CmdVkSetStencilCompareMask(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_compare_mask_front_);
}
if (dynamic_stencil_compare_mask_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilCompareMask(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_compare_mask_back_);
VK_STENCIL_FACE_FRONT_AND_BACK,
dynamic_stencil_compare_mask_front_);
} else {
if (dynamic_stencil_compare_mask_front_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilCompareMask(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_compare_mask_front_);
}
if (dynamic_stencil_compare_mask_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilCompareMask(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_compare_mask_back_);
}
}
dynamic_stencil_compare_mask_front_update_needed_ = false;
dynamic_stencil_compare_mask_back_update_needed_ = false;
}
dynamic_stencil_compare_mask_front_update_needed_ = false;
dynamic_stencil_compare_mask_back_update_needed_ = false;
}
if (dynamic_stencil_write_mask_front_update_needed_ ||
dynamic_stencil_write_mask_back_update_needed_) {
if (dynamic_stencil_write_mask_front_ == dynamic_stencil_write_mask_back_) {
deferred_command_buffer_.CmdVkSetStencilWriteMask(
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_write_mask_front_);
} else {
if (dynamic_stencil_write_mask_front_update_needed_) {
if (dynamic_stencil_write_mask_front_update_needed_ ||
dynamic_stencil_write_mask_back_update_needed_) {
if (dynamic_stencil_write_mask_front_ ==
dynamic_stencil_write_mask_back_) {
deferred_command_buffer_.CmdVkSetStencilWriteMask(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_write_mask_front_);
}
if (dynamic_stencil_write_mask_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilWriteMask(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_write_mask_back_);
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_write_mask_front_);
} else {
if (dynamic_stencil_write_mask_front_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilWriteMask(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_write_mask_front_);
}
if (dynamic_stencil_write_mask_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilWriteMask(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_write_mask_back_);
}
}
dynamic_stencil_write_mask_front_update_needed_ = false;
dynamic_stencil_write_mask_back_update_needed_ = false;
}
dynamic_stencil_write_mask_front_update_needed_ = false;
dynamic_stencil_write_mask_back_update_needed_ = false;
}
if (dynamic_stencil_reference_front_update_needed_ ||
dynamic_stencil_reference_back_update_needed_) {
if (dynamic_stencil_reference_front_ == dynamic_stencil_reference_back_) {
deferred_command_buffer_.CmdVkSetStencilReference(
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_reference_front_);
} else {
if (dynamic_stencil_reference_front_update_needed_) {
if (dynamic_stencil_reference_front_update_needed_ ||
dynamic_stencil_reference_back_update_needed_) {
if (dynamic_stencil_reference_front_ == dynamic_stencil_reference_back_) {
deferred_command_buffer_.CmdVkSetStencilReference(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_reference_front_);
}
if (dynamic_stencil_reference_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilReference(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_reference_back_);
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_reference_front_);
} else {
if (dynamic_stencil_reference_front_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilReference(
VK_STENCIL_FACE_FRONT_BIT, dynamic_stencil_reference_front_);
}
if (dynamic_stencil_reference_back_update_needed_) {
deferred_command_buffer_.CmdVkSetStencilReference(
VK_STENCIL_FACE_BACK_BIT, dynamic_stencil_reference_back_);
}
}
dynamic_stencil_reference_front_update_needed_ = false;
dynamic_stencil_reference_back_update_needed_ = false;
}
dynamic_stencil_reference_front_update_needed_ = false;
dynamic_stencil_reference_back_update_needed_ = false;
}
// TODO(Triang3l): VK_EXT_extended_dynamic_state and
@@ -3373,23 +3451,67 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
bool primitive_polygonal,
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
uint32_t used_texture_mask) {
uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask) {
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
const RegisterFile& regs = *register_file_;
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
float rb_alpha_ref = regs[XE_GPU_REG_RB_ALPHA_REF].f32;
auto rb_colorcontrol = regs.Get<reg::RB_COLORCONTROL>();
auto rb_depth_info = regs.Get<reg::RB_DEPTH_INFO>();
auto rb_stencilrefmask = regs.Get<reg::RB_STENCILREFMASK>();
auto rb_stencilrefmask_bf =
regs.Get<reg::RB_STENCILREFMASK>(XE_GPU_REG_RB_STENCILREFMASK_BF);
auto rb_surface_info = regs.Get<reg::RB_SURFACE_INFO>();
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
int32_t vgt_indx_offset = int32_t(regs[XE_GPU_REG_VGT_INDX_OFFSET].u32);
// Get the color info register values for each render target.
bool edram_fragment_shader_interlock =
render_target_cache_->GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
uint32_t draw_resolution_scale_x = texture_cache_->draw_resolution_scale_x();
uint32_t draw_resolution_scale_y = texture_cache_->draw_resolution_scale_y();
// Get the color info register values for each render target. Also, for FSI,
// exclude components that don't exist in the format from the write mask.
// Don't exclude fully overlapping render targets, however - two render
// targets with the same base address are used in the lighting pass of
// 4D5307E6, for example, with the needed one picked with dynamic control
// flow.
reg::RB_COLOR_INFO color_infos[xenos::kMaxColorRenderTargets];
float rt_clamp[4][4];
// Two UINT32_MAX if no components actually existing in the RT are written.
uint32_t rt_keep_masks[4][2];
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
color_infos[i] = regs.Get<reg::RB_COLOR_INFO>(
auto color_info = regs.Get<reg::RB_COLOR_INFO>(
reg::RB_COLOR_INFO::rt_register_indices[i]);
color_infos[i] = color_info;
if (edram_fragment_shader_interlock) {
RenderTargetCache::GetPSIColorFormatInfo(
color_info.color_format, (normalized_color_mask >> (i * 4)) & 0b1111,
rt_clamp[i][0], rt_clamp[i][1], rt_clamp[i][2], rt_clamp[i][3],
rt_keep_masks[i][0], rt_keep_masks[i][1]);
}
}
// Disable depth and stencil if it aliases a color render target (for
// instance, during the XBLA logo in 58410954, though depth writing is already
// disabled there).
bool depth_stencil_enabled = normalized_depth_control.stencil_enable ||
normalized_depth_control.z_enable;
if (edram_fragment_shader_interlock && depth_stencil_enabled) {
for (uint32_t i = 0; i < 4; ++i) {
if (rb_depth_info.depth_base == color_infos[i].color_base &&
(rt_keep_masks[i][0] != UINT32_MAX ||
rt_keep_masks[i][1] != UINT32_MAX)) {
depth_stencil_enabled = false;
break;
}
}
}
bool dirty = false;
@@ -3433,6 +3555,13 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
if (draw_util::IsPrimitiveLine(regs)) {
flags |= SpirvShaderTranslator::kSysFlag_PrimitiveLine;
}
// MSAA sample count.
flags |= uint32_t(rb_surface_info.msaa_samples)
<< SpirvShaderTranslator::kSysFlag_MsaaSamples_Shift;
// Depth format.
if (rb_depth_info.depth_format == xenos::DepthRenderTargetFormat::kD24FS8) {
flags |= SpirvShaderTranslator::kSysFlag_DepthFloat24;
}
// Alpha test.
xenos::CompareFunction alpha_test_function =
rb_colorcontrol.alpha_test_enable ? rb_colorcontrol.alpha_func
@@ -3447,6 +3576,30 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
flags |= SpirvShaderTranslator::kSysFlag_ConvertColor0ToGamma << i;
}
}
if (edram_fragment_shader_interlock && depth_stencil_enabled) {
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthStencil;
if (normalized_depth_control.z_enable) {
flags |= uint32_t(normalized_depth_control.zfunc)
<< SpirvShaderTranslator::kSysFlag_FSIDepthPassIfLess_Shift;
if (normalized_depth_control.z_write_enable) {
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthWrite;
}
} else {
// In case stencil is used without depth testing - always pass, and
// don't modify the stored depth.
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthPassIfLess |
SpirvShaderTranslator::kSysFlag_FSIDepthPassIfEqual |
SpirvShaderTranslator::kSysFlag_FSIDepthPassIfGreater;
}
if (normalized_depth_control.stencil_enable) {
flags |= SpirvShaderTranslator::kSysFlag_FSIStencilTest;
}
// Hint - if not applicable to the shader, will not have effect.
if (alpha_test_function == xenos::CompareFunction::kAlways &&
!rb_colorcontrol.alpha_to_mask_enable) {
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthStencilEarlyWrite;
}
}
dirty |= system_constants_.flags != flags;
system_constants_.flags = flags;
@@ -3506,10 +3659,10 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
// to radius conversion to avoid multiplying the per-vertex diameter by an
// additional constant in the shader.
float point_screen_diameter_to_ndc_radius_x =
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_x())) /
(/* 0.5f * 2.0f * */ float(draw_resolution_scale_x)) /
std::max(viewport_info.xy_extent[0], uint32_t(1));
float point_screen_diameter_to_ndc_radius_y =
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_y())) /
(/* 0.5f * 2.0f * */ float(draw_resolution_scale_y)) /
std::max(viewport_info.xy_extent[1], uint32_t(1));
dirty |= system_constants_.point_screen_diameter_to_ndc_radius[0] !=
point_screen_diameter_to_ndc_radius_x;
@@ -3574,7 +3727,25 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
dirty |= system_constants_.alpha_test_reference != rb_alpha_ref;
system_constants_.alpha_test_reference = rb_alpha_ref;
// Color exponent bias.
uint32_t edram_tile_dwords_scaled =
xenos::kEdramTileWidthSamples * xenos::kEdramTileHeightSamples *
(draw_resolution_scale_x * draw_resolution_scale_y);
// EDRAM pitch for FSI render target writing.
if (edram_fragment_shader_interlock) {
// Align, then multiply by 32bpp tile size in dwords.
uint32_t edram_32bpp_tile_pitch_dwords_scaled =
((rb_surface_info.surface_pitch *
(rb_surface_info.msaa_samples >= xenos::MsaaSamples::k4X ? 2 : 1)) +
(xenos::kEdramTileWidthSamples - 1)) /
xenos::kEdramTileWidthSamples * edram_tile_dwords_scaled;
dirty |= system_constants_.edram_32bpp_tile_pitch_dwords_scaled !=
edram_32bpp_tile_pitch_dwords_scaled;
system_constants_.edram_32bpp_tile_pitch_dwords_scaled =
edram_32bpp_tile_pitch_dwords_scaled;
}
// Color exponent bias and FSI render target writing.
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
reg::RB_COLOR_INFO color_info = color_infos[i];
// Exponent bias is in bits 20:25 of RB_COLOR_INFO.
@@ -3595,6 +3766,148 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
UINT32_C(0x3F800000) + (color_exp_bias << 23);
dirty |= system_constants_.color_exp_bias[i] != color_exp_bias_scale;
system_constants_.color_exp_bias[i] = color_exp_bias_scale;
if (edram_fragment_shader_interlock) {
dirty |=
system_constants_.edram_rt_keep_mask[i][0] != rt_keep_masks[i][0];
system_constants_.edram_rt_keep_mask[i][0] = rt_keep_masks[i][0];
dirty |=
system_constants_.edram_rt_keep_mask[i][1] != rt_keep_masks[i][1];
system_constants_.edram_rt_keep_mask[i][1] = rt_keep_masks[i][1];
if (rt_keep_masks[i][0] != UINT32_MAX ||
rt_keep_masks[i][1] != UINT32_MAX) {
uint32_t rt_base_dwords_scaled =
color_info.color_base * edram_tile_dwords_scaled;
dirty |= system_constants_.edram_rt_base_dwords_scaled[i] !=
rt_base_dwords_scaled;
system_constants_.edram_rt_base_dwords_scaled[i] =
rt_base_dwords_scaled;
uint32_t format_flags =
RenderTargetCache::AddPSIColorFormatFlags(color_info.color_format);
dirty |= system_constants_.edram_rt_format_flags[i] != format_flags;
system_constants_.edram_rt_format_flags[i] = format_flags;
uint32_t blend_factors_ops =
regs[reg::RB_BLENDCONTROL::rt_register_indices[i]].u32 & 0x1FFF1FFF;
dirty |= system_constants_.edram_rt_blend_factors_ops[i] !=
blend_factors_ops;
system_constants_.edram_rt_blend_factors_ops[i] = blend_factors_ops;
// Can't do float comparisons here because NaNs would result in always
// setting the dirty flag.
dirty |= std::memcmp(system_constants_.edram_rt_clamp[i], rt_clamp[i],
4 * sizeof(float)) != 0;
std::memcpy(system_constants_.edram_rt_clamp[i], rt_clamp[i],
4 * sizeof(float));
}
}
}
if (edram_fragment_shader_interlock) {
uint32_t depth_base_dwords_scaled =
rb_depth_info.depth_base * edram_tile_dwords_scaled;
dirty |= system_constants_.edram_depth_base_dwords_scaled !=
depth_base_dwords_scaled;
system_constants_.edram_depth_base_dwords_scaled = depth_base_dwords_scaled;
// For non-polygons, front polygon offset is used, and it's enabled if
// POLY_OFFSET_PARA_ENABLED is set, for polygons, separate front and back
// are used.
float poly_offset_front_scale = 0.0f, poly_offset_front_offset = 0.0f;
float poly_offset_back_scale = 0.0f, poly_offset_back_offset = 0.0f;
if (primitive_polygonal) {
if (pa_su_sc_mode_cntl.poly_offset_front_enable) {
poly_offset_front_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
poly_offset_front_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
}
if (pa_su_sc_mode_cntl.poly_offset_back_enable) {
poly_offset_back_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
poly_offset_back_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
}
} else {
if (pa_su_sc_mode_cntl.poly_offset_para_enable) {
poly_offset_front_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
poly_offset_front_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
poly_offset_back_scale = poly_offset_front_scale;
poly_offset_back_offset = poly_offset_front_offset;
}
}
// With non-square resolution scaling, make sure the worst-case impact is
// reverted (slope only along the scaled axis), thus max. More bias is
// better than less bias, because less bias means Z fighting with the
// background is more likely.
float poly_offset_scale_factor =
xenos::kPolygonOffsetScaleSubpixelUnit *
std::max(draw_resolution_scale_x, draw_resolution_scale_y);
poly_offset_front_scale *= poly_offset_scale_factor;
poly_offset_back_scale *= poly_offset_scale_factor;
dirty |= system_constants_.edram_poly_offset_front_scale !=
poly_offset_front_scale;
system_constants_.edram_poly_offset_front_scale = poly_offset_front_scale;
dirty |= system_constants_.edram_poly_offset_front_offset !=
poly_offset_front_offset;
system_constants_.edram_poly_offset_front_offset = poly_offset_front_offset;
dirty |= system_constants_.edram_poly_offset_back_scale !=
poly_offset_back_scale;
system_constants_.edram_poly_offset_back_scale = poly_offset_back_scale;
dirty |= system_constants_.edram_poly_offset_back_offset !=
poly_offset_back_offset;
system_constants_.edram_poly_offset_back_offset = poly_offset_back_offset;
if (depth_stencil_enabled && normalized_depth_control.stencil_enable) {
uint32_t stencil_front_reference_masks =
rb_stencilrefmask.value & 0xFFFFFF;
dirty |= system_constants_.edram_stencil_front_reference_masks !=
stencil_front_reference_masks;
system_constants_.edram_stencil_front_reference_masks =
stencil_front_reference_masks;
uint32_t stencil_func_ops =
(normalized_depth_control.value >> 8) & ((1 << 12) - 1);
dirty |=
system_constants_.edram_stencil_front_func_ops != stencil_func_ops;
system_constants_.edram_stencil_front_func_ops = stencil_func_ops;
if (primitive_polygonal && normalized_depth_control.backface_enable) {
uint32_t stencil_back_reference_masks =
rb_stencilrefmask_bf.value & 0xFFFFFF;
dirty |= system_constants_.edram_stencil_back_reference_masks !=
stencil_back_reference_masks;
system_constants_.edram_stencil_back_reference_masks =
stencil_back_reference_masks;
uint32_t stencil_func_ops_bf =
(normalized_depth_control.value >> 20) & ((1 << 12) - 1);
dirty |= system_constants_.edram_stencil_back_func_ops !=
stencil_func_ops_bf;
system_constants_.edram_stencil_back_func_ops = stencil_func_ops_bf;
} else {
dirty |= std::memcmp(system_constants_.edram_stencil_back,
system_constants_.edram_stencil_front,
2 * sizeof(uint32_t)) != 0;
std::memcpy(system_constants_.edram_stencil_back,
system_constants_.edram_stencil_front,
2 * sizeof(uint32_t));
}
}
dirty |= system_constants_.edram_blend_constant[0] !=
regs[XE_GPU_REG_RB_BLEND_RED].f32;
system_constants_.edram_blend_constant[0] =
regs[XE_GPU_REG_RB_BLEND_RED].f32;
dirty |= system_constants_.edram_blend_constant[1] !=
regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
system_constants_.edram_blend_constant[1] =
regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
dirty |= system_constants_.edram_blend_constant[2] !=
regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
system_constants_.edram_blend_constant[2] =
regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
dirty |= system_constants_.edram_blend_constant[3] !=
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
system_constants_.edram_blend_constant[3] =
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
}
if (dirty) {