shader: Implement tessellation shaders, polygon mode and invocation id

This commit is contained in:
ReinUsesLisp 2021-04-15 22:46:11 -03:00 committed by ameerj
parent 34519d3fc6
commit 183855e396
28 changed files with 605 additions and 91 deletions

View file

@ -41,6 +41,8 @@ add_library(shader_recompiler STATIC
frontend/ir/opcodes.cpp frontend/ir/opcodes.cpp
frontend/ir/opcodes.h frontend/ir/opcodes.h
frontend/ir/opcodes.inc frontend/ir/opcodes.inc
frontend/ir/patch.cpp
frontend/ir/patch.h
frontend/ir/post_order.cpp frontend/ir/post_order.cpp
frontend/ir/post_order.h frontend/ir/post_order.h
frontend/ir/pred.h frontend/ir/pred.h

View file

@ -125,19 +125,36 @@ u32 NumVertices(InputTopology input_topology) {
throw InvalidArgument("Invalid input topology {}", input_topology); throw InvalidArgument("Invalid input topology {}", input_topology);
} }
Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
if (ctx.stage == Stage::Geometry) { std::optional<spv::BuiltIn> builtin = std::nullopt) {
const u32 num_vertices{NumVertices(ctx.profile.input_topology)}; switch (ctx.stage) {
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices)); case Stage::TessellationControl:
case Stage::TessellationEval:
if (per_invocation) {
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], 32u));
}
break;
case Stage::Geometry:
if (per_invocation) {
const u32 num_vertices{NumVertices(ctx.profile.input_topology)};
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices));
}
break;
default:
break;
} }
return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); return DefineVariable(ctx, type, builtin, spv::StorageClass::Input);
} }
Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
std::optional<spv::BuiltIn> builtin = std::nullopt) {
if (invocations && ctx.stage == Stage::TessellationControl) {
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], *invocations));
}
return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
} }
void DefineGenericOutput(EmitContext& ctx, size_t index) { void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
static constexpr std::string_view swizzle{"xyzw"}; static constexpr std::string_view swizzle{"xyzw"};
const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4}; const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
u32 element{0}; u32 element{0};
@ -150,7 +167,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index) {
} }
const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
const Id id{DefineOutput(ctx, ctx.F32[num_components])}; const Id id{DefineOutput(ctx, ctx.F32[num_components], invocations)};
ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
if (element > 0) { if (element > 0) {
ctx.Decorate(id, spv::Decoration::Component, element); ctx.Decorate(id, spv::Decoration::Component, element);
@ -161,10 +178,10 @@ void DefineGenericOutput(EmitContext& ctx, size_t index) {
ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset);
} }
if (num_components < 4 || element > 0) { if (num_components < 4 || element > 0) {
ctx.Name(id, fmt::format("out_attr{}", index));
} else {
const std::string_view subswizzle{swizzle.substr(element, num_components)}; const std::string_view subswizzle{swizzle.substr(element, num_components)};
ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle)); ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle));
} else {
ctx.Name(id, fmt::format("out_attr{}", index));
} }
const GenericElementInfo info{ const GenericElementInfo info{
.id = id, .id = id,
@ -383,7 +400,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin
AddCapability(spv::Capability::Shader); AddCapability(spv::Capability::Shader);
DefineCommonTypes(program.info); DefineCommonTypes(program.info);
DefineCommonConstants(); DefineCommonConstants();
DefineInterfaces(program.info); DefineInterfaces(program);
DefineLocalMemory(program); DefineLocalMemory(program);
DefineSharedMemory(program); DefineSharedMemory(program);
DefineSharedMemoryFunctions(program); DefineSharedMemoryFunctions(program);
@ -472,9 +489,9 @@ void EmitContext::DefineCommonConstants() {
f32_zero_value = Constant(F32[1], 0.0f); f32_zero_value = Constant(F32[1], 0.0f);
} }
void EmitContext::DefineInterfaces(const Info& info) { void EmitContext::DefineInterfaces(const IR::Program& program) {
DefineInputs(info); DefineInputs(program.info);
DefineOutputs(info); DefineOutputs(program);
} }
void EmitContext::DefineLocalMemory(const IR::Program& program) { void EmitContext::DefineLocalMemory(const IR::Program& program) {
@ -972,26 +989,29 @@ void EmitContext::DefineLabels(IR::Program& program) {
void EmitContext::DefineInputs(const Info& info) { void EmitContext::DefineInputs(const Info& info) {
if (info.uses_workgroup_id) { if (info.uses_workgroup_id) {
workgroup_id = DefineInput(*this, U32[3], spv::BuiltIn::WorkgroupId); workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
} }
if (info.uses_local_invocation_id) { if (info.uses_local_invocation_id) {
local_invocation_id = DefineInput(*this, U32[3], spv::BuiltIn::LocalInvocationId); local_invocation_id = DefineInput(*this, U32[3], false, spv::BuiltIn::LocalInvocationId);
}
if (info.uses_invocation_id) {
invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
} }
if (info.uses_is_helper_invocation) { if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, spv::BuiltIn::HelperInvocation); is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
} }
if (info.uses_subgroup_mask) { if (info.uses_subgroup_mask) {
subgroup_mask_eq = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
subgroup_mask_lt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
subgroup_mask_le = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
subgroup_mask_gt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGtMaskKHR); subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
subgroup_mask_ge = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGeMaskKHR); subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
} }
if (info.uses_subgroup_invocation_id || if (info.uses_subgroup_invocation_id ||
(profile.warp_size_potentially_larger_than_guest && (profile.warp_size_potentially_larger_than_guest &&
(info.uses_subgroup_vote || info.uses_subgroup_mask))) { (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
subgroup_local_invocation_id = subgroup_local_invocation_id =
DefineInput(*this, U32[1], spv::BuiltIn::SubgroupLocalInvocationId); DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
} }
if (info.uses_fswzadd) { if (info.uses_fswzadd) {
const Id f32_one{Constant(F32[1], 1.0f)}; const Id f32_one{Constant(F32[1], 1.0f)};
@ -1004,29 +1024,32 @@ void EmitContext::DefineInputs(const Info& info) {
if (info.loads_position) { if (info.loads_position) {
const bool is_fragment{stage != Stage::Fragment}; const bool is_fragment{stage != Stage::Fragment};
const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
input_position = DefineInput(*this, F32[4], built_in); input_position = DefineInput(*this, F32[4], true, built_in);
} }
if (info.loads_instance_id) { if (info.loads_instance_id) {
if (profile.support_vertex_instance_id) { if (profile.support_vertex_instance_id) {
instance_id = DefineInput(*this, U32[1], spv::BuiltIn::InstanceId); instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
} else { } else {
instance_index = DefineInput(*this, U32[1], spv::BuiltIn::InstanceIndex); instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
base_instance = DefineInput(*this, U32[1], spv::BuiltIn::BaseInstance); base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
} }
} }
if (info.loads_vertex_id) { if (info.loads_vertex_id) {
if (profile.support_vertex_instance_id) { if (profile.support_vertex_instance_id) {
vertex_id = DefineInput(*this, U32[1], spv::BuiltIn::VertexId); vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
} else { } else {
vertex_index = DefineInput(*this, U32[1], spv::BuiltIn::VertexIndex); vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
base_vertex = DefineInput(*this, U32[1], spv::BuiltIn::BaseVertex); base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
} }
} }
if (info.loads_front_face) { if (info.loads_front_face) {
front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing); front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
} }
if (info.loads_point_coord) { if (info.loads_point_coord) {
point_coord = DefineInput(*this, F32[2], spv::BuiltIn::PointCoord); point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord);
}
if (info.loads_tess_coord) {
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
} }
for (size_t index = 0; index < info.input_generics.size(); ++index) { for (size_t index = 0; index < info.input_generics.size(); ++index) {
const InputVarying generic{info.input_generics[index]}; const InputVarying generic{info.input_generics[index]};
@ -1038,7 +1061,7 @@ void EmitContext::DefineInputs(const Info& info) {
continue; continue;
} }
const Id type{GetAttributeType(*this, input_type)}; const Id type{GetAttributeType(*this, input_type)};
const Id id{DefineInput(*this, type)}; const Id id{DefineInput(*this, type, true)};
Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
Name(id, fmt::format("in_attr{}", index)); Name(id, fmt::format("in_attr{}", index));
input_generics[index] = id; input_generics[index] = id;
@ -1059,58 +1082,98 @@ void EmitContext::DefineInputs(const Info& info) {
break; break;
} }
} }
if (stage == Stage::TessellationEval) {
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
if (!info.uses_patches[index]) {
continue;
}
const Id id{DefineInput(*this, F32[4], false)};
Decorate(id, spv::Decoration::Patch);
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
patches[index] = id;
}
}
} }
void EmitContext::DefineOutputs(const Info& info) { void EmitContext::DefineOutputs(const IR::Program& program) {
const Info& info{program.info};
const std::optional<u32> invocations{program.invocations};
if (info.stores_position || stage == Stage::VertexB) { if (info.stores_position || stage == Stage::VertexB) {
output_position = DefineOutput(*this, F32[4], spv::BuiltIn::Position); output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
} }
if (info.stores_point_size || profile.fixed_state_point_size) { if (info.stores_point_size || profile.fixed_state_point_size) {
if (stage == Stage::Fragment) { if (stage == Stage::Fragment) {
throw NotImplementedException("Storing PointSize in fragment stage"); throw NotImplementedException("Storing PointSize in fragment stage");
} }
output_point_size = DefineOutput(*this, F32[1], spv::BuiltIn::PointSize); output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize);
} }
if (info.stores_clip_distance) { if (info.stores_clip_distance) {
if (stage == Stage::Fragment) { if (stage == Stage::Fragment) {
throw NotImplementedException("Storing ClipDistance in fragment stage"); throw NotImplementedException("Storing ClipDistance in fragment stage");
} }
const Id type{TypeArray(F32[1], Constant(U32[1], 8U))}; const Id type{TypeArray(F32[1], Constant(U32[1], 8U))};
clip_distances = DefineOutput(*this, type, spv::BuiltIn::ClipDistance); clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
} }
if (info.stores_layer && if (info.stores_layer &&
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
if (stage == Stage::Fragment) { if (stage == Stage::Fragment) {
throw NotImplementedException("Storing Layer in fragment stage"); throw NotImplementedException("Storing Layer in fragment stage");
} }
layer = DefineOutput(*this, U32[1], spv::BuiltIn::Layer); layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer);
} }
if (info.stores_viewport_index && if (info.stores_viewport_index &&
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
if (stage == Stage::Fragment) { if (stage == Stage::Fragment) {
throw NotImplementedException("Storing ViewportIndex in fragment stage"); throw NotImplementedException("Storing ViewportIndex in fragment stage");
} }
viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex);
} }
for (size_t index = 0; index < info.stores_generics.size(); ++index) { for (size_t index = 0; index < info.stores_generics.size(); ++index) {
if (info.stores_generics[index]) { if (info.stores_generics[index]) {
DefineGenericOutput(*this, index); DefineGenericOutput(*this, index, invocations);
} }
} }
if (stage == Stage::Fragment) { switch (stage) {
case Stage::TessellationControl:
if (info.stores_tess_level_outer) {
const Id type{TypeArray(F32[1], Constant(U32[1], 4))};
output_tess_level_outer =
DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelOuter);
Decorate(output_tess_level_outer, spv::Decoration::Patch);
}
if (info.stores_tess_level_inner) {
const Id type{TypeArray(F32[1], Constant(U32[1], 2))};
output_tess_level_inner =
DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelInner);
Decorate(output_tess_level_inner, spv::Decoration::Patch);
}
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
if (!info.uses_patches[index]) {
continue;
}
const Id id{DefineOutput(*this, F32[4], std::nullopt)};
Decorate(id, spv::Decoration::Patch);
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
patches[index] = id;
}
break;
case Stage::Fragment:
for (u32 index = 0; index < 8; ++index) { for (u32 index = 0; index < 8; ++index) {
if (!info.stores_frag_color[index]) { if (!info.stores_frag_color[index]) {
continue; continue;
} }
frag_color[index] = DefineOutput(*this, F32[4]); frag_color[index] = DefineOutput(*this, F32[4], std::nullopt);
Decorate(frag_color[index], spv::Decoration::Location, index); Decorate(frag_color[index], spv::Decoration::Location, index);
Name(frag_color[index], fmt::format("frag_color{}", index)); Name(frag_color[index], fmt::format("frag_color{}", index));
} }
if (info.stores_frag_depth) { if (info.stores_frag_depth) {
frag_depth = DefineOutput(*this, F32[1]); frag_depth = DefineOutput(*this, F32[1], std::nullopt);
Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
Name(frag_depth, "frag_depth"); Name(frag_depth, "frag_depth");
} }
break;
default:
break;
} }
} }

View file

@ -147,6 +147,7 @@ public:
Id workgroup_id{}; Id workgroup_id{};
Id local_invocation_id{}; Id local_invocation_id{};
Id invocation_id{};
Id is_helper_invocation{}; Id is_helper_invocation{};
Id subgroup_local_invocation_id{}; Id subgroup_local_invocation_id{};
Id subgroup_mask_eq{}; Id subgroup_mask_eq{};
@ -162,6 +163,7 @@ public:
Id base_vertex{}; Id base_vertex{};
Id front_face{}; Id front_face{};
Id point_coord{}; Id point_coord{};
Id tess_coord{};
Id clip_distances{}; Id clip_distances{};
Id layer{}; Id layer{};
Id viewport_index{}; Id viewport_index{};
@ -204,6 +206,10 @@ public:
Id output_position{}; Id output_position{};
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
Id output_tess_level_outer{};
Id output_tess_level_inner{};
std::array<Id, 30> patches{};
std::array<Id, 8> frag_color{}; std::array<Id, 8> frag_color{};
Id frag_depth{}; Id frag_depth{};
@ -212,7 +218,7 @@ public:
private: private:
void DefineCommonTypes(const Info& info); void DefineCommonTypes(const Info& info);
void DefineCommonConstants(); void DefineCommonConstants();
void DefineInterfaces(const Info& info); void DefineInterfaces(const IR::Program& program);
void DefineLocalMemory(const IR::Program& program); void DefineLocalMemory(const IR::Program& program);
void DefineSharedMemory(const IR::Program& program); void DefineSharedMemory(const IR::Program& program);
void DefineSharedMemoryFunctions(const IR::Program& program); void DefineSharedMemoryFunctions(const IR::Program& program);
@ -226,7 +232,7 @@ private:
void DefineLabels(IR::Program& program); void DefineLabels(IR::Program& program);
void DefineInputs(const Info& info); void DefineInputs(const Info& info);
void DefineOutputs(const Info& info); void DefineOutputs(const IR::Program& program);
}; };
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View file

