[Vulkan] Add tesselation support
Pretty much just following the D3D12 implementation
This commit is contained in:
@@ -225,6 +225,21 @@ bool VulkanCommandProcessor::SetupContext() {
|
||||
descriptor_set_layout_bindings_constants
|
||||
[SpirvShaderTranslator::kConstantBufferFetch]
|
||||
.stageFlags = guest_shader_stages;
|
||||
// Clip plane constants - used by vertex shader (and TES for tessellation).
|
||||
descriptor_set_layout_bindings_constants
|
||||
[SpirvShaderTranslator::kConstantBufferClipPlanes]
|
||||
.stageFlags = guest_shader_vertex_stages_;
|
||||
// Tessellation constants - used by tessellation control shader, the
|
||||
// tessellation vertex shader (for index/factor processing), and the
|
||||
// tessellation evaluation shader (domain shader, which is the translated
|
||||
// Xenos vertex shader).
|
||||
descriptor_set_layout_bindings_constants
|
||||
[SpirvShaderTranslator::kConstantBufferTessellation]
|
||||
.stageFlags = device_properties.tessellationShader
|
||||
? (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT |
|
||||
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT |
|
||||
VK_SHADER_STAGE_VERTEX_BIT)
|
||||
: 0;
|
||||
descriptor_set_layout_create_info.bindingCount =
|
||||
uint32_t(xe::countof(descriptor_set_layout_bindings_constants));
|
||||
descriptor_set_layout_create_info.pBindings =
|
||||
@@ -2302,12 +2317,17 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
// Nothing to draw.
|
||||
return true;
|
||||
}
|
||||
// TODO(Triang3l): Tessellation, geometry-type-specific vertex shader,
|
||||
// vertex shader as compute.
|
||||
// TODO(Triang3l): Geometry-type-specific vertex shader, vertex shader as
|
||||
// compute.
|
||||
// Skip unsupported host vertex shader types (but allow tessellation types
|
||||
// through - they will be handled in pipeline creation or rejected there if
|
||||
// not fully supported yet).
|
||||
if (primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kVertex &&
|
||||
primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip) {
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip &&
|
||||
!Shader::IsHostVertexShaderTypeDomain(
|
||||
primitive_processing_result.host_vertex_shader_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4514,6 +4534,49 @@ bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
|
||||
current_constant_buffers_up_to_date_ |=
|
||||
UINT32_C(1) << SpirvShaderTranslator::kConstantBufferClipPlanes;
|
||||
}
|
||||
// Tessellation constants.
|
||||
// Always initialize the buffer info, even if tessellation is not active,
|
||||
// because the descriptor set write always includes all constant buffers.
|
||||
if (!(current_constant_buffers_up_to_date_ &
|
||||
(UINT32_C(1)
|
||||
<< SpirvShaderTranslator::kConstantBufferTessellation))) {
|
||||
VkDescriptorBufferInfo& buffer_info = current_constant_buffer_infos_
|
||||
[SpirvShaderTranslator::kConstantBufferTessellation];
|
||||
uint8_t* mapping = uniform_buffer_pool_->Request(
|
||||
frame_current_, sizeof(SpirvShaderTranslator::TessellationConstants),
|
||||
uniform_buffer_alignment, buffer_info.buffer, buffer_info.offset);
|
||||
if (!mapping) {
|
||||
return false;
|
||||
}
|
||||
buffer_info.range = sizeof(SpirvShaderTranslator::TessellationConstants);
|
||||
// Populate tessellation constants from registers.
|
||||
SpirvShaderTranslator::TessellationConstants tessellation_constants;
|
||||
// Tessellation factor range, plus 1.0 according to Xbox 360 docs.
|
||||
// For fractional_even partitioning (continuous mode), minimum must be
|
||||
// >= 2.0.
|
||||
float tess_factor_min =
|
||||
regs.Get<float>(XE_GPU_REG_VGT_HOS_MIN_TESS_LEVEL) + 1.0f;
|
||||
float tess_factor_max =
|
||||
regs.Get<float>(XE_GPU_REG_VGT_HOS_MAX_TESS_LEVEL) + 1.0f;
|
||||
tessellation_constants.tessellation_factor_range[0] = tess_factor_min;
|
||||
tessellation_constants.tessellation_factor_range[1] = tess_factor_max;
|
||||
tessellation_constants.padding0[0] = 0.0f;
|
||||
tessellation_constants.padding0[1] = 0.0f;
|
||||
// Vertex index processing parameters for tessellation shaders.
|
||||
auto vgt_dma_size = regs.Get<reg::VGT_DMA_SIZE>();
|
||||
tessellation_constants.vertex_index_endian =
|
||||
static_cast<uint32_t>(vgt_dma_size.swap_mode);
|
||||
tessellation_constants.vertex_index_offset =
|
||||
regs[XE_GPU_REG_VGT_INDX_OFFSET];
|
||||
tessellation_constants.vertex_index_min_max[0] =
|
||||
regs[XE_GPU_REG_VGT_MIN_VTX_INDX];
|
||||
tessellation_constants.vertex_index_min_max[1] =
|
||||
regs[XE_GPU_REG_VGT_MAX_VTX_INDX];
|
||||
std::memcpy(mapping, &tessellation_constants,
|
||||
sizeof(SpirvShaderTranslator::TessellationConstants));
|
||||
current_constant_buffers_up_to_date_ |=
|
||||
UINT32_C(1) << SpirvShaderTranslator::kConstantBufferTessellation;
|
||||
}
|
||||
// Vertex shader float constants.
|
||||
if (!(current_constant_buffers_up_to_date_ &
|
||||
(UINT32_C(1) << SpirvShaderTranslator::kConstantBufferFloatVertex))) {
|
||||
|
||||
@@ -30,6 +30,21 @@
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
// Tessellation shader bytecode.
|
||||
namespace shaders {
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/adaptive_quad_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/adaptive_triangle_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/continuous_quad_1cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/continuous_quad_4cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/continuous_triangle_1cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/continuous_triangle_3cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/discrete_quad_1cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/discrete_quad_4cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/discrete_triangle_1cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/discrete_triangle_3cp_hs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/tessellation_adaptive_vs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/tessellation_indexed_vs.h"
|
||||
} // namespace shaders
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace vulkan {
|
||||
@@ -80,6 +95,68 @@ bool VulkanPipelineCache::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
// Create tessellation shaders if tessellation is supported.
|
||||
if (vulkan_device->properties().tessellationShader) {
|
||||
// Vertex shaders for tessellation.
|
||||
tessellation_indexed_vs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::tessellation_indexed_vs,
|
||||
sizeof(shaders::tessellation_indexed_vs));
|
||||
tessellation_adaptive_vs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::tessellation_adaptive_vs,
|
||||
sizeof(shaders::tessellation_adaptive_vs));
|
||||
// Discrete mode hull shaders.
|
||||
discrete_triangle_1cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::discrete_triangle_1cp_hs,
|
||||
sizeof(shaders::discrete_triangle_1cp_hs));
|
||||
discrete_triangle_3cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::discrete_triangle_3cp_hs,
|
||||
sizeof(shaders::discrete_triangle_3cp_hs));
|
||||
discrete_quad_1cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::discrete_quad_1cp_hs,
|
||||
sizeof(shaders::discrete_quad_1cp_hs));
|
||||
discrete_quad_4cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::discrete_quad_4cp_hs,
|
||||
sizeof(shaders::discrete_quad_4cp_hs));
|
||||
// Continuous mode hull shaders.
|
||||
continuous_triangle_1cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::continuous_triangle_1cp_hs,
|
||||
sizeof(shaders::continuous_triangle_1cp_hs));
|
||||
continuous_triangle_3cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::continuous_triangle_3cp_hs,
|
||||
sizeof(shaders::continuous_triangle_3cp_hs));
|
||||
continuous_quad_1cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::continuous_quad_1cp_hs,
|
||||
sizeof(shaders::continuous_quad_1cp_hs));
|
||||
continuous_quad_4cp_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::continuous_quad_4cp_hs,
|
||||
sizeof(shaders::continuous_quad_4cp_hs));
|
||||
// Adaptive mode hull shaders.
|
||||
adaptive_triangle_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::adaptive_triangle_hs,
|
||||
sizeof(shaders::adaptive_triangle_hs));
|
||||
adaptive_quad_hs_ = ui::vulkan::util::CreateShaderModule(
|
||||
vulkan_device, shaders::adaptive_quad_hs,
|
||||
sizeof(shaders::adaptive_quad_hs));
|
||||
|
||||
// Verify all tessellation shaders were created successfully.
|
||||
if (tessellation_indexed_vs_ == VK_NULL_HANDLE ||
|
||||
tessellation_adaptive_vs_ == VK_NULL_HANDLE ||
|
||||
discrete_triangle_1cp_hs_ == VK_NULL_HANDLE ||
|
||||
discrete_triangle_3cp_hs_ == VK_NULL_HANDLE ||
|
||||
discrete_quad_1cp_hs_ == VK_NULL_HANDLE ||
|
||||
discrete_quad_4cp_hs_ == VK_NULL_HANDLE ||
|
||||
continuous_triangle_1cp_hs_ == VK_NULL_HANDLE ||
|
||||
continuous_triangle_3cp_hs_ == VK_NULL_HANDLE ||
|
||||
continuous_quad_1cp_hs_ == VK_NULL_HANDLE ||
|
||||
continuous_quad_4cp_hs_ == VK_NULL_HANDLE ||
|
||||
adaptive_triangle_hs_ == VK_NULL_HANDLE ||
|
||||
adaptive_quad_hs_ == VK_NULL_HANDLE) {
|
||||
XELOGW(
|
||||
"VulkanPipelineCache: Failed to create one or more tessellation "
|
||||
"shaders - tessellation will not be available");
|
||||
}
|
||||
}
|
||||
|
||||
// Create Vulkan pipeline cache for faster pipeline creation.
|
||||
VkPipelineCacheCreateInfo pipeline_cache_create_info = {};
|
||||
pipeline_cache_create_info.sType =
|
||||
@@ -118,6 +195,31 @@ void VulkanPipelineCache::Shutdown() {
|
||||
// Destroy all internal shaders.
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
depth_only_fragment_shader_);
|
||||
// Destroy tessellation shaders.
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
tessellation_indexed_vs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
tessellation_adaptive_vs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
discrete_triangle_1cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
discrete_triangle_3cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
discrete_quad_1cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
discrete_quad_4cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
continuous_triangle_1cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
continuous_triangle_3cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
continuous_quad_1cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
continuous_quad_4cp_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
adaptive_triangle_hs_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
adaptive_quad_hs_);
|
||||
for (const auto& geometry_shader_pair : geometry_shaders_) {
|
||||
if (geometry_shader_pair.second != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyShaderModule(device, geometry_shader_pair.second, nullptr);
|
||||
@@ -368,10 +470,34 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
||||
PipelineCreationArguments creation_arguments;
|
||||
auto& pipeline =
|
||||
*pipelines_.emplace(description, Pipeline(pipeline_layout)).first;
|
||||
// Get tessellation shaders if needed.
|
||||
VkShaderModule tessellation_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule tessellation_control_shader = VK_NULL_HANDLE;
|
||||
if (description.tessellation_mode != PipelineTessellationMode::kNone) {
|
||||
tessellation_vertex_shader =
|
||||
GetTessellationVertexShader(description.tessellation_mode);
|
||||
bool use_control_point_count =
|
||||
(description.tessellation_mode == PipelineTessellationMode::kAdaptive);
|
||||
tessellation_control_shader = GetTessellationControlShader(
|
||||
description.tessellation_mode, description.tessellation_patch,
|
||||
use_control_point_count);
|
||||
if (tessellation_vertex_shader == VK_NULL_HANDLE ||
|
||||
tessellation_control_shader == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanPipelineCache: Failed to get tessellation shaders for mode {} "
|
||||
"patch {}",
|
||||
static_cast<uint32_t>(description.tessellation_mode),
|
||||
static_cast<uint32_t>(description.tessellation_patch));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
creation_arguments.pipeline = &pipeline;
|
||||
creation_arguments.vertex_shader = vertex_shader;
|
||||
creation_arguments.pixel_shader = pixel_shader;
|
||||
creation_arguments.geometry_shader = geometry_shader;
|
||||
creation_arguments.tessellation_vertex_shader = tessellation_vertex_shader;
|
||||
creation_arguments.tessellation_control_shader = tessellation_control_shader;
|
||||
creation_arguments.render_pass = render_pass;
|
||||
if (!EnsurePipelineCreated(creation_arguments)) {
|
||||
return false;
|
||||
@@ -556,45 +682,90 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
||||
// without them.
|
||||
PipelineGeometryShader geometry_shader = PipelineGeometryShader::kNone;
|
||||
PipelinePrimitiveTopology primitive_topology;
|
||||
switch (primitive_processing_result.host_primitive_type) {
|
||||
case xenos::PrimitiveType::kPointList:
|
||||
geometry_shader = PipelineGeometryShader::kPointList;
|
||||
primitive_topology = PipelinePrimitiveTopology::kPointList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kLineList:
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kLineStrip:
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineStrip;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleList:
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleFan:
|
||||
// The check should be performed at primitive processing time.
|
||||
assert_true(device_properties.triangleFans);
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleStrip:
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleStrip;
|
||||
break;
|
||||
case xenos::PrimitiveType::kRectangleList:
|
||||
// Only use geometry shader if not using the fallback AsTriangleStrip
|
||||
// vertex shader type (which is used when geometry shaders aren't
|
||||
// supported).
|
||||
if (primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kRectangleListAsTriangleStrip) {
|
||||
geometry_shader = PipelineGeometryShader::kRectangleList;
|
||||
}
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kQuadList:
|
||||
geometry_shader = PipelineGeometryShader::kQuadList;
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineListWithAdjacency;
|
||||
break;
|
||||
default:
|
||||
// TODO(Triang3l): All primitive types and tessellation.
|
||||
return false;
|
||||
|
||||
// Handle tessellated and non-tessellated draws separately, like D3D12.
|
||||
if (primitive_processing_result.IsTessellated()) {
|
||||
// Tessellation is enabled - use patch list topology.
|
||||
primitive_topology = PipelinePrimitiveTopology::kPatchList;
|
||||
|
||||
// Get tessellation mode from registers.
|
||||
auto vgt_hos_cntl = regs.Get<reg::VGT_HOS_CNTL>();
|
||||
switch (vgt_hos_cntl.tess_mode) {
|
||||
case xenos::TessellationMode::kDiscrete:
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kDiscrete;
|
||||
break;
|
||||
case xenos::TessellationMode::kContinuous:
|
||||
description_out.tessellation_mode =
|
||||
PipelineTessellationMode::kContinuous;
|
||||
break;
|
||||
case xenos::TessellationMode::kAdaptive:
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kAdaptive;
|
||||
break;
|
||||
default:
|
||||
// Unknown tessellation mode, fall back to discrete.
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kDiscrete;
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine patch type based on primitive type.
|
||||
switch (primitive_processing_result.host_primitive_type) {
|
||||
case xenos::PrimitiveType::kTriangleList:
|
||||
case xenos::PrimitiveType::kTrianglePatch:
|
||||
description_out.tessellation_patch =
|
||||
PipelineTessellationPatchType::kTriangle;
|
||||
break;
|
||||
case xenos::PrimitiveType::kQuadList:
|
||||
case xenos::PrimitiveType::kQuadPatch:
|
||||
description_out.tessellation_patch =
|
||||
PipelineTessellationPatchType::kQuad;
|
||||
break;
|
||||
default:
|
||||
XELOGE("VulkanPipelineCache: Unsupported tessellated primitive type {}",
|
||||
uint32_t(primitive_processing_result.host_primitive_type));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Non-tessellated draw.
|
||||
switch (primitive_processing_result.host_primitive_type) {
|
||||
case xenos::PrimitiveType::kPointList:
|
||||
geometry_shader = PipelineGeometryShader::kPointList;
|
||||
primitive_topology = PipelinePrimitiveTopology::kPointList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kLineList:
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kLineStrip:
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineStrip;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleList:
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleFan:
|
||||
// The check should be performed at primitive processing time.
|
||||
assert_true(device_properties.triangleFans);
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleStrip:
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleStrip;
|
||||
break;
|
||||
case xenos::PrimitiveType::kRectangleList:
|
||||
// Only use geometry shader if not using the fallback AsTriangleStrip
|
||||
// vertex shader type (which is used when geometry shaders aren't
|
||||
// supported).
|
||||
if (primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kRectangleListAsTriangleStrip) {
|
||||
geometry_shader = PipelineGeometryShader::kRectangleList;
|
||||
}
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kQuadList:
|
||||
geometry_shader = PipelineGeometryShader::kQuadList;
|
||||
primitive_topology = PipelinePrimitiveTopology::kLineListWithAdjacency;
|
||||
break;
|
||||
default:
|
||||
// TODO(Triang3l): Remaining primitive types.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
description_out.geometry_shader = geometry_shader;
|
||||
description_out.primitive_topology = primitive_topology;
|
||||
@@ -1804,6 +1975,55 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
return shader_module;
|
||||
}
|
||||
|
||||
VkShaderModule VulkanPipelineCache::GetTessellationControlShader(
|
||||
PipelineTessellationMode mode, PipelineTessellationPatchType patch_type,
|
||||
bool use_control_point_count) const {
|
||||
if (mode == PipelineTessellationMode::kNone ||
|
||||
patch_type == PipelineTessellationPatchType::kNone) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case PipelineTessellationMode::kDiscrete:
|
||||
if (patch_type == PipelineTessellationPatchType::kTriangle) {
|
||||
return use_control_point_count ? discrete_triangle_3cp_hs_
|
||||
: discrete_triangle_1cp_hs_;
|
||||
} else {
|
||||
return use_control_point_count ? discrete_quad_4cp_hs_
|
||||
: discrete_quad_1cp_hs_;
|
||||
}
|
||||
case PipelineTessellationMode::kContinuous:
|
||||
if (patch_type == PipelineTessellationPatchType::kTriangle) {
|
||||
return use_control_point_count ? continuous_triangle_3cp_hs_
|
||||
: continuous_triangle_1cp_hs_;
|
||||
} else {
|
||||
return use_control_point_count ? continuous_quad_4cp_hs_
|
||||
: continuous_quad_1cp_hs_;
|
||||
}
|
||||
case PipelineTessellationMode::kAdaptive:
|
||||
// Adaptive mode always uses per-corner control points.
|
||||
if (patch_type == PipelineTessellationPatchType::kTriangle) {
|
||||
return adaptive_triangle_hs_;
|
||||
} else {
|
||||
return adaptive_quad_hs_;
|
||||
}
|
||||
default:
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
VkShaderModule VulkanPipelineCache::GetTessellationVertexShader(
|
||||
PipelineTessellationMode mode) const {
|
||||
if (mode == PipelineTessellationMode::kNone) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
// Adaptive mode reads edge factors from index buffer; other modes pass
|
||||
// vertex indices.
|
||||
return (mode == PipelineTessellationMode::kAdaptive)
|
||||
? tessellation_adaptive_vs_
|
||||
: tessellation_indexed_vs_;
|
||||
}
|
||||
|
||||
bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
const PipelineCreationArguments& creation_arguments) {
|
||||
if (creation_arguments.pipeline->second.pipeline != VK_NULL_HANDLE) {
|
||||
@@ -1839,26 +2059,77 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 3> shader_stages;
|
||||
bool is_tessellated =
|
||||
description.tessellation_mode != PipelineTessellationMode::kNone;
|
||||
|
||||
// Up to 5 shader stages: VS, TCS, TES, GS, FS.
|
||||
std::array<VkPipelineShaderStageCreateInfo, 5> shader_stages;
|
||||
uint32_t shader_stage_count = 0;
|
||||
|
||||
// Vertex or tessellation evaluation shader.
|
||||
// Vertex shader or tessellation evaluation shader.
|
||||
assert_true(creation_arguments.vertex_shader->is_translated());
|
||||
if (!creation_arguments.vertex_shader->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_vertex =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_vertex.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stage_vertex.pNext = nullptr;
|
||||
shader_stage_vertex.flags = 0;
|
||||
shader_stage_vertex.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shader_stage_vertex.module =
|
||||
creation_arguments.vertex_shader->shader_module();
|
||||
assert_true(shader_stage_vertex.module != VK_NULL_HANDLE);
|
||||
shader_stage_vertex.pName = "main";
|
||||
shader_stage_vertex.pSpecializationInfo = nullptr;
|
||||
|
||||
if (is_tessellated) {
|
||||
// For tessellation: use our pre-compiled VS for passing data to TCS,
|
||||
// then TCS (hull shader), then the translated Xenos vertex shader as TES
|
||||
// (domain shader).
|
||||
|
||||
// Tessellation vertex shader (passes indices to TCS).
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_tess_vs =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_tess_vs.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stage_tess_vs.pNext = nullptr;
|
||||
shader_stage_tess_vs.flags = 0;
|
||||
shader_stage_tess_vs.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shader_stage_tess_vs.module = creation_arguments.tessellation_vertex_shader;
|
||||
shader_stage_tess_vs.pName = "main";
|
||||
shader_stage_tess_vs.pSpecializationInfo = nullptr;
|
||||
|
||||
// Tessellation control shader (hull shader).
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_tcs =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_tcs.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stage_tcs.pNext = nullptr;
|
||||
shader_stage_tcs.flags = 0;
|
||||
shader_stage_tcs.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||
shader_stage_tcs.module = creation_arguments.tessellation_control_shader;
|
||||
shader_stage_tcs.pName = "main";
|
||||
shader_stage_tcs.pSpecializationInfo = nullptr;
|
||||
|
||||
// Tessellation evaluation shader (domain shader) - the translated Xenos
|
||||
// vertex shader.
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_tes =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_tes.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stage_tes.pNext = nullptr;
|
||||
shader_stage_tes.flags = 0;
|
||||
shader_stage_tes.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||
shader_stage_tes.module = creation_arguments.vertex_shader->shader_module();
|
||||
assert_true(shader_stage_tes.module != VK_NULL_HANDLE);
|
||||
shader_stage_tes.pName = "main";
|
||||
shader_stage_tes.pSpecializationInfo = nullptr;
|
||||
} else {
|
||||
// Non-tessellated: standard vertex shader.
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_vertex =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_vertex.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stage_vertex.pNext = nullptr;
|
||||
shader_stage_vertex.flags = 0;
|
||||
shader_stage_vertex.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shader_stage_vertex.module =
|
||||
creation_arguments.vertex_shader->shader_module();
|
||||
assert_true(shader_stage_vertex.module != VK_NULL_HANDLE);
|
||||
shader_stage_vertex.pName = "main";
|
||||
shader_stage_vertex.pSpecializationInfo = nullptr;
|
||||
}
|
||||
|
||||
// Geometry shader.
|
||||
if (creation_arguments.geometry_shader != VK_NULL_HANDLE) {
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_geometry =
|
||||
@@ -2174,6 +2445,27 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE;
|
||||
}
|
||||
|
||||
// Tessellation state (only used when tessellation is active).
|
||||
VkPipelineTessellationStateCreateInfo tessellation_state = {};
|
||||
tessellation_state.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
if (is_tessellated) {
|
||||
// Determine patch control point count based on mode and patch type.
|
||||
// For adaptive mode, we use the actual patch corner count (3 for triangles,
|
||||
// 4 for quads) since each control point has its own edge factor.
|
||||
// For discrete/continuous modes, we use 1 control point (the Xenos vertex
|
||||
// shader receives the patch index and computes all corners internally).
|
||||
if (description.tessellation_mode == PipelineTessellationMode::kAdaptive) {
|
||||
tessellation_state.patchControlPoints =
|
||||
(description.tessellation_patch ==
|
||||
PipelineTessellationPatchType::kTriangle)
|
||||
? 3
|
||||
: 4;
|
||||
} else {
|
||||
tessellation_state.patchControlPoints = 1;
|
||||
}
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info;
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_create_info.pNext = nullptr;
|
||||
@@ -2182,7 +2474,8 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
pipeline_create_info.pStages = shader_stages.data();
|
||||
pipeline_create_info.pVertexInputState = &vertex_input_state;
|
||||
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
|
||||
pipeline_create_info.pTessellationState = nullptr;
|
||||
pipeline_create_info.pTessellationState =
|
||||
is_tessellated ? &tessellation_state : nullptr;
|
||||
pipeline_create_info.pViewportState = &viewport_state;
|
||||
pipeline_create_info.pRasterizationState = &rasterization_state;
|
||||
pipeline_create_info.pMultisampleState = &multisample_state;
|
||||
@@ -2199,19 +2492,23 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
VkPipeline pipeline;
|
||||
if (dfn.vkCreateGraphicsPipelines(device, vk_pipeline_cache_, 1,
|
||||
&pipeline_create_info, nullptr,
|
||||
&pipeline) != VK_SUCCESS) {
|
||||
// TODO(Triang3l): Move these error messages outside.
|
||||
/* if (creation_arguments.pixel_shader) {
|
||||
VkResult result = dfn.vkCreateGraphicsPipelines(
|
||||
device, vk_pipeline_cache_, 1, &pipeline_create_info, nullptr, &pipeline);
|
||||
if (result != VK_SUCCESS) {
|
||||
if (creation_arguments.pixel_shader) {
|
||||
XELOGE(
|
||||
"Failed to create graphics pipeline with VS {:016X}, PS {:016X}",
|
||||
"Failed to create graphics pipeline with VS {:016X}, PS {:016X} "
|
||||
"(tessellated={}, result={})",
|
||||
creation_arguments.vertex_shader->shader().ucode_data_hash(),
|
||||
creation_arguments.pixel_shader->shader().ucode_data_hash());
|
||||
creation_arguments.pixel_shader->shader().ucode_data_hash(),
|
||||
is_tessellated, static_cast<int>(result));
|
||||
} else {
|
||||
XELOGE("Failed to create graphics pipeline with VS {:016X}",
|
||||
creation_arguments.vertex_shader->shader().ucode_data_hash());
|
||||
} */
|
||||
XELOGE(
|
||||
"Failed to create graphics pipeline with VS {:016X} "
|
||||
"(tessellated={}, result={})",
|
||||
creation_arguments.vertex_shader->shader().ucode_data_hash(),
|
||||
is_tessellated, static_cast<int>(result));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
creation_arguments.pipeline->second.pipeline = pipeline;
|
||||
|
||||
@@ -114,6 +114,22 @@ class VulkanPipelineCache {
|
||||
kPoint,
|
||||
};
|
||||
|
||||
// Tessellation mode for pipeline creation.
|
||||
// Must match the TCS (hull shader) selection logic.
|
||||
enum class PipelineTessellationMode : uint32_t {
|
||||
kNone,
|
||||
kDiscrete, // Integer tessellation factors.
|
||||
kContinuous, // Fractional (fractional_even) tessellation factors.
|
||||
kAdaptive, // Per-edge factors from index buffer.
|
||||
};
|
||||
|
||||
// Tessellation primitive type.
|
||||
enum class PipelineTessellationPatchType : uint32_t {
|
||||
kNone,
|
||||
kTriangle,
|
||||
kQuad,
|
||||
};
|
||||
|
||||
enum class PipelineBlendFactor : uint32_t {
|
||||
kZero,
|
||||
kOne,
|
||||
@@ -152,10 +168,12 @@ class VulkanPipelineCache {
|
||||
VulkanRenderTargetCache::RenderPassKey render_pass_key;
|
||||
|
||||
// Shader stages.
|
||||
PipelineGeometryShader geometry_shader : 2; // 2
|
||||
PipelineGeometryShader geometry_shader : 2; // 2
|
||||
PipelineTessellationMode tessellation_mode : 2; // 4
|
||||
PipelineTessellationPatchType tessellation_patch : 2; // 6
|
||||
// Input assembly.
|
||||
PipelinePrimitiveTopology primitive_topology : 3; // 5
|
||||
uint32_t primitive_restart : 1; // 6
|
||||
PipelinePrimitiveTopology primitive_topology : 3; // 9
|
||||
uint32_t primitive_restart : 1; // 10
|
||||
// Rasterization.
|
||||
uint32_t depth_clamp_enable : 1; // 7
|
||||
PipelinePolygonMode polygon_mode : 2; // 9
|
||||
@@ -216,6 +234,9 @@ class VulkanPipelineCache {
|
||||
const VulkanShader::VulkanTranslation* vertex_shader;
|
||||
const VulkanShader::VulkanTranslation* pixel_shader;
|
||||
VkShaderModule geometry_shader;
|
||||
// Tessellation shaders (only used when tessellation is active).
|
||||
VkShaderModule tessellation_vertex_shader; // VS for passing data to TCS.
|
||||
VkShaderModule tessellation_control_shader; // TCS (hull shader).
|
||||
VkRenderPass render_pass;
|
||||
};
|
||||
|
||||
@@ -271,6 +292,15 @@ class VulkanPipelineCache {
|
||||
GeometryShaderKey& key_out);
|
||||
VkShaderModule GetGeometryShader(GeometryShaderKey key);
|
||||
|
||||
// Get the appropriate tessellation control shader (hull shader) module.
|
||||
VkShaderModule GetTessellationControlShader(
|
||||
PipelineTessellationMode mode, PipelineTessellationPatchType patch_type,
|
||||
bool use_control_point_count) const;
|
||||
|
||||
// Get the appropriate tessellation vertex shader module.
|
||||
VkShaderModule GetTessellationVertexShader(
|
||||
PipelineTessellationMode mode) const;
|
||||
|
||||
// Can be called from creation threads - all needed data must be fully set up
|
||||
// at the point of the call: shaders must be translated, pipeline layout and
|
||||
// render pass objects must be available.
|
||||
@@ -317,6 +347,26 @@ class VulkanPipelineCache {
|
||||
// shader interlock when no Xenos pixel shader provided.
|
||||
VkShaderModule depth_only_fragment_shader_ = VK_NULL_HANDLE;
|
||||
|
||||
// Tessellation shaders.
|
||||
// Vertex shaders for tessellation - pass indices/factors to TCS.
|
||||
VkShaderModule tessellation_indexed_vs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule tessellation_adaptive_vs_ = VK_NULL_HANDLE;
|
||||
// Tessellation control shaders (hull shaders) for different modes and
|
||||
// primitive types.
|
||||
// Discrete mode (integer tessellation factors).
|
||||
VkShaderModule discrete_triangle_1cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule discrete_triangle_3cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule discrete_quad_1cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule discrete_quad_4cp_hs_ = VK_NULL_HANDLE;
|
||||
// Continuous mode (fractional_even tessellation factors).
|
||||
VkShaderModule continuous_triangle_1cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule continuous_triangle_3cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule continuous_quad_1cp_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule continuous_quad_4cp_hs_ = VK_NULL_HANDLE;
|
||||
// Adaptive mode (per-edge factors from index buffer).
|
||||
VkShaderModule adaptive_triangle_hs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule adaptive_quad_hs_ = VK_NULL_HANDLE;
|
||||
|
||||
// Vulkan pipeline cache for faster pipeline creation.
|
||||
VkPipelineCache vk_pipeline_cache_ = VK_NULL_HANDLE;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user