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) {

View File

@@ -16,6 +16,7 @@
#include <deque>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -259,6 +260,9 @@ class VulkanCommandProcessor final : public CommandProcessor {
void SetViewport(const VkViewport& viewport);
void SetScissor(const VkRect2D& scissor);
// Returns the text to display in the GPU backend name in the window title.
std::string GetWindowTitleText() const;
protected:
bool SetupContext() override;
void ShutdownContext() override;
@@ -443,7 +447,8 @@ class VulkanCommandProcessor final : public CommandProcessor {
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);
bool UpdateBindings(const VulkanShader* vertex_shader,
const VulkanShader* pixel_shader);
// Allocates a descriptor set and fills one or two VkWriteDescriptorSet
@@ -520,12 +525,12 @@ class VulkanCommandProcessor final : public CommandProcessor {
// Descriptor set layouts used by different shaders.
VkDescriptorSetLayout descriptor_set_layout_empty_ = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ =
VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout_constants_ = VK_NULL_HANDLE;
std::array<VkDescriptorSetLayout,
size_t(SingleTransientDescriptorLayout::kCount)>
descriptor_set_layouts_single_transient_{};
VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ =
VK_NULL_HANDLE;
// Descriptor set layouts are referenced by pipeline_layouts_.
std::unordered_map<TextureDescriptorSetLayoutKey, VkDescriptorSetLayout,
@@ -655,6 +660,9 @@ class VulkanCommandProcessor final : public CommandProcessor {
// declared as dynamic in the pipeline) invalidates such dynamic state.
VkViewport dynamic_viewport_;
VkRect2D dynamic_scissor_;
// Dynamic fixed-function depth bias, blend constants, stencil state are
// applicable only to the render target implementations where they are
// actually involved.
float dynamic_depth_bias_constant_factor_;
float dynamic_depth_bias_slope_factor_;
float dynamic_blend_constants_[4];

View File

@@ -21,6 +21,15 @@ VulkanGraphicsSystem::VulkanGraphicsSystem() {}
VulkanGraphicsSystem::~VulkanGraphicsSystem() {}
std::string VulkanGraphicsSystem::name() const {
auto vulkan_command_processor =
static_cast<VulkanCommandProcessor*>(command_processor());
if (vulkan_command_processor != nullptr) {
return vulkan_command_processor->GetWindowTitleText();
}
return "Vulkan - HEAVILY INCOMPLETE, early development";
}
X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
kernel::KernelState* kernel_state,
ui::WindowedAppContext* app_context,

View File

@@ -26,9 +26,7 @@ class VulkanGraphicsSystem : public GraphicsSystem {
static bool IsAvailable() { return true; }
std::string name() const override {
return "Vulkan - HEAVILY INCOMPLETE, early development";
}
std::string name() const override;
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::WindowedAppContext* app_context,

View File

@@ -15,6 +15,7 @@
#include <cstring>
#include <memory>
#include <utility>
#include <vector>
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/glslang/SPIRV/SpvBuilder.h"
@@ -53,8 +54,32 @@ bool VulkanPipelineCache::Initialize() {
const ui::vulkan::VulkanProvider& provider =
command_processor_.GetVulkanProvider();
bool edram_fragment_shader_interlock =
render_target_cache_.GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
SpirvShaderTranslator::Features(provider));
SpirvShaderTranslator::Features(provider),
render_target_cache_.msaa_2x_attachments_supported(),
render_target_cache_.msaa_2x_no_attachments_supported(),
edram_fragment_shader_interlock);
if (edram_fragment_shader_interlock) {
std::vector<uint8_t> depth_only_fragment_shader_code =
shader_translator_->CreateDepthOnlyFragmentShader();
depth_only_fragment_shader_ = ui::vulkan::util::CreateShaderModule(
provider,
reinterpret_cast<const uint32_t*>(
depth_only_fragment_shader_code.data()),
depth_only_fragment_shader_code.size());
if (depth_only_fragment_shader_ == VK_NULL_HANDLE) {
XELOGE(
"VulkanPipelineCache: Failed to create the depth/stencil-only "
"fragment shader for the fragment shader interlock render backend "
"implementation");
return false;
}
}
return true;
}
@@ -75,6 +100,8 @@ void VulkanPipelineCache::Shutdown() {
pipelines_.clear();
// Destroy all internal shaders.
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
depth_only_fragment_shader_);
for (const auto& geometry_shader_pair : geometry_shaders_) {
if (geometry_shader_pair.second != VK_NULL_HANDLE) {
dfn.vkDestroyShaderModule(device, geometry_shader_pair.second, nullptr);
@@ -179,15 +206,18 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
modification.pixel.param_gen_point = 0;
}
using DepthStencilMode =
SpirvShaderTranslator::Modification::DepthStencilMode;
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
modification.pixel.depth_stencil_mode = DepthStencilMode::kEarlyHint;
} else {
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
if (render_target_cache_.GetPath() ==
RenderTargetCache::Path::kHostRenderTargets) {
using DepthStencilMode =
SpirvShaderTranslator::Modification::DepthStencilMode;
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
modification.pixel.depth_stencil_mode = DepthStencilMode::kEarlyHint;
} else {
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
}
}
return modification;
@@ -303,7 +333,11 @@ bool VulkanPipelineCache::ConfigurePipeline(
}
}
VkRenderPass render_pass =
render_target_cache_.GetRenderPass(render_pass_key);
render_target_cache_.GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock
? render_target_cache_.GetFragmentShaderInterlockRenderPass()
: render_target_cache_.GetHostRenderTargetsRenderPass(
render_pass_key);
if (render_pass == VK_NULL_HANDLE) {
return false;
}
@@ -603,123 +637,127 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
description_out.polygon_mode = PipelinePolygonMode::kFill;
}
// TODO(Triang3l): Skip depth / stencil and color state for the fragment
// shader interlock RB implementation.
if (render_pass_key.depth_and_color_used & 1) {
if (normalized_depth_control.z_enable) {
description_out.depth_write_enable =
normalized_depth_control.z_write_enable;
description_out.depth_compare_op = normalized_depth_control.zfunc;
} else {
description_out.depth_compare_op = xenos::CompareFunction::kAlways;
}
if (normalized_depth_control.stencil_enable) {
description_out.stencil_test_enable = 1;
description_out.stencil_front_fail_op =
normalized_depth_control.stencilfail;
description_out.stencil_front_pass_op =
normalized_depth_control.stencilzpass;
description_out.stencil_front_depth_fail_op =
normalized_depth_control.stencilzfail;
description_out.stencil_front_compare_op =
normalized_depth_control.stencilfunc;
if (primitive_polygonal && normalized_depth_control.backface_enable) {
description_out.stencil_back_fail_op =
normalized_depth_control.stencilfail_bf;
description_out.stencil_back_pass_op =
normalized_depth_control.stencilzpass_bf;
description_out.stencil_back_depth_fail_op =
normalized_depth_control.stencilzfail_bf;
description_out.stencil_back_compare_op =
normalized_depth_control.stencilfunc_bf;
if (render_target_cache_.GetPath() ==
RenderTargetCache::Path::kHostRenderTargets) {
if (render_pass_key.depth_and_color_used & 1) {
if (normalized_depth_control.z_enable) {
description_out.depth_write_enable =
normalized_depth_control.z_write_enable;
description_out.depth_compare_op = normalized_depth_control.zfunc;
} else {
description_out.stencil_back_fail_op =
description_out.stencil_front_fail_op;
description_out.stencil_back_pass_op =
description_out.stencil_front_pass_op;
description_out.stencil_back_depth_fail_op =
description_out.stencil_front_depth_fail_op;
description_out.stencil_back_compare_op =
description_out.stencil_front_compare_op;
description_out.depth_compare_op = xenos::CompareFunction::kAlways;
}
if (normalized_depth_control.stencil_enable) {
description_out.stencil_test_enable = 1;
description_out.stencil_front_fail_op =
normalized_depth_control.stencilfail;
description_out.stencil_front_pass_op =
normalized_depth_control.stencilzpass;
description_out.stencil_front_depth_fail_op =
normalized_depth_control.stencilzfail;
description_out.stencil_front_compare_op =
normalized_depth_control.stencilfunc;
if (primitive_polygonal && normalized_depth_control.backface_enable) {
description_out.stencil_back_fail_op =
normalized_depth_control.stencilfail_bf;
description_out.stencil_back_pass_op =
normalized_depth_control.stencilzpass_bf;
description_out.stencil_back_depth_fail_op =
normalized_depth_control.stencilzfail_bf;
description_out.stencil_back_compare_op =
normalized_depth_control.stencilfunc_bf;
} else {
description_out.stencil_back_fail_op =
description_out.stencil_front_fail_op;
description_out.stencil_back_pass_op =
description_out.stencil_front_pass_op;
description_out.stencil_back_depth_fail_op =
description_out.stencil_front_depth_fail_op;
description_out.stencil_back_compare_op =
description_out.stencil_front_compare_op;
}
}
}
}
// 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_features.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 multiple
// draw calls with different pipelines 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].u32
: 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.
// 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_features.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);
description_out.render_targets[color_rt_index] =
render_pass_first_color_rt;
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].u32
: 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;
}
}
}
}
@@ -1929,6 +1967,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
command_processor_.GetVulkanProvider();
const VkPhysicalDeviceFeatures& device_features = provider.device_features();
bool edram_fragment_shader_interlock =
render_target_cache_.GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock;
std::array<VkPipelineShaderStageCreateInfo, 3> shader_stages;
uint32_t shader_stage_count = 0;
@@ -1962,24 +2004,32 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
shader_stage_geometry.pName = "main";
shader_stage_geometry.pSpecializationInfo = nullptr;
}
// Pixel shader.
// Fragment shader.
VkPipelineShaderStageCreateInfo& shader_stage_fragment =
shader_stages[shader_stage_count++];
shader_stage_fragment.sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stage_fragment.pNext = nullptr;
shader_stage_fragment.flags = 0;
shader_stage_fragment.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stage_fragment.module = VK_NULL_HANDLE;
shader_stage_fragment.pName = "main";
shader_stage_fragment.pSpecializationInfo = nullptr;
if (creation_arguments.pixel_shader) {
assert_true(creation_arguments.pixel_shader->is_translated());
if (!creation_arguments.pixel_shader->is_valid()) {
return false;
}
VkPipelineShaderStageCreateInfo& shader_stage_fragment =
shader_stages[shader_stage_count++];
shader_stage_fragment.sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stage_fragment.pNext = nullptr;
shader_stage_fragment.flags = 0;
shader_stage_fragment.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stage_fragment.module =
creation_arguments.pixel_shader->shader_module();
assert_true(shader_stage_fragment.module != VK_NULL_HANDLE);
shader_stage_fragment.pName = "main";
shader_stage_fragment.pSpecializationInfo = nullptr;
} else {
if (edram_fragment_shader_interlock) {
shader_stage_fragment.module = depth_only_fragment_shader_;
}
}
if (shader_stage_fragment.module == VK_NULL_HANDLE) {
--shader_stage_count;
}
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
@@ -2087,11 +2137,11 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
// formula, though Z has no effect on anything if a depth attachment is not
// used (the guest shader can't access Z), enabling only when there's a
// depth / stencil attachment for correctness.
// TODO(Triang3l): Disable the depth bias for the fragment shader interlock RB
// implementation.
rasterization_state.depthBiasEnable =
(description.render_pass_key.depth_and_color_used & 0b1) ? VK_TRUE
: VK_FALSE;
(!edram_fragment_shader_interlock &&
(description.render_pass_key.depth_and_color_used & 0b1))
? VK_TRUE
: VK_FALSE;
// TODO(Triang3l): Wide lines.
rasterization_state.lineWidth = 1.0f;
@@ -2101,6 +2151,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
if (description.render_pass_key.msaa_samples == xenos::MsaaSamples::k2X &&
!render_target_cache_.IsMsaa2xSupported(
!edram_fragment_shader_interlock &&
description.render_pass_key.depth_and_color_used != 0)) {
// Using sample 0 as 0 and 3 as 1 for 2x instead (not exactly the same
// sample locations, but still top-left and bottom-right - however, this can
@@ -2119,126 +2170,131 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
depth_stencil_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_state.pNext = nullptr;
if (description.depth_write_enable ||
description.depth_compare_op != xenos::CompareFunction::kAlways) {
depth_stencil_state.depthTestEnable = VK_TRUE;
depth_stencil_state.depthWriteEnable =
description.depth_write_enable ? VK_TRUE : VK_FALSE;
depth_stencil_state.depthCompareOp = VkCompareOp(
uint32_t(VK_COMPARE_OP_NEVER) + uint32_t(description.depth_compare_op));
}
if (description.stencil_test_enable) {
depth_stencil_state.stencilTestEnable = VK_TRUE;
depth_stencil_state.front.failOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_fail_op));
depth_stencil_state.front.passOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_pass_op));
depth_stencil_state.front.depthFailOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_depth_fail_op));
depth_stencil_state.front.compareOp =
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
uint32_t(description.stencil_front_compare_op));
depth_stencil_state.back.failOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_fail_op));
depth_stencil_state.back.passOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_pass_op));
depth_stencil_state.back.depthFailOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_depth_fail_op));
depth_stencil_state.back.compareOp =
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
uint32_t(description.stencil_back_compare_op));
}
VkPipelineColorBlendAttachmentState
color_blend_attachments[xenos::kMaxColorRenderTargets] = {};
uint32_t color_rts_used =
description.render_pass_key.depth_and_color_used >> 1;
{
static const VkBlendFactor kBlendFactorMap[] = {
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_CONSTANT_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE,
};
// 8 entries for safety since 3 bits from the guest are passed directly.
static const VkBlendOp kBlendOpMap[] = {VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_ADD,
VK_BLEND_OP_ADD,
VK_BLEND_OP_ADD};
uint32_t color_rts_remaining = color_rts_used;
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);
VkPipelineColorBlendAttachmentState& color_blend_attachment =
color_blend_attachments[color_rt_index];
const PipelineRenderTarget& color_rt =
description.render_targets[color_rt_index];
if (color_rt.src_color_blend_factor != PipelineBlendFactor::kOne ||
color_rt.dst_color_blend_factor != PipelineBlendFactor::kZero ||
color_rt.color_blend_op != xenos::BlendOp::kAdd ||
color_rt.src_alpha_blend_factor != PipelineBlendFactor::kOne ||
color_rt.dst_alpha_blend_factor != PipelineBlendFactor::kZero ||
color_rt.alpha_blend_op != xenos::BlendOp::kAdd) {
color_blend_attachment.blendEnable = VK_TRUE;
color_blend_attachment.srcColorBlendFactor =
kBlendFactorMap[uint32_t(color_rt.src_color_blend_factor)];
color_blend_attachment.dstColorBlendFactor =
kBlendFactorMap[uint32_t(color_rt.dst_color_blend_factor)];
color_blend_attachment.colorBlendOp =
kBlendOpMap[uint32_t(color_rt.color_blend_op)];
color_blend_attachment.srcAlphaBlendFactor =
kBlendFactorMap[uint32_t(color_rt.src_alpha_blend_factor)];
color_blend_attachment.dstAlphaBlendFactor =
kBlendFactorMap[uint32_t(color_rt.dst_alpha_blend_factor)];
color_blend_attachment.alphaBlendOp =
kBlendOpMap[uint32_t(color_rt.alpha_blend_op)];
}
color_blend_attachment.colorWriteMask =
VkColorComponentFlags(color_rt.color_write_mask);
if (!device_features.independentBlend) {
// For non-independent blend, the pAttachments element for the first
// actually used color will be replicated into all.
break;
}
if (!edram_fragment_shader_interlock) {
if (description.depth_write_enable ||
description.depth_compare_op != xenos::CompareFunction::kAlways) {
depth_stencil_state.depthTestEnable = VK_TRUE;
depth_stencil_state.depthWriteEnable =
description.depth_write_enable ? VK_TRUE : VK_FALSE;
depth_stencil_state.depthCompareOp =
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
uint32_t(description.depth_compare_op));
}
if (description.stencil_test_enable) {
depth_stencil_state.stencilTestEnable = VK_TRUE;
depth_stencil_state.front.failOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_fail_op));
depth_stencil_state.front.passOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_pass_op));
depth_stencil_state.front.depthFailOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_front_depth_fail_op));
depth_stencil_state.front.compareOp =
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
uint32_t(description.stencil_front_compare_op));
depth_stencil_state.back.failOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_fail_op));
depth_stencil_state.back.passOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_pass_op));
depth_stencil_state.back.depthFailOp =
VkStencilOp(uint32_t(VK_STENCIL_OP_KEEP) +
uint32_t(description.stencil_back_depth_fail_op));
depth_stencil_state.back.compareOp =
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
uint32_t(description.stencil_back_compare_op));
}
}
VkPipelineColorBlendStateCreateInfo color_blend_state = {};
color_blend_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blend_state.attachmentCount = 32 - xe::lzcnt(color_rts_used);
color_blend_state.pAttachments = color_blend_attachments;
if (color_rts_used && !device_features.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;
VkPipelineColorBlendAttachmentState
color_blend_attachments[xenos::kMaxColorRenderTargets] = {};
if (!edram_fragment_shader_interlock) {
uint32_t color_rts_used =
description.render_pass_key.depth_and_color_used >> 1;
{
static const VkBlendFactor kBlendFactorMap[] = {
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_CONSTANT_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE,
};
// 8 entries for safety since 3 bits from the guest are passed directly.
static const VkBlendOp kBlendOpMap[] = {VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_ADD,
VK_BLEND_OP_ADD,
VK_BLEND_OP_ADD};
uint32_t color_rts_remaining = color_rts_used;
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);
VkPipelineColorBlendAttachmentState& color_blend_attachment =
color_blend_attachments[color_rt_index];
const PipelineRenderTarget& color_rt =
description.render_targets[color_rt_index];
if (color_rt.src_color_blend_factor != PipelineBlendFactor::kOne ||
color_rt.dst_color_blend_factor != PipelineBlendFactor::kZero ||
color_rt.color_blend_op != xenos::BlendOp::kAdd ||
color_rt.src_alpha_blend_factor != PipelineBlendFactor::kOne ||
color_rt.dst_alpha_blend_factor != PipelineBlendFactor::kZero ||
color_rt.alpha_blend_op != xenos::BlendOp::kAdd) {
color_blend_attachment.blendEnable = VK_TRUE;
color_blend_attachment.srcColorBlendFactor =
kBlendFactorMap[uint32_t(color_rt.src_color_blend_factor)];
color_blend_attachment.dstColorBlendFactor =
kBlendFactorMap[uint32_t(color_rt.dst_color_blend_factor)];
color_blend_attachment.colorBlendOp =
kBlendOpMap[uint32_t(color_rt.color_blend_op)];
color_blend_attachment.srcAlphaBlendFactor =
kBlendFactorMap[uint32_t(color_rt.src_alpha_blend_factor)];
color_blend_attachment.dstAlphaBlendFactor =
kBlendFactorMap[uint32_t(color_rt.dst_alpha_blend_factor)];
color_blend_attachment.alphaBlendOp =
kBlendOpMap[uint32_t(color_rt.alpha_blend_op)];
}
color_blend_attachment.colorWriteMask =
VkColorComponentFlags(color_rt.color_write_mask);
if (!device_features.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_features.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];
}
color_blend_attachments[i] =
color_blend_attachments[first_color_rt_index];
}
}
@@ -2255,16 +2311,18 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
// invalidated (again, even if it has no effect).
dynamic_states[dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
dynamic_states[dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_DEPTH_BIAS;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_BLEND_CONSTANTS;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_REFERENCE;
if (!edram_fragment_shader_interlock) {
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_DEPTH_BIAS;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_BLEND_CONSTANTS;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
dynamic_states[dynamic_state.dynamicStateCount++] =
VK_DYNAMIC_STATE_STENCIL_REFERENCE;
}
VkGraphicsPipelineCreateInfo pipeline_create_info;
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;

View File

@@ -314,6 +314,10 @@ class VulkanPipelineCache {
GeometryShaderKey::Hasher>
geometry_shaders_;
// Empty depth-only pixel shader for writing to depth buffer using fragment
// shader interlock when no Xenos pixel shader provided.
VkShaderModule depth_only_fragment_shader_ = VK_NULL_HANDLE;
std::unordered_map<PipelineDescription, Pipeline, PipelineDescription::Hasher>
pipelines_;

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,10 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
// true 4x MSAA passes (framebuffers because render target cache render
// targets are different for 2x and 4x guest MSAA, pipelines because the
// sample mask will have 2 samples excluded for 2x-as-4x).
// This has effect only on the attachments, but even in cases when there
// are no attachments, it can be used to the sample count between
// subsystems, for instance, to specify the desired number of samples to
// use when there are no attachments in pipelines.
xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; // 2
// << 0 is depth, << 1...4 is color.
uint32_t depth_and_color_used : 1 + xenos::kMaxColorRenderTargets; // 7
@@ -81,8 +85,9 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
static_assert_size(RenderPassKey, sizeof(uint32_t));
struct Framebuffer {
VkFramebuffer framebuffer;
VkExtent2D host_extent;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
VkExtent2D host_extent{};
Framebuffer() = default;
Framebuffer(VkFramebuffer framebuffer, const VkExtent2D& host_extent)
: framebuffer(framebuffer), host_extent(host_extent) {}
};
@@ -96,15 +101,16 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
// Transient descriptor set layouts must be initialized in the command
// processor.
bool Initialize();
bool Initialize(uint32_t shared_memory_binding_count);
void Shutdown(bool from_destructor = false);
void ClearCache() override;
void CompletedSubmissionUpdated();
void EndSubmission();
// TODO(Triang3l): Fragment shader interlock.
Path GetPath() const override { return Path::kHostRenderTargets; }
Path GetPath() const override { return path_; }
VkBuffer edram_buffer() const { return edram_buffer_; }
// Performs the resolve to a shared memory area according to the current
// register values, and also clears the render targets if needed. Must be in a
@@ -161,7 +167,11 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
// Returns the render pass object, or VK_NULL_HANDLE if failed to create.
// A render pass managed by the render target cache may be ended and resumed
// at any time (to allow for things like copying and texture loading).
VkRenderPass GetRenderPass(RenderPassKey key);
VkRenderPass GetHostRenderTargetsRenderPass(RenderPassKey key);
VkRenderPass GetFragmentShaderInterlockRenderPass() const {
assert_true(GetPath() == Path::kPixelShaderInterlock);
return fsi_render_pass_;
}
VkFormat GetDepthVulkanFormat(xenos::DepthRenderTargetFormat format) const;
VkFormat GetColorVulkanFormat(xenos::ColorRenderTargetFormat format) const;
@@ -178,6 +188,8 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
bool IsHostDepthEncodingDifferent(
xenos::DepthRenderTargetFormat format) const override;
void RequestPixelShaderInterlockBarrier() override;
private:
enum class EdramBufferUsage {
// There's no need for combined fragment and compute usages.
@@ -251,6 +263,8 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
VulkanCommandProcessor& command_processor_;
TraceWriter& trace_writer_;
Path path_ = Path::kHostRenderTargets;
// Accessible in fragment and compute shaders.
VkDescriptorSetLayout descriptor_set_layout_storage_buffer_ = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout_sampled_image_ = VK_NULL_HANDLE;
@@ -276,9 +290,18 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
std::array<VkPipeline, size_t(draw_util::ResolveCopyShaderIndex::kCount)>
resolve_copy_pipelines_{};
// RenderPassKey::key -> VkRenderPass.
// VK_NULL_HANDLE if failed to create.
std::unordered_map<uint32_t, VkRenderPass> render_passes_;
// On the fragment shader interlock path, the render pass key is used purely
// for passing parameters to pipeline setup - there's always only one render
// pass.
RenderPassKey last_update_render_pass_key_;
VkRenderPass last_update_render_pass_ = VK_NULL_HANDLE;
// The pitch is not used on the fragment shader interlock path.
uint32_t last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
// The attachments are not used on the fragment shader interlock path.
const RenderTarget* const*
last_update_framebuffer_attachments_[1 + xenos::kMaxColorRenderTargets] =
{};
const Framebuffer* last_update_framebuffer_ = VK_NULL_HANDLE;
// For host render targets.
@@ -809,7 +832,7 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
};
// Returns the framebuffer object, or VK_NULL_HANDLE if failed to create.
const Framebuffer* GetFramebuffer(
const Framebuffer* GetHostRenderTargetsFramebuffer(
RenderPassKey render_pass_key, uint32_t pitch_tiles_at_32bpp,
const RenderTarget* const* depth_and_color_render_targets);
@@ -845,17 +868,13 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
bool msaa_2x_attachments_supported_ = false;
bool msaa_2x_no_attachments_supported_ = false;
// VK_NULL_HANDLE if failed to create.
std::unordered_map<RenderPassKey, VkRenderPass, RenderPassKey::Hasher>
render_passes_;
std::unordered_map<FramebufferKey, Framebuffer, FramebufferKey::Hasher>
framebuffers_;
RenderPassKey last_update_render_pass_key_;
VkRenderPass last_update_render_pass_ = VK_NULL_HANDLE;
uint32_t last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
const RenderTarget* const*
last_update_framebuffer_attachments_[1 + xenos::kMaxColorRenderTargets] =
{};
const Framebuffer* last_update_framebuffer_ = VK_NULL_HANDLE;
// Set 0 - EDRAM storage buffer, set 1 - source depth sampled image (and
// unused stencil from the transfer descriptor set), HostDepthStoreConstants
// passed via push constants.
@@ -895,6 +914,15 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
// Temporary storage for DumpRenderTargets.
std::vector<ResolveCopyDumpRectangle> dump_rectangles_;
std::vector<DumpInvocation> dump_invocations_;
// For pixel (fragment) shader interlock.
VkRenderPass fsi_render_pass_ = VK_NULL_HANDLE;
Framebuffer fsi_framebuffer_;
VkPipelineLayout resolve_fsi_clear_pipeline_layout_ = VK_NULL_HANDLE;
VkPipeline resolve_fsi_clear_32bpp_pipeline_ = VK_NULL_HANDLE;
VkPipeline resolve_fsi_clear_64bpp_pipeline_ = VK_NULL_HANDLE;
};
} // namespace vulkan