@ -45,6 +45,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
return arg.Label(); return arg.Label();
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
return arg.Attribute(); return arg.Attribute();
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
return arg.Patch();
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) { } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
return arg.Reg(); return arg.Reg();
} }
@ -120,6 +122,30 @@ Id DefineMain(EmitContext& ctx, IR::Program& program) {
return main; return main;
} }
spv::ExecutionMode ExecutionMode(TessPrimitive primitive) {
switch (primitive) {
case TessPrimitive::Isolines:
return spv::ExecutionMode::Isolines;
case TessPrimitive::Triangles:
return spv::ExecutionMode::Triangles;
case TessPrimitive::Quads:
return spv::ExecutionMode::Quads;
}
throw InvalidArgument("Tessellation primitive {}", primitive);
}
spv::ExecutionMode ExecutionMode(TessSpacing spacing) {
switch (spacing) {
case TessSpacing::Equal:
return spv::ExecutionMode::SpacingEqual;
case TessSpacing::FractionalOdd:
return spv::ExecutionMode::SpacingFractionalOdd;
case TessSpacing::FractionalEven:
return spv::ExecutionMode::SpacingFractionalEven;
}
throw InvalidArgument("Tessellation spacing {}", spacing);
}
void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
spv::ExecutionModel execution_model{}; spv::ExecutionModel execution_model{};
@ -134,6 +160,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
case Stage::VertexB: case Stage::VertexB:
execution_model = spv::ExecutionModel::Vertex; execution_model = spv::ExecutionModel::Vertex;
break; break;
case Stage::TessellationControl:
execution_model = spv::ExecutionModel::TessellationControl;
ctx.AddCapability(spv::Capability::Tessellation);
ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations);
break;
case Stage::TessellationEval:
execution_model = spv::ExecutionModel::TessellationEvaluation;
ctx.AddCapability(spv::Capability::Tessellation);
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive));
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing));
ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw
: spv::ExecutionMode::VertexOrderCcw);
break;
case Stage::Geometry: case Stage::Geometry:
execution_model = spv::ExecutionModel::Geometry; execution_model = spv::ExecutionModel::Geometry;
ctx.AddCapability(spv::Capability::Geometry); ctx.AddCapability(spv::Capability::Geometry);

View file

@ -55,6 +55,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex); Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex); void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch);
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value);
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value);
void EmitSetFragDepth(EmitContext& ctx, Id value); void EmitSetFragDepth(EmitContext& ctx, Id value);
void EmitGetZFlag(EmitContext& ctx); void EmitGetZFlag(EmitContext& ctx);
@ -67,6 +69,7 @@ void EmitSetCFlag(EmitContext& ctx);
void EmitSetOFlag(EmitContext& ctx); void EmitSetOFlag(EmitContext& ctx);
Id EmitWorkgroupId(EmitContext& ctx); Id EmitWorkgroupId(EmitContext& ctx);
Id EmitLocalInvocationId(EmitContext& ctx); Id EmitLocalInvocationId(EmitContext& ctx);
Id EmitInvocationId(EmitContext& ctx);
Id EmitIsHelperInvocation(EmitContext& ctx); Id EmitIsHelperInvocation(EmitContext& ctx);
Id EmitLoadLocal(EmitContext& ctx, Id word_offset); Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);

View file

