[GPU] Shader::HostVertexShaderType enum for domain shader types

This commit is contained in:
Triang3l 2020-04-06 00:03:23 +03:00
parent 8da8044771
commit 4b9f63cdf1
12 changed files with 492 additions and 443 deletions

View File

@ -177,14 +177,17 @@ void D3D12CommandProcessor::SubmitBarriers() {
} }
ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature( ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature(
const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader, const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader) {
bool tessellated) {
assert_true(vertex_shader->is_translated()); assert_true(vertex_shader->is_translated());
assert_true(pixel_shader == nullptr || pixel_shader->is_translated()); assert_true(pixel_shader == nullptr || pixel_shader->is_translated());
D3D12_SHADER_VISIBILITY vertex_visibility = D3D12_SHADER_VISIBILITY vertex_visibility;
tessellated ? D3D12_SHADER_VISIBILITY_DOMAIN if (vertex_shader->host_vertex_shader_type() !=
: D3D12_SHADER_VISIBILITY_VERTEX; Shader::HostVertexShaderType::kVertex) {
vertex_visibility = D3D12_SHADER_VISIBILITY_DOMAIN;
} else {
vertex_visibility = D3D12_SHADER_VISIBILITY_VERTEX;
}
uint32_t texture_count_vertex, sampler_count_vertex; uint32_t texture_count_vertex, sampler_count_vertex;
vertex_shader->GetTextureSRVs(texture_count_vertex); vertex_shader->GetTextureSRVs(texture_count_vertex);
@ -207,7 +210,8 @@ ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature(
index_offset += D3D12Shader::kMaxTextureSRVIndexBits; index_offset += D3D12Shader::kMaxTextureSRVIndexBits;
index |= sampler_count_vertex << index_offset; index |= sampler_count_vertex << index_offset;
index_offset += D3D12Shader::kMaxSamplerBindingIndexBits; index_offset += D3D12Shader::kMaxSamplerBindingIndexBits;
index |= (tessellated ? 1 : 0) << index_offset; index |= uint32_t(vertex_visibility == D3D12_SHADER_VISIBILITY_DOMAIN)
<< index_offset;
++index_offset; ++index_offset;
assert_true(index_offset <= 32); assert_true(index_offset <= 32);
@ -1284,15 +1288,6 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
return true; return true;
} }
// Check if using tessellation to get the correct primitive type.
bool tessellated;
if (major_mode_explicit) {
tessellated = regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select ==
xenos::VGTOutputPath::kTessellationEnable;
} else {
tessellated = false;
}
// Shaders will have already been defined by previous loads. // Shaders will have already been defined by previous loads.
// We need them to do just about anything so validate here. // We need them to do just about anything so validate here.
auto vertex_shader = static_cast<D3D12Shader*>(active_vertex_shader()); auto vertex_shader = static_cast<D3D12Shader*>(active_vertex_shader());
@ -1308,13 +1303,21 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
// Need a pixel shader in normal color mode. // Need a pixel shader in normal color mode.
return false; return false;
} }
// Get tessellation info for the current draw for vertex shader translation.
Shader::HostVertexShaderType host_vertex_shader_type =
pipeline_cache_->GetHostVertexShaderTypeIfValid();
if (host_vertex_shader_type == Shader::HostVertexShaderType(-1)) {
return false;
}
// Translate the shaders now to get memexport configuration and color mask, // Translate the shaders now to get memexport configuration and color mask,
// which is needed by the render target cache, to check the possibility of // which is needed by the render target cache, to check the possibility of
// doing early depth/stencil, and also to get used textures and samplers. // doing early depth/stencil, and also to get used textures and samplers.
if (!pipeline_cache_->EnsureShadersTranslated(vertex_shader, pixel_shader, if (!pipeline_cache_->EnsureShadersTranslated(vertex_shader, pixel_shader,
tessellated, primitive_type)) { host_vertex_shader_type)) {
return false; return false;
} }
bool tessellated =
host_vertex_shader_type != Shader::HostVertexShaderType::kVertex;
// Check if memexport is used. If it is, we can't skip draw calls that have no // Check if memexport is used. If it is, we can't skip draw calls that have no
// visual effect. // visual effect.
@ -1348,39 +1351,24 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
// Set up primitive topology. // Set up primitive topology.
bool indexed = index_buffer_info != nullptr && index_buffer_info->guest_base; bool indexed = index_buffer_info != nullptr && index_buffer_info->guest_base;
bool adaptive_tessellation =
host_vertex_shader_type ==
Shader::HostVertexShaderType::kLineDomainAdaptive ||
host_vertex_shader_type ==
Shader::HostVertexShaderType::kTriangleDomainAdaptive ||
host_vertex_shader_type ==
Shader::HostVertexShaderType::kQuadDomainAdaptive;
// Adaptive tessellation requires an index buffer, but it contains per-edge // Adaptive tessellation requires an index buffer, but it contains per-edge
// tessellation factors (as floats) instead of control point indices. // tessellation factors (as floats) instead of control point indices.
bool adaptive_tessellation; assert_true(!adaptive_tessellation ||
if (tessellated) { (indexed && index_buffer_info->format == IndexFormat::kInt32));
xenos::TessellationMode tessellation_mode =
regs.Get<reg::VGT_HOS_CNTL>().tess_mode;
adaptive_tessellation =
tessellation_mode == xenos::TessellationMode::kAdaptive;
if (adaptive_tessellation &&
(!indexed || index_buffer_info->format != IndexFormat::kInt32)) {
return false;
}
// TODO(Triang3l): Implement all tessellation modes if games using any other
// than adaptive are found. The biggest question about them is what is being
// passed to vertex shader registers, especially if patches are drawn with
// an index buffer.
// https://www.slideshare.net/blackdevilvikas/next-generation-graphics-programming-on-xbox-360
if (tessellation_mode != xenos::TessellationMode::kAdaptive) {
XELOGE(
"Tessellation mode %u is not implemented yet, only adaptive is "
"partially available now - report the game to Xenia developers!",
uint32_t(tessellation_mode));
return false;
}
} else {
adaptive_tessellation = false;
}
PrimitiveType primitive_type_converted; PrimitiveType primitive_type_converted;
D3D_PRIMITIVE_TOPOLOGY primitive_topology; D3D_PRIMITIVE_TOPOLOGY primitive_topology;
if (tessellated) { if (tessellated) {
primitive_type_converted = primitive_type; primitive_type_converted = primitive_type;
switch (primitive_type_converted) { switch (primitive_type_converted) {
// TODO(Triang3l): Support line patches. // TODO(Triang3l): Support all kinds of patches if found in games.
case PrimitiveType::kTriangleList:
case PrimitiveType::kTrianglePatch: case PrimitiveType::kTrianglePatch:
primitive_topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST; primitive_topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
break; break;
@ -1422,7 +1410,7 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
deferred_command_list_->D3DIASetPrimitiveTopology(primitive_topology); deferred_command_list_->D3DIASetPrimitiveTopology(primitive_topology);
} }
uint32_t line_loop_closing_index; uint32_t line_loop_closing_index;
if (!tessellated && primitive_type == PrimitiveType::kLineLoop && !indexed && if (primitive_type == PrimitiveType::kLineLoop && !indexed &&
index_count >= 3) { index_count >= 3) {
// Add a vertex to close the loop, and make the vertex shader replace its // Add a vertex to close the loop, and make the vertex shader replace its
// index (before adding the offset) with 0 to fetch the first vertex again. // index (before adding the offset) with 0 to fetch the first vertex again.
@ -1455,7 +1443,7 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
void* pipeline_handle; void* pipeline_handle;
ID3D12RootSignature* root_signature; ID3D12RootSignature* root_signature;
if (!pipeline_cache_->ConfigurePipeline( if (!pipeline_cache_->ConfigurePipeline(
vertex_shader, pixel_shader, tessellated, primitive_type_converted, vertex_shader, pixel_shader, primitive_type_converted,
indexed ? index_buffer_info->format : IndexFormat::kInt16, early_z, indexed ? index_buffer_info->format : IndexFormat::kInt16, early_z,
pipeline_render_targets, &pipeline_handle, &root_signature)) { pipeline_render_targets, &pipeline_handle, &root_signature)) {
return false; return false;

View File

@ -95,8 +95,7 @@ class D3D12CommandProcessor : public CommandProcessor {
// Finds or creates root signature for a pipeline. // Finds or creates root signature for a pipeline.
ID3D12RootSignature* GetRootSignature(const D3D12Shader* vertex_shader, ID3D12RootSignature* GetRootSignature(const D3D12Shader* vertex_shader,
const D3D12Shader* pixel_shader, const D3D12Shader* pixel_shader);
bool tessellated);
ui::d3d12::UploadBufferPool* GetConstantBufferPool() const { ui::d3d12::UploadBufferPool* GetConstantBufferPool() const {
return constant_buffer_pool_.get(); return constant_buffer_pool_.get();

View File

@ -272,9 +272,10 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
break; break;
} }
assert_not_null(shader_to_translate.second); assert_not_null(shader_to_translate.second);
if (!TranslateShader(translator, shader_to_translate.second, if (!TranslateShader(
translator, shader_to_translate.second,
shader_to_translate.first.sq_program_cntl, shader_to_translate.first.sq_program_cntl,
shader_to_translate.first.patch_primitive_type)) { shader_to_translate.first.host_vertex_shader_type)) {
std::unique_lock<std::mutex> lock(shaders_failed_to_translate_mutex); std::unique_lock<std::mutex> lock(shaders_failed_to_translate_mutex);
shaders_failed_to_translate.push_back(shader_to_translate.second); shaders_failed_to_translate.push_back(shader_to_translate.second);
} }
@ -516,8 +517,7 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
pipeline_runtime_description.root_signature = pipeline_runtime_description.root_signature =
command_processor_->GetRootSignature( command_processor_->GetRootSignature(
pipeline_runtime_description.vertex_shader, pipeline_runtime_description.vertex_shader,
pipeline_runtime_description.pixel_shader, pipeline_runtime_description.pixel_shader);
pipeline_description.patch_type != PipelinePatchType::kNone);
if (!pipeline_runtime_description.root_signature) { if (!pipeline_runtime_description.root_signature) {
continue; continue;
} }
@ -712,10 +712,59 @@ D3D12Shader* PipelineCache::LoadShader(ShaderType shader_type,
return shader; return shader;
} }
bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader, Shader::HostVertexShaderType PipelineCache::GetHostVertexShaderTypeIfValid()
D3D12Shader* pixel_shader, const {
bool tessellated, auto& regs = *register_file_;
PrimitiveType primitive_type) { auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
if (!xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type)) {
// VGT_OUTPUT_PATH_CNTL and HOS registers are ignored in implicit major
// mode.
return Shader::HostVertexShaderType::kVertex;
}
if (regs.Get<reg::VGT_OUTPUT_PATH_CNTL>().path_select !=
xenos::VGTOutputPath::kTessellationEnable) {
return Shader::HostVertexShaderType::kVertex;
}
xenos::TessellationMode tessellation_mode =
regs.Get<reg::VGT_HOS_CNTL>().tess_mode;
switch (vgt_draw_initiator.prim_type) {
// case PrimitiveType::kTriangleList:
// switch (tessellation_mode) {
// case xenos::TessellationMode::kDiscrete:
// // Call of Duty 3 - green terrain in the first mission.
// case xenos::TessellationMode::kContinuous:
// // Viva Pinata - something on the start screen.
// return Shader::HostVertexShaderType::kTriangleDomainConstant;
// }
// break;
// TODO(Triang3l): Support non-adaptive tessellation.
case PrimitiveType::kTrianglePatch:
if (tessellation_mode == xenos::TessellationMode::kAdaptive) {
// Banjo-Kazooie: Nuts & Bolts - water.
// Halo 3 - water.
return Shader::HostVertexShaderType::kTriangleDomainAdaptive;
}
break;
case PrimitiveType::kQuadPatch:
if (tessellation_mode == xenos::TessellationMode::kAdaptive) {
// Viva Pinata - something on the start screen.
return Shader::HostVertexShaderType::kQuadDomainAdaptive;
}
break;
// TODO(Triang3l): Support line patches and non-adaptive quad
// tessellation.
}
XELOGE(
"Unsupported tessellation mode %u for primitive type %u. Report the game "
"to Xenia developers!",
uint32_t(tessellation_mode), uint32_t(vgt_draw_initiator.prim_type));
return Shader::HostVertexShaderType(-1);
}
bool PipelineCache::EnsureShadersTranslated(
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
Shader::HostVertexShaderType host_vertex_shader_type) {
auto& regs = *register_file_; auto& regs = *register_file_;
// These are the constant base addresses/ranges for shaders. // These are the constant base addresses/ranges for shaders.
@ -734,12 +783,9 @@ bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
xenos::VertexShaderExportMode::kPosition2VectorsEdgeKill); xenos::VertexShaderExportMode::kPosition2VectorsEdgeKill);
assert_false(sq_program_cntl.gen_index_vtx); assert_false(sq_program_cntl.gen_index_vtx);
PrimitiveType patch_primitive_type =
tessellated ? primitive_type : PrimitiveType::kNone;
if (!vertex_shader->is_translated()) { if (!vertex_shader->is_translated()) {
if (!TranslateShader(*shader_translator_, vertex_shader, sq_program_cntl, if (!TranslateShader(*shader_translator_, vertex_shader, sq_program_cntl,
patch_primitive_type)) { host_vertex_shader_type)) {
XELOGE("Failed to translate the vertex shader!"); XELOGE("Failed to translate the vertex shader!");
return false; return false;
} }
@ -756,8 +802,7 @@ bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
} }
if (pixel_shader != nullptr && !pixel_shader->is_translated()) { if (pixel_shader != nullptr && !pixel_shader->is_translated()) {
if (!TranslateShader(*shader_translator_, pixel_shader, sq_program_cntl, if (!TranslateShader(*shader_translator_, pixel_shader, sq_program_cntl)) {
patch_primitive_type)) {
XELOGE("Failed to translate the pixel shader!"); XELOGE("Failed to translate the pixel shader!");
return false; return false;
} }
@ -777,7 +822,7 @@ bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
} }
bool PipelineCache::ConfigurePipeline( bool PipelineCache::ConfigurePipeline(
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
PrimitiveType primitive_type, IndexFormat index_format, bool early_z, PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
const RenderTargetCache::PipelineRenderTarget render_targets[5], const RenderTargetCache::PipelineRenderTarget render_targets[5],
void** pipeline_state_handle_out, void** pipeline_state_handle_out,
@ -790,9 +835,9 @@ bool PipelineCache::ConfigurePipeline(
assert_not_null(root_signature_out); assert_not_null(root_signature_out);
PipelineRuntimeDescription runtime_description; PipelineRuntimeDescription runtime_description;
if (!GetCurrentStateDescription(vertex_shader, pixel_shader, tessellated, if (!GetCurrentStateDescription(vertex_shader, pixel_shader, primitive_type,
primitive_type, index_format, early_z, index_format, early_z, render_targets,
render_targets, runtime_description)) { runtime_description)) {
return false; return false;
} }
PipelineDescription& description = runtime_description.description; PipelineDescription& description = runtime_description.description;
@ -819,8 +864,9 @@ bool PipelineCache::ConfigurePipeline(
} }
} }
if (!EnsureShadersTranslated(vertex_shader, pixel_shader, tessellated, if (!EnsureShadersTranslated(
primitive_type)) { vertex_shader, pixel_shader,
Shader::HostVertexShaderType(description.host_vertex_shader_type))) {
return false; return false;
} }
@ -864,13 +910,13 @@ bool PipelineCache::ConfigurePipeline(
return true; return true;
} }
bool PipelineCache::TranslateShader(DxbcShaderTranslator& translator, bool PipelineCache::TranslateShader(
D3D12Shader* shader, DxbcShaderTranslator& translator, D3D12Shader* shader,
reg::SQ_PROGRAM_CNTL cntl, reg::SQ_PROGRAM_CNTL cntl,
PrimitiveType patch_primitive_type) { Shader::HostVertexShaderType host_vertex_shader_type) {
// Perform translation. // Perform translation.
// If this fails the shader will be marked as invalid and ignored later. // If this fails the shader will be marked as invalid and ignored later.
if (!translator.Translate(shader, patch_primitive_type, cntl)) { if (!translator.Translate(shader, cntl, host_vertex_shader_type)) {
XELOGE("Shader %.16" PRIX64 " translation failed; marking as ignored", XELOGE("Shader %.16" PRIX64 " translation failed; marking as ignored",
shader->ucode_data_hash()); shader->ucode_data_hash());
return false; return false;
@ -923,7 +969,7 @@ bool PipelineCache::TranslateShader(DxbcShaderTranslator& translator,
} }
bool PipelineCache::GetCurrentStateDescription( bool PipelineCache::GetCurrentStateDescription(
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
PrimitiveType primitive_type, IndexFormat index_format, bool early_z, PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
const RenderTargetCache::PipelineRenderTarget render_targets[5], const RenderTargetCache::PipelineRenderTarget render_targets[5],
PipelineRuntimeDescription& runtime_description_out) { PipelineRuntimeDescription& runtime_description_out) {
@ -931,14 +977,13 @@ bool PipelineCache::GetCurrentStateDescription(
auto& regs = *register_file_; auto& regs = *register_file_;
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>(); auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
bool primitive_two_faced = IsPrimitiveTwoFaced(tessellated, primitive_type);
// Initialize all unused fields to zero for comparison/hashing. // Initialize all unused fields to zero for comparison/hashing.
std::memset(&runtime_description_out, 0, sizeof(runtime_description_out)); std::memset(&runtime_description_out, 0, sizeof(runtime_description_out));
// Root signature. // Root signature.
runtime_description_out.root_signature = command_processor_->GetRootSignature( runtime_description_out.root_signature =
vertex_shader, pixel_shader, tessellated); command_processor_->GetRootSignature(vertex_shader, pixel_shader);
if (runtime_description_out.root_signature == nullptr) { if (runtime_description_out.root_signature == nullptr) {
return false; return false;
} }
@ -962,48 +1007,18 @@ bool PipelineCache::GetCurrentStateDescription(
description_out.strip_cut_index = PipelineStripCutIndex::kNone; description_out.strip_cut_index = PipelineStripCutIndex::kNone;
} }
// Primitive topology type, tessellation mode and geometry shader. // Host vertex shader type and primitive topology.
if (tessellated) { Shader::HostVertexShaderType host_vertex_shader_type =
xenos::TessellationMode tessellation_mode = GetHostVertexShaderTypeIfValid();
regs.Get<reg::VGT_HOS_CNTL>().tess_mode; if (host_vertex_shader_type == Shader::HostVertexShaderType(-1)) {
switch (tessellation_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:
assert_unhandled_case(tessellation_mode);
return false; return false;
} }
description_out.primitive_topology_type = description_out.host_vertex_shader_type = host_vertex_shader_type;
PipelinePrimitiveTopologyType::kPatch; if (host_vertex_shader_type == Shader::HostVertexShaderType::kVertex) {
switch (primitive_type) {
case PrimitiveType::kLinePatch:
description_out.patch_type = PipelinePatchType::kLine;
break;
case PrimitiveType::kTrianglePatch:
description_out.patch_type = PipelinePatchType::kTriangle;
break;
case PrimitiveType::kQuadPatch:
description_out.patch_type = PipelinePatchType::kQuad;
break;
default:
assert_unhandled_case(primitive_type);
return false;
}
description_out.geometry_shader = PipelineGeometryShader::kNone;
} else {
description_out.tessellation_mode = PipelineTessellationMode::kNone;
switch (primitive_type) { switch (primitive_type) {
case PrimitiveType::kPointList: case PrimitiveType::kPointList:
description_out.primitive_topology_type = description_out.primitive_topology_type_or_tessellation_mode =
PipelinePrimitiveTopologyType::kPoint; uint32_t(PipelinePrimitiveTopologyType::kPoint);
break; break;
case PrimitiveType::kLineList: case PrimitiveType::kLineList:
case PrimitiveType::kLineStrip: case PrimitiveType::kLineStrip:
@ -1011,15 +1026,14 @@ bool PipelineCache::GetCurrentStateDescription(
// Quads are emulated as line lists with adjacency. // Quads are emulated as line lists with adjacency.
case PrimitiveType::kQuadList: case PrimitiveType::kQuadList:
case PrimitiveType::k2DLineStrip: case PrimitiveType::k2DLineStrip:
description_out.primitive_topology_type = description_out.primitive_topology_type_or_tessellation_mode =
PipelinePrimitiveTopologyType::kLine; uint32_t(PipelinePrimitiveTopologyType::kLine);
break; break;
default: default:
description_out.primitive_topology_type = description_out.primitive_topology_type_or_tessellation_mode =
PipelinePrimitiveTopologyType::kTriangle; uint32_t(PipelinePrimitiveTopologyType::kTriangle);
break; break;
} }
description_out.patch_type = PipelinePatchType::kNone;
switch (primitive_type) { switch (primitive_type) {
case PrimitiveType::kPointList: case PrimitiveType::kPointList:
description_out.geometry_shader = PipelineGeometryShader::kPointList; description_out.geometry_shader = PipelineGeometryShader::kPointList;
@ -1035,8 +1049,15 @@ bool PipelineCache::GetCurrentStateDescription(
description_out.geometry_shader = PipelineGeometryShader::kNone; description_out.geometry_shader = PipelineGeometryShader::kNone;
break; break;
} }
} else {
description_out.primitive_topology_type_or_tessellation_mode =
uint32_t(regs.Get<reg::VGT_HOS_CNTL>().tess_mode);
} }
bool primitive_two_faced = IsPrimitiveTwoFaced(
host_vertex_shader_type != Shader::HostVertexShaderType::kVertex,
primitive_type);
// Rasterizer state. // Rasterizer state.
// Because Direct3D 12 doesn't support per-side fill mode and depth bias, the // Because Direct3D 12 doesn't support per-side fill mode and depth bias, the
// values to use depends on the current culling state. // values to use depends on the current culling state.
@ -1139,9 +1160,8 @@ bool PipelineCache::GetCurrentStateDescription(
description_out.depth_bias_slope_scaled = description_out.depth_bias_slope_scaled =
poly_offset_scale * (1.0f / 16.0f); poly_offset_scale * (1.0f / 16.0f);
} }
if (cvars::d3d12_tessellation_wireframe && tessellated && if (cvars::d3d12_tessellation_wireframe &&
(primitive_type == PrimitiveType::kTrianglePatch || host_vertex_shader_type != Shader::HostVertexShaderType::kVertex) {
primitive_type == PrimitiveType::kQuadPatch)) {
description_out.fill_mode_wireframe = 1; description_out.fill_mode_wireframe = 1;
} }
description_out.depth_clip = !regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable; description_out.depth_clip = !regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
@ -1333,97 +1353,33 @@ ID3D12PipelineState* PipelineCache::CreateD3D12PipelineState(
break; break;
} }
// Vertex or hull/domain shaders. // Primitive topology, vertex, hull/domain and geometry shaders.
if (!runtime_description.vertex_shader->is_translated()) { if (!runtime_description.vertex_shader->is_translated()) {
XELOGE("Vertex shader %.16" PRIX64 " not translated", XELOGE("Vertex shader %.16" PRIX64 " not translated",
runtime_description.vertex_shader->ucode_data_hash()); runtime_description.vertex_shader->ucode_data_hash());
assert_always(); assert_always();
return nullptr; return nullptr;
} }
if (description.tessellation_mode != PipelineTessellationMode::kNone) { Shader::HostVertexShaderType host_vertex_shader_type =
state_desc.VS.pShaderBytecode = tessellation_vs; description.host_vertex_shader_type;
state_desc.VS.BytecodeLength = sizeof(tessellation_vs); if (runtime_description.vertex_shader->host_vertex_shader_type() !=
switch (description.patch_type) { host_vertex_shader_type) {
case PipelinePatchType::kTriangle: XELOGE("Vertex shader %.16" PRIX64
if (runtime_description.vertex_shader->patch_primitive_type() != " translated into the wrong host shader "
PrimitiveType::kTrianglePatch) { "type",
XELOGE(
"Tried to use vertex shader %.16" PRIX64
" for triangle patch tessellation, but it's not a tessellation "
"domain shader or has the wrong domain",
runtime_description.vertex_shader->ucode_data_hash());
assert_always();
return nullptr;
}
switch (description.tessellation_mode) {
case PipelineTessellationMode::kDiscrete:
state_desc.HS.pShaderBytecode = discrete_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_triangle_hs);
break;
case PipelineTessellationMode::kContinuous:
state_desc.HS.pShaderBytecode = continuous_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_triangle_hs);
break;
case PipelineTessellationMode::kAdaptive:
state_desc.HS.pShaderBytecode = adaptive_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(adaptive_triangle_hs);
break;
default:
assert_unhandled_case(description.tessellation_mode);
return nullptr;
}
break;
case PipelinePatchType::kQuad:
if (runtime_description.vertex_shader->patch_primitive_type() !=
PrimitiveType::kQuadPatch) {
XELOGE("Tried to use vertex shader %.16" PRIX64
" for quad patch tessellation, but it's not a tessellation "
"domain shader or has the wrong domain",
runtime_description.vertex_shader->ucode_data_hash());
assert_always();
return nullptr;
}
switch (description.tessellation_mode) {
case PipelineTessellationMode::kDiscrete:
state_desc.HS.pShaderBytecode = discrete_quad_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_quad_hs);
break;
case PipelineTessellationMode::kContinuous:
state_desc.HS.pShaderBytecode = continuous_quad_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_quad_hs);
break;
// TODO(Triang3l): True adaptive tessellation when properly tested.
default:
assert_unhandled_case(description.tessellation_mode);
return nullptr;
}
break;
default:
assert_unhandled_case(description.patch_type);
return nullptr;
}
// The Xenos vertex shader works like a domain shader with tessellation.
state_desc.DS.pShaderBytecode =
runtime_description.vertex_shader->translated_binary().data();
state_desc.DS.BytecodeLength =
runtime_description.vertex_shader->translated_binary().size();
} else {
if (runtime_description.vertex_shader->patch_primitive_type() !=
PrimitiveType::kNone) {
XELOGE("Tried to use vertex shader %.16" PRIX64
" without tessellation, but it's a tessellation domain shader",
runtime_description.vertex_shader->ucode_data_hash()); runtime_description.vertex_shader->ucode_data_hash());
assert_always(); assert_always();
return nullptr; return nullptr;
} }
if (host_vertex_shader_type == Shader::HostVertexShaderType::kVertex) {
state_desc.VS.pShaderBytecode = state_desc.VS.pShaderBytecode =
runtime_description.vertex_shader->translated_binary().data(); runtime_description.vertex_shader->translated_binary().data();
state_desc.VS.BytecodeLength = state_desc.VS.BytecodeLength =
runtime_description.vertex_shader->translated_binary().size(); runtime_description.vertex_shader->translated_binary().size();
} PipelinePrimitiveTopologyType primitive_topology_type =
PipelinePrimitiveTopologyType(
// Pre-GS primitive topology type. description.primitive_topology_type_or_tessellation_mode);
switch (description.primitive_topology_type) { switch (primitive_topology_type) {
case PipelinePrimitiveTopologyType::kPoint: case PipelinePrimitiveTopologyType::kPoint:
state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
break; break;
@ -1431,17 +1387,13 @@ ID3D12PipelineState* PipelineCache::CreateD3D12PipelineState(
state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
break; break;
case PipelinePrimitiveTopologyType::kTriangle: case PipelinePrimitiveTopologyType::kTriangle:
state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; state_desc.PrimitiveTopologyType =
break; D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
case PipelinePrimitiveTopologyType::kPatch:
state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
break; break;
default: default:
assert_unhandled_case(description.primitive_topology_type); assert_unhandled_case(primitive_topology_type);
return nullptr; return nullptr;
} }
// Geometry shader.
switch (description.geometry_shader) { switch (description.geometry_shader) {
case PipelineGeometryShader::kPointList: case PipelineGeometryShader::kPointList:
state_desc.GS.pShaderBytecode = primitive_point_list_gs; state_desc.GS.pShaderBytecode = primitive_point_list_gs;
@ -1458,6 +1410,63 @@ ID3D12PipelineState* PipelineCache::CreateD3D12PipelineState(
default: default:
break; break;
} }
} else {
state_desc.VS.pShaderBytecode = tessellation_vs;
state_desc.VS.BytecodeLength = sizeof(tessellation_vs);
state_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
xenos::TessellationMode tessellation_mode = xenos::TessellationMode(
description.primitive_topology_type_or_tessellation_mode);
switch (tessellation_mode) {
case xenos::TessellationMode::kDiscrete:
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainConstant:
state_desc.HS.pShaderBytecode = discrete_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_triangle_hs);
break;
case Shader::HostVertexShaderType::kQuadDomainConstant:
state_desc.HS.pShaderBytecode = discrete_quad_hs;
state_desc.HS.BytecodeLength = sizeof(discrete_quad_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
return nullptr;
}
break;
case xenos::TessellationMode::kContinuous:
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainConstant:
state_desc.HS.pShaderBytecode = continuous_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_triangle_hs);
break;
case Shader::HostVertexShaderType::kQuadDomainConstant:
state_desc.HS.pShaderBytecode = continuous_quad_hs;
state_desc.HS.BytecodeLength = sizeof(continuous_quad_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
return nullptr;
}
break;
case xenos::TessellationMode::kAdaptive:
switch (host_vertex_shader_type) {
case Shader::HostVertexShaderType::kTriangleDomainAdaptive:
state_desc.HS.pShaderBytecode = adaptive_triangle_hs;
state_desc.HS.BytecodeLength = sizeof(adaptive_triangle_hs);
break;
default:
assert_unhandled_case(host_vertex_shader_type);
return nullptr;
}
break;
default:
assert_unhandled_case(tessellation_mode);
return nullptr;
}
state_desc.DS.pShaderBytecode =
runtime_description.vertex_shader->translated_binary().data();
state_desc.DS.BytecodeLength =
runtime_description.vertex_shader->translated_binary().size();
}
// Pixel shader. // Pixel shader.
if (runtime_description.pixel_shader != nullptr) { if (runtime_description.pixel_shader != nullptr) {
@ -1715,7 +1724,7 @@ void PipelineCache::StorageWriteThread() {
shader_header.ucode_data_hash = shader->ucode_data_hash(); shader_header.ucode_data_hash = shader->ucode_data_hash();
shader_header.ucode_dword_count = shader->ucode_dword_count(); shader_header.ucode_dword_count = shader->ucode_dword_count();
shader_header.type = shader->type(); shader_header.type = shader->type();
shader_header.patch_primitive_type = shader->patch_primitive_type(); shader_header.host_vertex_shader_type = shader->host_vertex_shader_type();
shader_header.sq_program_cntl = shader_pair.second; shader_header.sq_program_cntl = shader_pair.second;
assert_not_null(shader_storage_file_); assert_not_null(shader_storage_file_);
fwrite(&shader_header, sizeof(shader_header), 1, shader_storage_file_); fwrite(&shader_header, sizeof(shader_header), 1, shader_storage_file_);

View File

@ -56,13 +56,17 @@ class PipelineCache {
D3D12Shader* LoadShader(ShaderType shader_type, uint32_t guest_address, D3D12Shader* LoadShader(ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address, uint32_t dword_count); const uint32_t* host_address, uint32_t dword_count);
// Returns the host vertex shader type for the current draw if it's valid and
// supported, or Shader::HostVertexShaderType(-1) if not.
Shader::HostVertexShaderType GetHostVertexShaderTypeIfValid() const;
// Translates shaders if needed, also making shader info up to date. // Translates shaders if needed, also making shader info up to date.
bool EnsureShadersTranslated(D3D12Shader* vertex_shader, bool EnsureShadersTranslated(
D3D12Shader* pixel_shader, bool tessellated, D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
PrimitiveType primitive_type); Shader::HostVertexShaderType host_vertex_shader_type);
bool ConfigurePipeline( bool ConfigurePipeline(
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
PrimitiveType primitive_type, IndexFormat index_format, bool early_z, PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
const RenderTargetCache::PipelineRenderTarget render_targets[5], const RenderTargetCache::PipelineRenderTarget render_targets[5],
void** pipeline_state_handle_out, void** pipeline_state_handle_out,
@ -81,11 +85,11 @@ class PipelineCache {
uint32_t ucode_dword_count : 16; uint32_t ucode_dword_count : 16;
ShaderType type : 1; ShaderType type : 1;
PrimitiveType patch_primitive_type : 6; Shader::HostVertexShaderType host_vertex_shader_type : 3;
reg::SQ_PROGRAM_CNTL sq_program_cntl; reg::SQ_PROGRAM_CNTL sq_program_cntl;
static constexpr uint32_t kVersion = 0x20200301; static constexpr uint32_t kVersion = 0x20200405;
}); });
// Update PipelineDescription::kVersion if any of the Pipeline* enums are // Update PipelineDescription::kVersion if any of the Pipeline* enums are
@ -115,7 +119,6 @@ class PipelineCache {
kPoint, kPoint,
kLine, kLine,
kTriangle, kTriangle,
kPatch,
}; };
enum class PipelineGeometryShader : uint32_t { enum class PipelineGeometryShader : uint32_t {
@ -169,21 +172,23 @@ class PipelineCache {
float depth_bias_slope_scaled; float depth_bias_slope_scaled;
PipelineStripCutIndex strip_cut_index : 2; // 2 PipelineStripCutIndex strip_cut_index : 2; // 2
PipelineTessellationMode tessellation_mode : 2; // 4 Shader::HostVertexShaderType host_vertex_shader_type : 3; // 5
PipelinePrimitiveTopologyType primitive_topology_type : 2; // 6 // PipelinePrimitiveTopologyType for a vertex shader.
PipelinePatchType patch_type : 2; // 8 // xenos::TessellationMode for a domain shader.
PipelineGeometryShader geometry_shader : 2; // 10 uint32_t primitive_topology_type_or_tessellation_mode : 2; // 7
uint32_t fill_mode_wireframe : 1; // 11 // Zero for non-kVertex host_vertex_shader_type.
PipelineCullMode cull_mode : 2; // 13 PipelineGeometryShader geometry_shader : 2; // 9
uint32_t front_counter_clockwise : 1; // 14 uint32_t fill_mode_wireframe : 1; // 10
uint32_t depth_clip : 1; // 15 PipelineCullMode cull_mode : 2; // 12
uint32_t rov_msaa : 1; // 16 uint32_t front_counter_clockwise : 1; // 13
DepthRenderTargetFormat depth_format : 1; // 17 uint32_t depth_clip : 1; // 14
CompareFunction depth_func : 3; // 20 uint32_t rov_msaa : 1; // 15
uint32_t depth_write : 1; // 21 DepthRenderTargetFormat depth_format : 1; // 16
uint32_t stencil_enable : 1; // 22 CompareFunction depth_func : 3; // 19
uint32_t stencil_read_mask : 8; // 30 uint32_t depth_write : 1; // 20
uint32_t force_early_z : 1; // 31 uint32_t stencil_enable : 1; // 21
uint32_t stencil_read_mask : 8; // 29
uint32_t force_early_z : 1; // 30
uint32_t stencil_write_mask : 8; // 8 uint32_t stencil_write_mask : 8; // 8
StencilOp stencil_front_fail_op : 3; // 11 StencilOp stencil_front_fail_op : 3; // 11
@ -197,7 +202,7 @@ class PipelineCache {
PipelineRenderTarget render_targets[4]; PipelineRenderTarget render_targets[4];
static constexpr uint32_t kVersion = 0x20200309; static constexpr uint32_t kVersion = 0x20200405;
}); });
XEPACKEDSTRUCT(PipelineStoredDescription, { XEPACKEDSTRUCT(PipelineStoredDescription, {
@ -214,10 +219,11 @@ class PipelineCache {
bool TranslateShader(DxbcShaderTranslator& translator, D3D12Shader* shader, bool TranslateShader(DxbcShaderTranslator& translator, D3D12Shader* shader,
reg::SQ_PROGRAM_CNTL cntl, reg::SQ_PROGRAM_CNTL cntl,
PrimitiveType patch_primitive_type); Shader::HostVertexShaderType host_vertex_shader_type =
Shader::HostVertexShaderType::kVertex);
bool GetCurrentStateDescription( bool GetCurrentStateDescription(
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
PrimitiveType primitive_type, IndexFormat index_format, bool early_z, PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
const RenderTargetCache::PipelineRenderTarget render_targets[5], const RenderTargetCache::PipelineRenderTarget render_targets[5],
PipelineRuntimeDescription& runtime_description_out); PipelineRuntimeDescription& runtime_description_out);

View File

@ -452,72 +452,41 @@ void DxbcShaderTranslator::StartVertexOrDomainShader() {
DxbcSrc::LF(0.0f)); DxbcSrc::LF(0.0f));
} }
if (IsDxbcVertexShader()) { // Remember that x# are only accessible via mov load or store - use a
// Write the vertex index to GPR 0. // temporary variable if need to do any computations!
switch (host_vertex_shader_type()) {
case Shader::HostVertexShaderType::kVertex:
StartVertexShader_LoadVertexIndex(); StartVertexShader_LoadVertexIndex();
} else if (IsDxbcDomainShader()) { break;
assert_true(register_count() >= 2);
if (register_count() != 0) {
uint32_t temp_register_operand_length =
uses_register_dynamic_addressing() ? 3 : 2;
// Copy the domain location to r0.yz (for quad patches) or r0.xyz (for case Shader::HostVertexShaderType::kTriangleDomainAdaptive:
// triangle patches), and also set the domain in STAT. assert_true(register_count() >= 2);
uint32_t domain_location_mask, domain_location_swizzle; if (register_count() >= 1) {
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) { // Copy the domain location to r0.xyz.
domain_location_mask = 0b0111;
// ZYX swizzle with r1.y == 0, according to the water shader in // ZYX swizzle with r1.y == 0, according to the water shader in
// Banjo-Kazooie: Nuts & Bolts. // Banjo-Kazooie: Nuts & Bolts.
domain_location_swizzle = 0b00000110; DxbcOpMov(uses_register_dynamic_addressing() ? DxbcDest::X(0, 0, 0b0111)
stat_.tessellator_domain = DxbcTessellatorDomain::kTriangle; : DxbcDest::R(0, 0b0111),
} else { DxbcSrc::VDomain(0b000110));
// TODO(Triang3l): Support line patches. if (register_count() >= 2) {
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch); // Copy the primitive index to r1.x as a float.
// According to the ground shader in Viva Pinata, though it's untested uint32_t primitive_id_temp =
// as of April 4th, 2020. uses_register_dynamic_addressing() ? PushSystemTemp() : 1;
domain_location_mask = 0b0110;
domain_location_swizzle = 0b00000100;
stat_.tessellator_domain = DxbcTessellatorDomain::kQuad;
}
DxbcOpMov(uses_register_dynamic_addressing()
? DxbcDest::X(0, 0, domain_location_mask)
: DxbcDest::R(0, domain_location_mask),
DxbcSrc::VDomain(domain_location_swizzle));
// Copy the primitive index to r0.x (for quad patches) or r1.x (for
// triangle patches) as a float.
// When using indexable temps, copy through a r# because x# are apparently
// only accessible via mov.
// TODO(Triang3l): Investigate what should be written for primitives (or
// even control points) for non-adaptive tessellation modes (they may
// possibly have an index buffer).
// TODO(Triang3l): Support line patches.
uint32_t primitive_id_gpr_index =
patch_primitive_type() == PrimitiveType::kTrianglePatch ? 1 : 0;
if (register_count() > primitive_id_gpr_index) {
uint32_t primitive_id_temp = uses_register_dynamic_addressing()
? PushSystemTemp()
: primitive_id_gpr_index;
DxbcOpUToF(DxbcDest::R(primitive_id_temp, 0b0001), DxbcSrc::VPrim()); DxbcOpUToF(DxbcDest::R(primitive_id_temp, 0b0001), DxbcSrc::VPrim());
if (uses_register_dynamic_addressing()) { if (uses_register_dynamic_addressing()) {
DxbcOpMov(DxbcDest::X(0, primitive_id_gpr_index, 0b0001), DxbcOpMov(DxbcDest::X(0, 1, 0b0001),
DxbcSrc::R(primitive_id_temp, DxbcSrc::kXXXX)); DxbcSrc::R(primitive_id_temp, DxbcSrc::kXXXX));
// Release primitive_id_temp. // Release primitive_id_temp.
PopSystemTemp(); PopSystemTemp();
} }
} // Write the swizzle of the barycentric coordinates to r1.y. It
// appears that the tessellator offloads the reordering of coordinates
if (register_count() >= 2) { // for edges to game shaders.
// Write the swizzle of the barycentric/UV coordinates to r1.x (for quad
// patches) or r1.y (for triangle patches). It appears that the
// tessellator offloads the reordering of coordinates for edges to game
// shaders.
// //
// In Banjo-Kazooie: Nuts & Bolts (triangle patches with per-edge // In Banjo-Kazooie: Nuts & Bolts, the water shader multiplies the
// factors), the shader multiplies the first control point's position by // first control point's position by r0.z, the second CP's by r0.y,
// r0.z, the second CP's by r0.y, and the third CP's by r0.x. But before // and the third CP's by r0.x. But before doing that it swizzles
// doing that it swizzles r0.xyz the following way depending on the // r0.xyz the following way depending on the value in r1.y:
// value in r1.y:
// - ZXY for 1.0. // - ZXY for 1.0.
// - YZX for 2.0. // - YZX for 2.0.
// - XZY for 4.0. // - XZY for 4.0.
@ -527,10 +496,43 @@ void DxbcShaderTranslator::StartVertexOrDomainShader() {
// rotation of the swizzle to the right, and 1 << 2 is set when the // rotation of the swizzle to the right, and 1 << 2 is set when the
// swizzle needs to be flipped before rotating. // swizzle needs to be flipped before rotating.
// //
// In Viva Pinata (quad patches with per-edge factors - not possible to // Direct3D 12 appears to be passing the coordinates in a consistent
// test however as of December 12th, 2018), if we assume that r0.y is V // order, so can just use ZYX for triangle patches.
// and r0.z is U, the factors each control point value is multiplied by DxbcOpMov(uses_register_dynamic_addressing()
// are the following: ? DxbcDest::X(0, 1, 0b0010)
: DxbcDest::R(1, 0b0010),
DxbcSrc::LF(0.0f));
}
}
break;
case Shader::HostVertexShaderType::kQuadDomainAdaptive:
assert_true(register_count() >= 2);
if (register_count() >= 1) {
// Copy the domain location to r0.yz.
// According to the ground shader in Viva Pinata, though it's untested
// as of April 4th, 2020.
DxbcOpMov(uses_register_dynamic_addressing() ? DxbcDest::X(0, 0, 0b0110)
: DxbcDest::R(0, 0b0110),
DxbcSrc::VDomain(0b000100));
// Copy the primitive index to r0.x as a float.
uint32_t primitive_id_temp =
uses_register_dynamic_addressing() ? PushSystemTemp() : 0;
DxbcOpUToF(DxbcDest::R(primitive_id_temp, 0b0001), DxbcSrc::VPrim());
if (uses_register_dynamic_addressing()) {
DxbcOpMov(DxbcDest::X(0, 0, 0b0001),
DxbcSrc::R(primitive_id_temp, DxbcSrc::kXXXX));
// Release primitive_id_temp.
PopSystemTemp();
}
if (register_count() >= 2) {
// Write the swizzle of the barycentric coordinates to r1.x. It
// appears that the tessellator offloads the reordering of coordinates
// for edges to game shaders.
//
// In Viva Pinata, if we assume that r0.y is V and r0.z is U, the
// factors each control point value is multiplied by are the
// following:
// - (1-v)*(1-u), v*(1-u), (1-v)*u, v*u for 0.0 (base swizzle). // - (1-v)*(1-u), v*(1-u), (1-v)*u, v*u for 0.0 (base swizzle).
// - v*(1-u), (1-v)*(1-u), v*u, (1-v)*u for 1.0 (YXWZ). // - v*(1-u), (1-v)*(1-u), v*u, (1-v)*u for 1.0 (YXWZ).
// - v*u, (1-v)*u, v*(1-u), (1-v)*(1-u) for 2.0 (WZYX). // - v*u, (1-v)*u, v*(1-u), (1-v)*(1-u) for 2.0 (WZYX).
@ -538,22 +540,23 @@ void DxbcShaderTranslator::StartVertexOrDomainShader() {
// According to the control point order at // According to the control point order at
// https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt // https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt
// the first is located at (0,0), the second at (0,1), the third at // the first is located at (0,0), the second at (0,1), the third at
// (1,0) and the fourth at (1,1). So, swizzle index 0 appears to be the // (1,0) and the fourth at (1,1). So, swizzle index 0 appears to be
// correct one. But, this hasn't been tested yet. // the correct one. (This, however, hasn't been tested yet as of April
// // 5th, 2020.)
// Direct3D 12 appears to be passing the coordinates in a consistent
// order, so we can just use ZYX for triangle patches.
//
// TODO(Triang3l): Support line patches.
uint32_t domain_location_swizzle_mask =
patch_primitive_type() == PrimitiveType::kTrianglePatch ? 0b0010
: 0b0001;
DxbcOpMov(uses_register_dynamic_addressing() DxbcOpMov(uses_register_dynamic_addressing()
? DxbcDest::X(0, 1, domain_location_swizzle_mask) ? DxbcDest::X(0, 1, 0b0001)
: DxbcDest::R(1, domain_location_swizzle_mask), : DxbcDest::R(1, 0b0001),
DxbcSrc::LF(0.0f)); DxbcSrc::LF(0.0f));
} }
} }
break;
default:
// TODO(Triang3l): Support line and non-adaptive patches.
assert_unhandled_case(host_vertex_shader_type());
EmitTranslationError(
"Unsupported host vertex shader type in StartVertexOrDomainShader");
break;
} }
} }
@ -3707,15 +3710,32 @@ void DxbcShaderTranslator::WritePatchConstantSignature() {
// FXC refuses to compile without SV_TessFactor and SV_InsideTessFactor input, // FXC refuses to compile without SV_TessFactor and SV_InsideTessFactor input,
// so this is required. // so this is required.
uint32_t tess_factor_count_edge, tess_factor_count_inside; uint32_t tess_factor_count_edge = 3, tess_factor_name_edge = 13;
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) { uint32_t tess_factor_count_inside = 1, tess_factor_name_inside = 14;
switch (host_vertex_shader_type()) {
case Shader::HostVertexShaderType::kTriangleDomainConstant:
case Shader::HostVertexShaderType::kTriangleDomainAdaptive:
tess_factor_count_edge = 3; tess_factor_count_edge = 3;
// D3D_NAME_FINAL_TRI_EDGE_TESSFACTOR.
tess_factor_name_edge = 13;
tess_factor_count_inside = 1; tess_factor_count_inside = 1;
} else { // D3D_NAME_FINAL_TRI_INSIDE_TESSFACTOR.
// TODO(Triang3l): Support line patches. tess_factor_name_inside = 14;
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch); break;
case Shader::HostVertexShaderType::kQuadDomainConstant:
case Shader::HostVertexShaderType::kQuadDomainAdaptive:
tess_factor_count_edge = 4; tess_factor_count_edge = 4;
// D3D_NAME_FINAL_QUAD_EDGE_TESSFACTOR.
tess_factor_name_edge = 11;
tess_factor_count_inside = 2; tess_factor_count_inside = 2;
// D3D_NAME_FINAL_QUAD_INSIDE_TESSFACTOR.
tess_factor_name_inside = 12;
break;
default:
// TODO(Triang3l): Support line patches.
assert_unhandled_case(host_vertex_shader_type());
EmitTranslationError(
"Unsupported host vertex shader type in WritePatchConstantSignature");
} }
uint32_t tess_factor_count_total = uint32_t tess_factor_count_total =
tess_factor_count_edge + tess_factor_count_inside; tess_factor_count_edge + tess_factor_count_inside;
@ -3729,24 +3749,10 @@ void DxbcShaderTranslator::WritePatchConstantSignature() {
shader_object_.push_back(0); shader_object_.push_back(0);
shader_object_.push_back( shader_object_.push_back(
i < tess_factor_count_edge ? i : (i - tess_factor_count_edge)); i < tess_factor_count_edge ? i : (i - tess_factor_count_edge));
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
if (i < tess_factor_count_edge) { if (i < tess_factor_count_edge) {
// D3D_NAME_FINAL_TRI_EDGE_TESSFACTOR. shader_object_.push_back(tess_factor_name_edge);
shader_object_.push_back(13);
} else { } else {
// D3D_NAME_FINAL_TRI_INSIDE_TESSFACTOR. shader_object_.push_back(tess_factor_name_inside);
shader_object_.push_back(14);
}
} else {
// TODO(Triang3l): Support line patches.
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
if (i < tess_factor_count_edge) {
// D3D_NAME_FINAL_QUAD_EDGE_TESSFACTOR.
shader_object_.push_back(11);
} else {
// D3D_NAME_FINAL_QUAD_INSIDE_TESSFACTOR.
shader_object_.push_back(12);
}
} }
// D3D_REGISTER_COMPONENT_FLOAT32. // D3D_REGISTER_COMPONENT_FLOAT32.
shader_object_.push_back(3); shader_object_.push_back(3);
@ -3965,29 +3971,39 @@ void DxbcShaderTranslator::WriteShaderCode() {
// Inputs/outputs have 1D-indexed operands with a component mask and a // Inputs/outputs have 1D-indexed operands with a component mask and a
// register index. // register index.
uint32_t domain_location_mask = 0b0111;
if (IsDxbcDomainShader()) { if (IsDxbcDomainShader()) {
// Not using control point data since Xenos only has a vertex shader acting // Not using control point data since Xenos only has a vertex shader acting
// as both vertex shader and domain shader. // as both vertex shader and domain shader.
uint32_t control_point_count; stat_.c_control_points = 3;
D3D11_SB_TESSELLATOR_DOMAIN domain; stat_.tessellator_domain = DxbcTessellatorDomain::kTriangle;
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) { switch (host_vertex_shader_type()) {
control_point_count = 3; case Shader::HostVertexShaderType::kTriangleDomainConstant:
domain = D3D11_SB_TESSELLATOR_DOMAIN_TRI; case Shader::HostVertexShaderType::kTriangleDomainAdaptive:
} else { stat_.c_control_points = 3;
stat_.tessellator_domain = DxbcTessellatorDomain::kTriangle;
domain_location_mask = 0b0111;
break;
case Shader::HostVertexShaderType::kQuadDomainConstant:
case Shader::HostVertexShaderType::kQuadDomainAdaptive:
stat_.c_control_points = 4;
stat_.tessellator_domain = DxbcTessellatorDomain::kQuad;
domain_location_mask = 0b0011;
break;
default:
// TODO(Triang3l): Support line patches. // TODO(Triang3l): Support line patches.
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch); assert_unhandled_case(host_vertex_shader_type());
control_point_count = 4; EmitTranslationError(
domain = D3D11_SB_TESSELLATOR_DOMAIN_QUAD; "Unsupported host vertex shader type in WriteShaderCode");
} }
shader_object_.push_back( shader_object_.push_back(
ENCODE_D3D10_SB_OPCODE_TYPE( ENCODE_D3D10_SB_OPCODE_TYPE(
D3D11_SB_OPCODE_DCL_INPUT_CONTROL_POINT_COUNT) | D3D11_SB_OPCODE_DCL_INPUT_CONTROL_POINT_COUNT) |
ENCODE_D3D11_SB_INPUT_CONTROL_POINT_COUNT(control_point_count) | ENCODE_D3D11_SB_INPUT_CONTROL_POINT_COUNT(stat_.c_control_points) |
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
stat_.c_control_points = control_point_count;
shader_object_.push_back( shader_object_.push_back(
ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DCL_TESS_DOMAIN) | ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DCL_TESS_DOMAIN) |
ENCODE_D3D11_SB_TESS_DOMAIN(domain) | ENCODE_D3D11_SB_TESS_DOMAIN(uint32_t(stat_.tessellator_domain)) |
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
} }
@ -4164,14 +4180,6 @@ void DxbcShaderTranslator::WriteShaderCode() {
if (IsDxbcVertexOrDomainShader()) { if (IsDxbcVertexOrDomainShader()) {
if (IsDxbcDomainShader()) { if (IsDxbcDomainShader()) {
// Domain location input (barycentric for triangles, UV for quads). // Domain location input (barycentric for triangles, UV for quads).
uint32_t domain_location_mask;
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
domain_location_mask = 0b0111;
} else {
// TODO(Triang3l): Support line patches.
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
domain_location_mask = 0b0011;
}
shader_object_.push_back( shader_object_.push_back(
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT) | ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT) |
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2)); ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2));

View File

@ -1657,11 +1657,11 @@ class DxbcShaderTranslator : public ShaderTranslator {
} }
inline bool IsDxbcVertexShader() const { inline bool IsDxbcVertexShader() const {
return IsDxbcVertexOrDomainShader() && return IsDxbcVertexOrDomainShader() &&
patch_primitive_type() == PrimitiveType::kNone; host_vertex_shader_type() == Shader::HostVertexShaderType::kVertex;
} }
inline bool IsDxbcDomainShader() const { inline bool IsDxbcDomainShader() const {
return IsDxbcVertexOrDomainShader() && return IsDxbcVertexOrDomainShader() &&
patch_primitive_type() != PrimitiveType::kNone; host_vertex_shader_type() != Shader::HostVertexShaderType::kVertex;
} }
inline bool IsDxbcPixelShader() const { inline bool IsDxbcPixelShader() const {
return is_depth_only_pixel_shader_ || is_pixel_shader(); return is_depth_only_pixel_shader_ || is_pixel_shader();

View File

@ -530,6 +530,19 @@ struct ParsedAluInstruction {
class Shader { class Shader {
public: public:
// If values are changed, invalidate shader storages where this is stored! And
// check bit count where this is packed. This is : uint32_t for simplicity of
// packing in bit fields.
enum class HostVertexShaderType : uint32_t {
kVertex,
kLineDomainConstant,
kLineDomainAdaptive,
kTriangleDomainConstant,
kTriangleDomainAdaptive,
kQuadDomainConstant,
kQuadDomainAdaptive,
};
struct Error { struct Error {
bool is_fatal = false; bool is_fatal = false;
std::string message; std::string message;
@ -592,9 +605,12 @@ class Shader {
// Whether the shader is identified as a vertex or pixel shader. // Whether the shader is identified as a vertex or pixel shader.
ShaderType type() const { return shader_type_; } ShaderType type() const { return shader_type_; }
// Tessellation patch primitive type for a vertex shader translated into a // If this is a vertex shader, and it has been translated, type of the shader
// domain shader, or PrimitiveType::kNone for a normal vertex shader. // in a D3D11-like rendering pipeline - shader interface depends on in, so it
PrimitiveType patch_primitive_type() const { return patch_primitive_type_; } // must be known at translation time.
HostVertexShaderType host_vertex_shader_type() const {
return host_vertex_shader_type_;
}
// Microcode dwords in host endianness. // Microcode dwords in host endianness.
const std::vector<uint32_t>& ucode_data() const { return ucode_data_; } const std::vector<uint32_t>& ucode_data() const { return ucode_data_; }
@ -676,7 +692,7 @@ class Shader {
friend class ShaderTranslator; friend class ShaderTranslator;
ShaderType shader_type_; ShaderType shader_type_;
PrimitiveType patch_primitive_type_ = PrimitiveType::kNone; HostVertexShaderType host_vertex_shader_type_ = HostVertexShaderType::kVertex;
std::vector<uint32_t> ucode_data_; std::vector<uint32_t> ucode_data_;
uint64_t ucode_data_hash_; uint64_t ucode_data_hash_;

View File

@ -35,10 +35,11 @@ DEFINE_string(shader_output, "", "Output shader file path.", "GPU");
DEFINE_string(shader_output_type, "ucode", DEFINE_string(shader_output_type, "ucode",
"Translator to use: [ucode, spirv, spirvtext, dxbc, dxbctext].", "Translator to use: [ucode, spirv, spirvtext, dxbc, dxbctext].",
"GPU"); "GPU");
DEFINE_string(shader_output_patch, "", DEFINE_string(
"Tessellation patch type in the generated tessellation " vertex_shader_output_type, "",
"evaluation (domain) shader, or unspecified to produce a vertex " "Type of the host interface to produce the vertex or domain shader for: "
"shader: [line, triangle, quad].", "[vertex or unspecified, linedomain, linedomainadaptive, triangledomain, "
"triangledomainadaptive, quaddomain, quaddomainadaptive].",
"GPU"); "GPU");
DEFINE_bool(shader_output_dxbc_rov, false, DEFINE_bool(shader_output_dxbc_rov, false,
"Output ROV-based output-merger code in DXBC pixel shaders.", "Output ROV-based output-merger code in DXBC pixel shaders.",
@ -112,18 +113,31 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
translator = std::make_unique<UcodeShaderTranslator>(); translator = std::make_unique<UcodeShaderTranslator>();
} }
PrimitiveType patch_primitive_type = PrimitiveType::kNone; Shader::HostVertexShaderType host_vertex_shader_type =
Shader::HostVertexShaderType::kVertex;
if (shader_type == ShaderType::kVertex) { if (shader_type == ShaderType::kVertex) {
if (cvars::shader_output_patch == "line") { if (cvars::vertex_shader_output_type == "linedomain") {
patch_primitive_type = PrimitiveType::kLinePatch; host_vertex_shader_type =
} else if (cvars::shader_output_patch == "triangle") { Shader::HostVertexShaderType::kLineDomainConstant;
patch_primitive_type = PrimitiveType::kTrianglePatch; } else if (cvars::vertex_shader_output_type == "linedomainadaptive") {
} else if (cvars::shader_output_patch == "quad") { host_vertex_shader_type =
patch_primitive_type = PrimitiveType::kQuadPatch; Shader::HostVertexShaderType::kLineDomainAdaptive;
} else if (cvars::vertex_shader_output_type == "triangledomain") {
host_vertex_shader_type =
Shader::HostVertexShaderType::kTriangleDomainConstant;
} else if (cvars::vertex_shader_output_type == "triangledomainadaptive") {
host_vertex_shader_type =
Shader::HostVertexShaderType::kTriangleDomainAdaptive;
} else if (cvars::vertex_shader_output_type == "quaddomain") {
host_vertex_shader_type =
Shader::HostVertexShaderType::kQuadDomainConstant;
} else if (cvars::vertex_shader_output_type == "quaddomainadaptive") {
host_vertex_shader_type =
Shader::HostVertexShaderType::kQuadDomainAdaptive;
} }
} }
translator->Translate(shader.get(), patch_primitive_type); translator->Translate(shader.get(), host_vertex_shader_type);
const void* source_data = shader->translated_binary().data(); const void* source_data = shader->translated_binary().data();
size_t source_data_size = shader->translated_binary().size(); size_t source_data_size = shader->translated_binary().size();

View File

@ -107,26 +107,27 @@ bool ShaderTranslator::GatherAllBindingInformation(Shader* shader) {
return true; return true;
} }
bool ShaderTranslator::Translate(Shader* shader, PrimitiveType patch_type, bool ShaderTranslator::Translate(
reg::SQ_PROGRAM_CNTL cntl) { Shader* shader, reg::SQ_PROGRAM_CNTL cntl,
Shader::HostVertexShaderType host_vertex_shader_type) {
Reset(); Reset();
uint32_t cntl_num_reg = uint32_t cntl_num_reg =
shader->type() == ShaderType::kVertex ? cntl.vs_num_reg : cntl.ps_num_reg; shader->type() == ShaderType::kVertex ? cntl.vs_num_reg : cntl.ps_num_reg;
register_count_ = (cntl_num_reg & 0x80) ? 0 : (cntl_num_reg + 1); register_count_ = (cntl_num_reg & 0x80) ? 0 : (cntl_num_reg + 1);
return TranslateInternal(shader, patch_type); return TranslateInternal(shader, host_vertex_shader_type);
} }
bool ShaderTranslator::Translate(Shader* shader, PrimitiveType patch_type) { bool ShaderTranslator::Translate(
Shader* shader, Shader::HostVertexShaderType host_vertex_shader_type) {
Reset(); Reset();
return TranslateInternal(shader, patch_type); return TranslateInternal(shader, host_vertex_shader_type);
} }
bool ShaderTranslator::TranslateInternal(Shader* shader, bool ShaderTranslator::TranslateInternal(
PrimitiveType patch_type) { Shader* shader, Shader::HostVertexShaderType host_vertex_shader_type) {
shader_type_ = shader->type(); shader_type_ = shader->type();
patch_primitive_type_ = host_vertex_shader_type_ = host_vertex_shader_type;
shader_type_ == ShaderType::kVertex ? patch_type : PrimitiveType::kNone;
ucode_dwords_ = shader->ucode_dwords(); ucode_dwords_ = shader->ucode_dwords();
ucode_dword_count_ = shader->ucode_dword_count(); ucode_dword_count_ = shader->ucode_dword_count();
@ -194,7 +195,7 @@ bool ShaderTranslator::TranslateInternal(Shader* shader,
shader->errors_ = std::move(errors_); shader->errors_ = std::move(errors_);
shader->translated_binary_ = CompleteTranslation(); shader->translated_binary_ = CompleteTranslation();
shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string(); shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
shader->patch_primitive_type_ = patch_primitive_type_; shader->host_vertex_shader_type_ = host_vertex_shader_type_;
shader->vertex_bindings_ = std::move(vertex_bindings_); shader->vertex_bindings_ = std::move(vertex_bindings_);
shader->texture_bindings_ = std::move(texture_bindings_); shader->texture_bindings_ = std::move(texture_bindings_);
shader->constant_register_map_ = std::move(constant_register_map_); shader->constant_register_map_ = std::move(constant_register_map_);

View File

@ -33,9 +33,12 @@ class ShaderTranslator {
// DEPRECATED(benvanik): remove this when shader cache is removed. // DEPRECATED(benvanik): remove this when shader cache is removed.
bool GatherAllBindingInformation(Shader* shader); bool GatherAllBindingInformation(Shader* shader);
bool Translate(Shader* shader, PrimitiveType patch_type, bool Translate(Shader* shader, reg::SQ_PROGRAM_CNTL cntl,
reg::SQ_PROGRAM_CNTL cntl); Shader::HostVertexShaderType host_vertex_shader_type =
bool Translate(Shader* shader, PrimitiveType patch_type); Shader::HostVertexShaderType::kVertex);
bool Translate(Shader* shader,
Shader::HostVertexShaderType host_vertex_shader_type =
Shader::HostVertexShaderType::kVertex);
protected: protected:
ShaderTranslator(); ShaderTranslator();
@ -47,9 +50,11 @@ class ShaderTranslator {
uint32_t register_count() const { return register_count_; } uint32_t register_count() const { return register_count_; }
// True if the current shader is a vertex shader. // True if the current shader is a vertex shader.
bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; } bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; }
// Tessellation patch primitive type for a vertex shader translated into a // If translating a vertex shader, type of the shader in a D3D11-like
// domain shader, or PrimitiveType::kNone for a normal vertex shader. // rendering pipeline.
PrimitiveType patch_primitive_type() const { return patch_primitive_type_; } Shader::HostVertexShaderType host_vertex_shader_type() const {
return host_vertex_shader_type_;
}
// True if the current shader is a pixel shader. // True if the current shader is a pixel shader.
bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; } bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; }
const Shader::ConstantRegisterMap& constant_register_map() const { const Shader::ConstantRegisterMap& constant_register_map() const {
@ -181,7 +186,8 @@ class ShaderTranslator {
bool disable_implicit_early_z; bool disable_implicit_early_z;
}; };
bool TranslateInternal(Shader* shader, PrimitiveType patch_type); bool TranslateInternal(Shader* shader,
Shader::HostVertexShaderType host_vertex_shader_type);
void MarkUcodeInstruction(uint32_t dword_offset); void MarkUcodeInstruction(uint32_t dword_offset);
void AppendUcodeDisasm(char c); void AppendUcodeDisasm(char c);
@ -230,7 +236,7 @@ class ShaderTranslator {
// Input shader metadata and microcode. // Input shader metadata and microcode.
ShaderType shader_type_; ShaderType shader_type_;
PrimitiveType patch_primitive_type_; Shader::HostVertexShaderType host_vertex_shader_type_;
const uint32_t* ucode_dwords_; const uint32_t* ucode_dwords_;
size_t ucode_dword_count_; size_t ucode_dword_count_;
reg::SQ_PROGRAM_CNTL program_cntl_; reg::SQ_PROGRAM_CNTL program_cntl_;

View File

@ -366,7 +366,7 @@ bool PipelineCache::TranslateShader(VulkanShader* shader,
reg::SQ_PROGRAM_CNTL cntl) { reg::SQ_PROGRAM_CNTL cntl) {
// Perform translation. // Perform translation.
// If this fails the shader will be marked as invalid and ignored later. // If this fails the shader will be marked as invalid and ignored later.
if (!shader_translator_->Translate(shader, PrimitiveType::kNone, cntl)) { if (!shader_translator_->Translate(shader, cntl)) {
XELOGE("Shader translation failed; marking shader as ignored"); XELOGE("Shader translation failed; marking shader as ignored");
return false; return false;
} }

View File

@ -52,17 +52,19 @@ enum class PrimitiveType : uint32_t {
k2DLineStrip = 0x15, k2DLineStrip = 0x15,
k2DTriStrip = 0x16, k2DTriStrip = 0x16,
// Tessellation patches (D3DTPT) when VGT_OUTPUT_PATH_CNTL & 3 is // Tessellation patches (D3DTPT) when VGT_OUTPUT_PATH_CNTL::path_select is
// VGT_OUTPATH_TESS_EN (1). // xenos::VGTOutputPath::tess_mode. Seen being used with adaptive tessellation
// in Banjo-Kazooie: Nuts & Bolts, Halo 3 and Viva Pinata; discrete/continuous
// uses kTriangleList in Call of Duty 3 and Viva Pinata.
kLinePatch = 0x10, kLinePatch = 0x10,
kTrianglePatch = 0x11, kTrianglePatch = 0x11,
kQuadPatch = 0x12, kQuadPatch = 0x12,
}; };
inline bool IsPrimitiveTwoFaced(bool tessellated, PrimitiveType type) { inline bool IsPrimitiveTwoFaced(bool tessellated, PrimitiveType type) {
if (tessellated) { if (tessellated && (type == PrimitiveType::kTrianglePatch ||
return type == PrimitiveType::kTrianglePatch || type == PrimitiveType::kQuadPatch)) {
type == PrimitiveType::kQuadPatch; return true;
} }
switch (type) { switch (type) {
case PrimitiveType::kTriangleList: case PrimitiveType::kTriangleList: