[GPU] Vulkan fragment shader interlock RB and related fixes/cleanup
Also fixes addressing of MSAA samples 2 and 3 for 64bpp color render targets in the ROV RB implementation on Direct3D 12. Additionally, with FSI/ROV, alpha test and alpha to coverage are done only if the render target 0 was dynamically written to (according to the Direct3D 9 rules for writing to color render targets, though not sure if they actually apply to the alpha tests on Direct3D 9, but for safety). There is also some code cleanup for things spotted during the development of the feature.
This commit is contained in:
@@ -67,9 +67,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),
|
||||
@@ -106,6 +103,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");
|
||||
@@ -146,7 +169,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.
|
||||
@@ -163,37 +186,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] = {};
|
||||
@@ -289,16 +281,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_);
|
||||
@@ -320,9 +366,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;
|
||||
@@ -369,20 +414,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.
|
||||
|
||||
@@ -1041,6 +1111,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(
|
||||
@@ -1050,9 +1123,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_);
|
||||
|
||||
@@ -2401,7 +2471,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.
|
||||
@@ -2461,6 +2532,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());
|
||||
@@ -3180,175 +3253,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
|
||||
@@ -3359,23 +3437,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;
|
||||
@@ -3419,6 +3541,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
|
||||
@@ -3433,6 +3562,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;
|
||||
|
||||
@@ -3492,10 +3645,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;
|
||||
@@ -3560,7 +3713,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.
|
||||
@@ -3581,6 +3752,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) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -256,6 +257,9 @@ class VulkanCommandProcessor : 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;
|
||||
@@ -437,7 +441,8 @@ class VulkanCommandProcessor : 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
|
||||
@@ -514,12 +519,12 @@ class VulkanCommandProcessor : 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,
|
||||
@@ -649,6 +654,9 @@ class VulkanCommandProcessor : 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];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user