@ -32,13 +32,26 @@ std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
template <typename... Args> template <typename... Args>
Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
if (ctx.stage == Stage::Geometry) { switch (ctx.stage) {
case Stage::TessellationControl:
case Stage::TessellationEval:
case Stage::Geometry:
return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...); return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...);
} else { default:
return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...); return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...);
} }
} }
template <typename... Args>
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
if (ctx.stage == Stage::TessellationControl) {
const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)};
return ctx.OpAccessChain(result_type, base, invocation_id, std::forward<Args>(args)...);
} else {
return ctx.OpAccessChain(result_type, base, std::forward<Args>(args)...);
}
}
std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
if (IR::IsGeneric(attr)) { if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
@ -49,7 +62,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
} else { } else {
const u32 index_element{element - info.first_element}; const u32 index_element{element - info.first_element};
const Id index_id{ctx.Constant(ctx.U32[1], index_element)}; const Id index_id{ctx.Constant(ctx.U32[1], index_element)};
return ctx.OpAccessChain(ctx.output_f32, info.id, index_id); return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
} }
} }
switch (attr) { switch (attr) {
@ -61,7 +74,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
case IR::Attribute::PositionW: { case IR::Attribute::PositionW: {
const u32 element{static_cast<u32>(attr) % 4}; const u32 element{static_cast<u32>(attr) % 4};
const Id element_id{ctx.Constant(ctx.U32[1], element)}; const Id element_id{ctx.Constant(ctx.U32[1], element)};
return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id); return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
} }
case IR::Attribute::ClipDistance0: case IR::Attribute::ClipDistance0:
case IR::Attribute::ClipDistance1: case IR::Attribute::ClipDistance1:
@ -74,7 +87,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)}; const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
const u32 index{static_cast<u32>(attr) - base}; const u32 index{static_cast<u32>(attr) - base};
const Id clip_num{ctx.Constant(ctx.U32[1], index)}; const Id clip_num{ctx.Constant(ctx.U32[1], index)};
return ctx.OpAccessChain(ctx.output_f32, ctx.clip_distances, clip_num); return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
} }
case IR::Attribute::Layer: case IR::Attribute::Layer:
return ctx.profile.support_viewport_index_layer_non_geometry || return ctx.profile.support_viewport_index_layer_non_geometry ||
@ -222,11 +235,18 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()),
ctx.u32_zero_value); ctx.u32_zero_value);
case IR::Attribute::PointSpriteS: case IR::Attribute::PointSpriteS:
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, return ctx.OpLoad(ctx.F32[1],
ctx.u32_zero_value)); ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
case IR::Attribute::PointSpriteT: case IR::Attribute::PointSpriteT:
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord,
ctx.Constant(ctx.U32[1], 1U))); ctx.Constant(ctx.U32[1], 1U)));
case IR::Attribute::TessellationEvaluationPointU:
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value));
case IR::Attribute::TessellationEvaluationPointV:
return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord,
ctx.Constant(ctx.U32[1], 1U)));
default: default:
throw NotImplementedException("Read attribute {}", attr); throw NotImplementedException("Read attribute {}", attr);
} }
@ -240,9 +260,12 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_un
} }
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) { Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) {
if (ctx.stage == Stage::Geometry) { switch (ctx.stage) {
case Stage::TessellationControl:
case Stage::TessellationEval:
case Stage::Geometry:
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex); return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex);
} else { default:
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset);
} }
} }
@ -251,6 +274,45 @@ void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unus
ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value);
} }
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
if (!IR::IsGeneric(patch)) {
throw NotImplementedException("Non-generic patch load");
}
const u32 index{IR::GenericPatchIndex(patch)};
const Id element{ctx.Constant(ctx.U32[1], IR::GenericPatchElement(patch))};
const Id pointer{ctx.OpAccessChain(ctx.input_f32, ctx.patches.at(index), element)};
return ctx.OpLoad(ctx.F32[1], pointer);
}
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
const Id pointer{[&] {
if (IR::IsGeneric(patch)) {
const u32 index{IR::GenericPatchIndex(patch)};
const Id element{ctx.Constant(ctx.U32[1], IR::GenericPatchElement(patch))};
return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element);
}
switch (patch) {
case IR::Patch::TessellationLodLeft:
case IR::Patch::TessellationLodRight:
case IR::Patch::TessellationLodTop:
case IR::Patch::TessellationLodBottom: {
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
const Id index_id{ctx.Constant(ctx.U32[1], index)};
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id);
}
case IR::Patch::TessellationLodInteriorU:
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
ctx.u32_zero_value);
case IR::Patch::TessellationLodInteriorV:
return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
ctx.Constant(ctx.U32[1], 1u));
default:
throw NotImplementedException("Patch {}", patch);
}
}()};
ctx.OpStore(pointer, value);
}
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) { void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
const Id component_id{ctx.Constant(ctx.U32[1], component)}; const Id component_id{ctx.Constant(ctx.U32[1], component)};
const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)}; const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
@ -301,6 +363,10 @@ Id EmitLocalInvocationId(EmitContext& ctx) {
return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id); return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
} }
Id EmitInvocationId(EmitContext& ctx) {
return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
}
Id EmitIsHelperInvocation(EmitContext& ctx) { Id EmitIsHelperInvocation(EmitContext& ctx) {
return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation); return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation);
} }

View file

@ -331,6 +331,14 @@ void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value, c
Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex); Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex);
} }
F32 IREmitter::GetPatch(Patch patch) {
return Inst<F32>(Opcode::GetPatch, patch);
}
void IREmitter::SetPatch(Patch patch, const F32& value) {
Inst(Opcode::SetPatch, patch, value);
}
void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) { void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) {
Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value); Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value);
} }
@ -363,6 +371,10 @@ U32 IREmitter::LocalInvocationIdZ() {
return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)}; return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)};
} }
U32 IREmitter::InvocationId() {
return Inst<U32>(Opcode::InvocationId);
}
U1 IREmitter::IsHelperInvocation() { U1 IREmitter::IsHelperInvocation() {
return Inst<U1>(Opcode::IsHelperInvocation); return Inst<U1>(Opcode::IsHelperInvocation);
} }

View file

@ -84,6 +84,9 @@ public:
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex); [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex);
void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex); void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex);
[[nodiscard]] F32 GetPatch(Patch patch);
void SetPatch(Patch patch, const F32& value);
void SetFragColor(u32 index, u32 component, const F32& value); void SetFragColor(u32 index, u32 component, const F32& value);
void SetFragDepth(const F32& value); void SetFragDepth(const F32& value);
@ -95,6 +98,7 @@ public:
[[nodiscard]] U32 LocalInvocationIdY(); [[nodiscard]] U32 LocalInvocationIdY();
[[nodiscard]] U32 LocalInvocationIdZ(); [[nodiscard]] U32 LocalInvocationIdZ();
[[nodiscard]] U32 InvocationId();
[[nodiscard]] U1 IsHelperInvocation(); [[nodiscard]] U1 IsHelperInvocation();
[[nodiscard]] U32 LaneId(); [[nodiscard]] U32 LaneId();

View file

@ -73,6 +73,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::EndPrimitive: case Opcode::EndPrimitive:
case Opcode::SetAttribute: case Opcode::SetAttribute:
case Opcode::SetAttributeIndexed: case Opcode::SetAttributeIndexed:
case Opcode::SetPatch:
case Opcode::SetFragColor: case Opcode::SetFragColor:
case Opcode::SetFragDepth: case Opcode::SetFragDepth:
case Opcode::WriteGlobalU8: case Opcode::WriteGlobalU8:

View file

@ -24,6 +24,7 @@ constexpr Type Label{Type::Label};
constexpr Type Reg{Type::Reg}; constexpr Type Reg{Type::Reg};
constexpr Type Pred{Type::Pred}; constexpr Type Pred{Type::Pred};
constexpr Type Attribute{Type::Attribute}; constexpr Type Attribute{Type::Attribute};
constexpr Type Patch{Type::Patch};
constexpr Type U1{Type::U1}; constexpr Type U1{Type::U1};
constexpr Type U8{Type::U8}; constexpr Type U8{Type::U8};
constexpr Type U16{Type::U16}; constexpr Type U16{Type::U16};

View file

@ -48,6 +48,8 @@ OPCODE(GetAttribute, F32, Attr
OPCODE(SetAttribute, Void, Attribute, F32, U32, ) OPCODE(SetAttribute, Void, Attribute, F32, U32, )
OPCODE(GetAttributeIndexed, F32, U32, U32, ) OPCODE(GetAttributeIndexed, F32, U32, U32, )
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, ) OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
OPCODE(GetPatch, F32, Patch, )
OPCODE(SetPatch, Void, Patch, F32, )
OPCODE(SetFragColor, Void, U32, U32, F32, ) OPCODE(SetFragColor, Void, U32, U32, F32, )
OPCODE(SetFragDepth, Void, F32, ) OPCODE(SetFragDepth, Void, F32, )
OPCODE(GetZFlag, U1, Void, ) OPCODE(GetZFlag, U1, Void, )
@ -60,6 +62,7 @@ OPCODE(SetCFlag, Void, U1,
OPCODE(SetOFlag, Void, U1, ) OPCODE(SetOFlag, Void, U1, )
OPCODE(WorkgroupId, U32x3, ) OPCODE(WorkgroupId, U32x3, )
OPCODE(LocalInvocationId, U32x3, ) OPCODE(LocalInvocationId, U32x3, )
OPCODE(InvocationId, U32, )
OPCODE(IsHelperInvocation, U1, ) OPCODE(IsHelperInvocation, U1, )
// Undefined // Undefined

View file

@ -0,0 +1,28 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/frontend/ir/patch.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
bool IsGeneric(Patch patch) noexcept {
return patch >= Patch::Component0 && patch <= Patch::Component119;
}
u32 GenericPatchIndex(Patch patch) {
if (!IsGeneric(patch)) {
throw InvalidArgument("Patch {} is not generic", patch);
}
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4;
}
u32 GenericPatchElement(Patch patch) {
if (!IsGeneric(patch)) {
throw InvalidArgument("Patch {} is not generic", patch);
}
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4;
}
} // namespace Shader::IR

View file

@ -0,0 +1,149 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Shader::IR {
enum class Patch : u64 {
TessellationLodLeft,
TessellationLodTop,
TessellationLodRight,
TessellationLodBottom,
TessellationLodInteriorU,
TessellationLodInteriorV,
ComponentPadding0,
ComponentPadding1,
Component0,
Component1,
Component2,
Component3,
Component4,
Component5,
Component6,
Component7,
Component8,
Component9,
Component10,
Component11,
Component12,
Component13,
Component14,
Component15,
Component16,
Component17,
Component18,
Component19,
Component20,
Component21,
Component22,
Component23,
Component24,
Component25,
Component26,
Component27,
Component28,
Component29,
Component30,
Component31,
Component32,
Component33,
Component34,
Component35,
Component36,
Component37,
Component38,
Component39,
Component40,
Component41,
Component42,
Component43,
Component44,
Component45,
Component46,
Component47,
Component48,
Component49,
Component50,
Component51,
Component52,
Component53,
Component54,
Component55,
Component56,
Component57,
Component58,
Component59,
Component60,
Component61,
Component62,
Component63,
Component64,
Component65,
Component66,
Component67,
Component68,
Component69,
Component70,
Component71,
Component72,
Component73,
Component74,
Component75,
Component76,
Component77,
Component78,
Component79,
Component80,
Component81,
Component82,
Component83,
Component84,
Component85,
Component86,
Component87,
Component88,
Component89,
Component90,
Component91,
Component92,
Component93,
Component94,
Component95,
Component96,
Component97,
Component98,
Component99,
Component100,
Component101,
Component102,
Component103,
Component104,
Component105,
Component106,
Component107,
Component108,
Component109,
Component110,
Component111,
Component112,
Component113,
Component114,
Component115,
Component116,
Component117,
Component118,
Component119,
};
static_assert(static_cast<u64>(Patch::Component119) == 127);
[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
[[nodiscard]] u32 GenericPatchIndex(Patch patch);
[[nodiscard]] u32 GenericPatchElement(Patch patch);
} // namespace Shader::IR

View file

@ -20,26 +20,27 @@ enum class Type {
Reg = 1 << 2, Reg = 1 << 2,
Pred = 1 << 3, Pred = 1 << 3,
Attribute = 1 << 4, Attribute = 1 << 4,
U1 = 1 << 5, Patch = 1 << 5,
U8 = 1 << 6, U1 = 1 << 6,
U16 = 1 << 7, U8 = 1 << 7,
U32 = 1 << 8, U16 = 1 << 8,
U64 = 1 << 9, U32 = 1 << 9,
F16 = 1 << 10, U64 = 1 << 10,
F32 = 1 << 11, F16 = 1 << 11,
F64 = 1 << 12, F32 = 1 << 12,
U32x2 = 1 << 13, F64 = 1 << 13,
U32x3 = 1 << 14, U32x2 = 1 << 14,
U32x4 = 1 << 15, U32x3 = 1 << 15,
F16x2 = 1 << 16, U32x4 = 1 << 16,
F16x3 = 1 << 17, F16x2 = 1 << 17,
F16x4 = 1 << 18, F16x3 = 1 << 18,
F32x2 = 1 << 19, F16x4 = 1 << 19,
F32x3 = 1 << 20, F32x2 = 1 << 20,
F32x4 = 1 << 21, F32x3 = 1 << 21,
F64x2 = 1 << 22, F32x4 = 1 << 22,
F64x3 = 1 << 23, F64x2 = 1 << 23,
F64x4 = 1 << 24, F64x3 = 1 << 24,
F64x4 = 1 << 25,
}; };
DECLARE_ENUM_FLAG_OPERATORS(Type) DECLARE_ENUM_FLAG_OPERATORS(Type)

View file

@ -18,6 +18,8 @@ Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {} Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {}
Value::Value(IR::Patch value) noexcept : type{Type::Patch}, patch{value} {}
Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {} Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {} Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
@ -109,6 +111,11 @@ IR::Attribute Value::Attribute() const {
return attribute; return attribute;
} }
IR::Patch Value::Patch() const {
ValidateAccess(Type::Patch);
return patch;
}
bool Value::U1() const { bool Value::U1() const {
if (IsIdentity()) { if (IsIdentity()) {
return inst->Arg(0).U1(); return inst->Arg(0).U1();
@ -182,6 +189,8 @@ bool Value::operator==(const Value& other) const {
return pred == other.pred; return pred == other.pred;
case Type::Attribute: case Type::Attribute:
return attribute == other.attribute; return attribute == other.attribute;
case Type::Patch:
return patch == other.patch;
case Type::U1: case Type::U1:
return imm_u1 == other.imm_u1; return imm_u1 == other.imm_u1;
case Type::U8: case Type::U8:

View file

@ -9,6 +9,7 @@
#include "shader_recompiler/frontend/ir/attribute.h" #include "shader_recompiler/frontend/ir/attribute.h"
#include "shader_recompiler/frontend/ir/pred.h" #include "shader_recompiler/frontend/ir/pred.h"
#include "shader_recompiler/frontend/ir/reg.h" #include "shader_recompiler/frontend/ir/reg.h"
#include "shader_recompiler/frontend/ir/patch.h"
#include "shader_recompiler/frontend/ir/type.h" #include "shader_recompiler/frontend/ir/type.h"
namespace Shader::IR { namespace Shader::IR {
@ -24,6 +25,7 @@ public:
explicit Value(IR::Reg value) noexcept; explicit Value(IR::Reg value) noexcept;
explicit Value(IR::Pred value) noexcept; explicit Value(IR::Pred value) noexcept;
explicit Value(IR::Attribute value) noexcept; explicit Value(IR::Attribute value) noexcept;
explicit Value(IR::Patch value) noexcept;
explicit Value(bool value) noexcept; explicit Value(bool value) noexcept;
explicit Value(u8 value) noexcept; explicit Value(u8 value) noexcept;
explicit Value(u16 value) noexcept; explicit Value(u16 value) noexcept;
@ -46,6 +48,7 @@ public:
[[nodiscard]] IR::Reg Reg() const; [[nodiscard]] IR::Reg Reg() const;
[[nodiscard]] IR::Pred Pred() const; [[nodiscard]] IR::Pred Pred() const;
[[nodiscard]] IR::Attribute Attribute() const; [[nodiscard]] IR::Attribute Attribute() const;
[[nodiscard]] IR::Patch Patch() const;
[[nodiscard]] bool U1() const; [[nodiscard]] bool U1() const;
[[nodiscard]] u8 U8() const; [[nodiscard]] u8 U8() const;
[[nodiscard]] u16 U16() const; [[nodiscard]] u16 U16() const;
@ -67,6 +70,7 @@ private:
IR::Reg reg; IR::Reg reg;
IR::Pred pred; IR::Pred pred;
IR::Attribute attribute; IR::Attribute attribute;
IR::Patch patch;
bool imm_u1; bool imm_u1;
u8 imm_u8; u8 imm_u8;
u16 imm_u16; u16 imm_u16;

View file

@ -70,6 +70,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
program.stage = env.ShaderStage(); program.stage = env.ShaderStage();
program.local_memory_size = env.LocalMemorySize(); program.local_memory_size = env.LocalMemorySize();
switch (program.stage) { switch (program.stage) {
case Stage::TessellationControl: {
const ProgramHeader& sph{env.SPH()};
program.invocations = sph.common2.threads_per_input_primitive;
break;
}
case Stage::Geometry: { case Stage::Geometry: {
const ProgramHeader& sph{env.SPH()}; const ProgramHeader& sph{env.SPH()};
program.output_topology = sph.common3.output_topology; program.output_topology = sph.common3.output_topology;

View file

@ -70,12 +70,6 @@ void TranslatorVisitor::ALD(u64 insn) {
BitField<47, 2, Size> size; BitField<47, 2, Size> size;
} const ald{insn}; } const ald{insn};
if (ald.o != 0) {
throw NotImplementedException("O");
}
if (ald.patch != 0) {
throw NotImplementedException("P");
}
const u64 offset{ald.absolute_offset.Value()}; const u64 offset{ald.absolute_offset.Value()};
if (offset % 4 != 0) { if (offset % 4 != 0) {
throw NotImplementedException("Unaligned absolute offset {}", offset); throw NotImplementedException("Unaligned absolute offset {}", offset);
@ -84,11 +78,19 @@ void TranslatorVisitor::ALD(u64 insn) {
const u32 num_elements{NumElements(ald.size)}; const u32 num_elements{NumElements(ald.size)};
if (ald.index_reg == IR::Reg::RZ) { if (ald.index_reg == IR::Reg::RZ) {
for (u32 element = 0; element < num_elements; ++element) { for (u32 element = 0; element < num_elements; ++element) {
const IR::Attribute attr{offset / 4 + element}; if (ald.patch != 0) {
F(ald.dest_reg + element, ir.GetAttribute(attr, vertex)); const IR::Patch patch{offset / 4 + element};
F(ald.dest_reg + element, ir.GetPatch(patch));
} else {
const IR::Attribute attr{offset / 4 + element};
F(ald.dest_reg + element, ir.GetAttribute(attr, vertex));
}
} }
return; return;
} }
if (ald.patch != 0) {
throw NotImplementedException("Indirect patch read");
}
HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex)); F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex));
}); });
@ -106,9 +108,6 @@ void TranslatorVisitor::AST(u64 insn) {
BitField<47, 2, Size> size; BitField<47, 2, Size> size;
} const ast{insn}; } const ast{insn};
if (ast.patch != 0) {
throw NotImplementedException("P");
}
if (ast.index_reg != IR::Reg::RZ) { if (ast.index_reg != IR::Reg::RZ) {
throw NotImplementedException("Indexed store"); throw NotImplementedException("Indexed store");
} }
@ -120,11 +119,19 @@ void TranslatorVisitor::AST(u64 insn) {
const u32 num_elements{NumElements(ast.size)}; const u32 num_elements{NumElements(ast.size)};
if (ast.index_reg == IR::Reg::RZ) { if (ast.index_reg == IR::Reg::RZ) {
for (u32 element = 0; element < num_elements; ++element) { for (u32 element = 0; element < num_elements; ++element) {
const IR::Attribute attr{offset / 4 + element}; if (ast.patch != 0) {
ir.SetAttribute(attr, F(ast.src_reg + element), vertex); const IR::Patch patch{offset / 4 + element};
ir.SetPatch(patch, F(ast.src_reg + element));
} else {
const IR::Attribute attr{offset / 4 + element};
ir.SetAttribute(attr, F(ast.src_reg + element), vertex);
}
} }
return; return;
} }
if (ast.patch != 0) {
throw NotImplementedException("Indexed tessellation patch store");
}
HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex); ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex);
}); });

View file

@ -113,6 +113,8 @@ enum class SpecialRegister : u64 {
[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) { [[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) {
switch (special_register) { switch (special_register) {
case SpecialRegister::SR_INVOCATION_ID:
return ir.InvocationId();
case SpecialRegister::SR_THREAD_KILL: case SpecialRegister::SR_THREAD_KILL:
return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
case SpecialRegister::SR_INVOCATION_INFO: case SpecialRegister::SR_INVOCATION_INFO:

View file

@ -53,6 +53,10 @@ void GetAttribute(Info& info, IR::Attribute attribute) {
case IR::Attribute::PointSpriteT: case IR::Attribute::PointSpriteT:
info.loads_point_coord = true; info.loads_point_coord = true;
break; break;
case IR::Attribute::TessellationEvaluationPointU:
case IR::Attribute::TessellationEvaluationPointV:
info.loads_tess_coord = true;
break;
default: default:
throw NotImplementedException("Get attribute {}", attribute); throw NotImplementedException("Get attribute {}", attribute);
} }
@ -94,6 +98,34 @@ void SetAttribute(Info& info, IR::Attribute attribute) {
} }
} }
void GetPatch(Info& info, IR::Patch patch) {
if (!IR::IsGeneric(patch)) {
throw NotImplementedException("Reading non-generic patch {}", patch);
}
info.uses_patches.at(IR::GenericPatchIndex(patch)) = true;
}
void SetPatch(Info& info, IR::Patch patch) {
if (IR::IsGeneric(patch)) {
info.uses_patches.at(IR::GenericPatchIndex(patch)) = true;
return;
}
switch (patch) {
case IR::Patch::TessellationLodLeft:
case IR::Patch::TessellationLodTop:
case IR::Patch::TessellationLodRight:
case IR::Patch::TessellationLodBottom:
info.stores_tess_level_outer = true;
break;
case IR::Patch::TessellationLodInteriorU:
case IR::Patch::TessellationLodInteriorV:
info.stores_tess_level_inner = true;
break;
default:
throw NotImplementedException("Set patch {}", patch);
}
}
void VisitUsages(Info& info, IR::Inst& inst) { void VisitUsages(Info& info, IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::CompositeConstructF16x2: case IR::Opcode::CompositeConstructF16x2:
@ -350,6 +382,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::SetAttribute: case IR::Opcode::SetAttribute:
SetAttribute(info, inst.Arg(0).Attribute()); SetAttribute(info, inst.Arg(0).Attribute());
break; break;
case IR::Opcode::GetPatch:
GetPatch(info, inst.Arg(0).Patch());
break;
case IR::Opcode::SetPatch:
SetPatch(info, inst.Arg(0).Patch());
break;
case IR::Opcode::GetAttributeIndexed: case IR::Opcode::GetAttributeIndexed:
info.loads_indexed_attributes = true; info.loads_indexed_attributes = true;
break; break;
@ -368,6 +406,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::LocalInvocationId: case IR::Opcode::LocalInvocationId:
info.uses_local_invocation_id = true; info.uses_local_invocation_id = true;
break; break;
case IR::Opcode::InvocationId:
info.uses_invocation_id = true;
break;
case IR::Opcode::IsHelperInvocation: case IR::Opcode::IsHelperInvocation:
info.uses_is_helper_invocation = true; info.uses_is_helper_invocation = true;
break; break;

View file

@ -38,6 +38,18 @@ enum class CompareFunction {
Always, Always,
}; };
enum class TessPrimitive {
Isolines,
Triangles,
Quads,
};
enum class TessSpacing {
Equal,
FractionalOdd,
FractionalEven,
};
struct TransformFeedbackVarying { struct TransformFeedbackVarying {
u32 buffer{}; u32 buffer{};
u32 stride{}; u32 stride{};
@ -74,6 +86,10 @@ struct Profile {
bool convert_depth_mode{}; bool convert_depth_mode{};
bool force_early_z{}; bool force_early_z{};
TessPrimitive tess_primitive{};
TessSpacing tess_spacing{};
bool tess_clockwise{};
InputTopology input_topology{}; InputTopology input_topology{};
std::optional<float> fixed_state_point_size; std::optional<float> fixed_state_point_size;

View file

@ -101,8 +101,10 @@ struct Info {
bool uses_workgroup_id{}; bool uses_workgroup_id{};
bool uses_local_invocation_id{}; bool uses_local_invocation_id{};
bool uses_invocation_id{};
bool uses_is_helper_invocation{}; bool uses_is_helper_invocation{};
bool uses_subgroup_invocation_id{}; bool uses_subgroup_invocation_id{};
std::array<bool, 30> uses_patches{};
std::array<InputVarying, 32> input_generics{}; std::array<InputVarying, 32> input_generics{};
bool loads_position{}; bool loads_position{};
@ -110,6 +112,7 @@ struct Info {
bool loads_vertex_id{}; bool loads_vertex_id{};
bool loads_front_face{}; bool loads_front_face{};
bool loads_point_coord{}; bool loads_point_coord{};
bool loads_tess_coord{};
bool loads_indexed_attributes{}; bool loads_indexed_attributes{};
std::array<bool, 8> stores_frag_color{}; std::array<bool, 8> stores_frag_color{};
@ -120,6 +123,8 @@ struct Info {
bool stores_clip_distance{}; bool stores_clip_distance{};
bool stores_layer{}; bool stores_layer{};
bool stores_viewport_index{}; bool stores_viewport_index{};
bool stores_tess_level_outer{};
bool stores_tess_level_inner{};
bool stores_indexed_attributes{}; bool stores_indexed_attributes{};
bool uses_fp16{}; bool uses_fp16{};

View file

@ -685,6 +685,19 @@ VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face) {
return {}; return {};
} }
VkPolygonMode PolygonMode(Maxwell::PolygonMode polygon_mode) {
switch (polygon_mode) {
case Maxwell::PolygonMode::Point:
return VK_POLYGON_MODE_POINT;
case Maxwell::PolygonMode::Line:
return VK_POLYGON_MODE_LINE;
case Maxwell::PolygonMode::Fill:
return VK_POLYGON_MODE_FILL;
}
UNIMPLEMENTED_MSG("Unimplemented polygon mode={}", polygon_mode);
return {};
}
VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle) { VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle) {
switch (swizzle) { switch (swizzle) {
case Tegra::Texture::SwizzleSource::Zero: case Tegra::Texture::SwizzleSource::Zero:

View file

@ -65,6 +65,8 @@ VkFrontFace FrontFace(Maxwell::FrontFace front_face);
VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face); VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face);
VkPolygonMode PolygonMode(Maxwell::PolygonMode polygon_mode);
VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle); VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle); VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle);

View file

@ -355,7 +355,8 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
.rasterizerDiscardEnable = .rasterizerDiscardEnable =
static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
.polygonMode = VK_POLYGON_MODE_FILL, .polygonMode =
MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(state.polygon_mode)),
.cullMode = static_cast<VkCullModeFlags>( .cullMode = static_cast<VkCullModeFlags>(
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),

View file

@ -1040,6 +1040,36 @@ Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key,
std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(),
&CastAttributeType); &CastAttributeType);
break; break;
case Shader::Stage::TessellationEval:
// We have to flip tessellation clockwise for some reason...
profile.tess_clockwise = key.state.tessellation_clockwise == 0;
profile.tess_primitive = [&key] {
const u32 raw{key.state.tessellation_primitive.Value()};
switch (static_cast<Maxwell::TessellationPrimitive>(raw)) {
case Maxwell::TessellationPrimitive::Isolines:
return Shader::TessPrimitive::Isolines;
case Maxwell::TessellationPrimitive::Triangles:
return Shader::TessPrimitive::Triangles;
case Maxwell::TessellationPrimitive::Quads:
return Shader::TessPrimitive::Quads;
}
UNREACHABLE();
return Shader::TessPrimitive::Triangles;
}();
profile.tess_spacing = [&] {
const u32 raw{key.state.tessellation_spacing};
switch (static_cast<Maxwell::TessellationSpacing>(raw)) {
case Maxwell::TessellationSpacing::Equal:
return Shader::TessSpacing::Equal;
case Maxwell::TessellationSpacing::FractionalOdd:
return Shader::TessSpacing::FractionalOdd;
case Maxwell::TessellationSpacing::FractionalEven:
return Shader::TessSpacing::FractionalEven;
}
UNREACHABLE();
return Shader::TessSpacing::Equal;
}();
break;
case Shader::Stage::Geometry: case Shader::Stage::Geometry:
if (program.output_topology == Shader::OutputTopology::PointList) { if (program.output_topology == Shader::OutputTopology::PointList) {
profile.fixed_state_point_size = point_size; profile.fixed_state_point_size = point_size;

View file

@ -91,7 +91,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
.flags = 0, .flags = 0,
.size = STREAM_BUFFER_SIZE, .size = STREAM_BUFFER_SIZE,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,

View file

@ -225,7 +225,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
.drawIndirectFirstInstance = false, .drawIndirectFirstInstance = false,
.depthClamp = true, .depthClamp = true,
.depthBiasClamp = true, .depthBiasClamp = true,
.fillModeNonSolid = false, .fillModeNonSolid = true,
.depthBounds = false, .depthBounds = false,
.wideLines = false, .wideLines = false,
.largePoints = true, .largePoints = true,
@ -670,6 +670,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
std::make_pair(features.largePoints, "largePoints"), std::make_pair(features.largePoints, "largePoints"),
std::make_pair(features.multiViewport, "multiViewport"), std::make_pair(features.multiViewport, "multiViewport"),
std::make_pair(features.depthBiasClamp, "depthBiasClamp"), std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"),
std::make_pair(features.geometryShader, "geometryShader"), std::make_pair(features.geometryShader, "geometryShader"),
std::make_pair(features.tessellationShader, "tessellationShader"), std::make_pair(features.tessellationShader, "tessellationShader"),
std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),