mirror of
https://github.com/yuzu-emu/yuzu-mainline.git
synced 2024-12-27 09:25:26 +00:00
shader: Implement tessellation shaders, polygon mode and invocation id
This commit is contained in:
parent
34519d3fc6
commit
183855e396
|
@ -41,6 +41,8 @@ add_library(shader_recompiler STATIC
|
|||
frontend/ir/opcodes.cpp
|
||||
frontend/ir/opcodes.h
|
||||
frontend/ir/opcodes.inc
|
||||
frontend/ir/patch.cpp
|
||||
frontend/ir/patch.h
|
||||
frontend/ir/post_order.cpp
|
||||
frontend/ir/post_order.h
|
||||
frontend/ir/pred.h
|
||||
|
|
|
@ -125,19 +125,36 @@ u32 NumVertices(InputTopology input_topology) {
|
|||
throw InvalidArgument("Invalid input topology {}", input_topology);
|
||||
}
|
||||
|
||||
Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) {
|
||||
if (ctx.stage == Stage::Geometry) {
|
||||
const u32 num_vertices{NumVertices(ctx.profile.input_topology)};
|
||||
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices));
|
||||
Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
|
||||
std::optional<spv::BuiltIn> builtin = std::nullopt) {
|
||||
switch (ctx.stage) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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"};
|
||||
const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
|
||||
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 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));
|
||||
if (element > 0) {
|
||||
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);
|
||||
}
|
||||
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)};
|
||||
ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle));
|
||||
} else {
|
||||
ctx.Name(id, fmt::format("out_attr{}", index));
|
||||
}
|
||||
const GenericElementInfo info{
|
||||
.id = id,
|
||||
|
@ -383,7 +400,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin
|
|||
AddCapability(spv::Capability::Shader);
|
||||
DefineCommonTypes(program.info);
|
||||
DefineCommonConstants();
|
||||
DefineInterfaces(program.info);
|
||||
DefineInterfaces(program);
|
||||
DefineLocalMemory(program);
|
||||
DefineSharedMemory(program);
|
||||
DefineSharedMemoryFunctions(program);
|
||||
|
@ -472,9 +489,9 @@ void EmitContext::DefineCommonConstants() {
|
|||
f32_zero_value = Constant(F32[1], 0.0f);
|
||||
}
|
||||
|
||||
void EmitContext::DefineInterfaces(const Info& info) {
|
||||
DefineInputs(info);
|
||||
DefineOutputs(info);
|
||||
void EmitContext::DefineInterfaces(const IR::Program& program) {
|
||||
DefineInputs(program.info);
|
||||
DefineOutputs(program);
|
||||
}
|
||||
|
||||
void EmitContext::DefineLocalMemory(const IR::Program& program) {
|
||||
|
@ -972,26 +989,29 @@ void EmitContext::DefineLabels(IR::Program& program) {
|
|||
|
||||
void EmitContext::DefineInputs(const Info& info) {
|
||||
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) {
|
||||
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) {
|
||||
is_helper_invocation = DefineInput(*this, U1, spv::BuiltIn::HelperInvocation);
|
||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||
}
|
||||
if (info.uses_subgroup_mask) {
|
||||
subgroup_mask_eq = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupEqMaskKHR);
|
||||
subgroup_mask_lt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLtMaskKHR);
|
||||
subgroup_mask_le = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLeMaskKHR);
|
||||
subgroup_mask_gt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGtMaskKHR);
|
||||
subgroup_mask_ge = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGeMaskKHR);
|
||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
||||
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
||||
}
|
||||
if (info.uses_subgroup_invocation_id ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
|
||||
subgroup_local_invocation_id =
|
||||
DefineInput(*this, U32[1], spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
}
|
||||
if (info.uses_fswzadd) {
|
||||
const Id f32_one{Constant(F32[1], 1.0f)};
|
||||
|
@ -1004,29 +1024,32 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
if (info.loads_position) {
|
||||
const bool is_fragment{stage != Stage::Fragment};
|
||||
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 (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 {
|
||||
instance_index = DefineInput(*this, U32[1], spv::BuiltIn::InstanceIndex);
|
||||
base_instance = DefineInput(*this, U32[1], spv::BuiltIn::BaseInstance);
|
||||
instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
|
||||
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
|
||||
}
|
||||
}
|
||||
if (info.loads_vertex_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 {
|
||||
vertex_index = DefineInput(*this, U32[1], spv::BuiltIn::VertexIndex);
|
||||
base_vertex = DefineInput(*this, U32[1], spv::BuiltIn::BaseVertex);
|
||||
vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
|
||||
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
const InputVarying generic{info.input_generics[index]};
|
||||
|
@ -1038,7 +1061,7 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
continue;
|
||||
}
|
||||
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));
|
||||
Name(id, fmt::format("in_attr{}", index));
|
||||
input_generics[index] = id;
|
||||
|
@ -1059,58 +1082,98 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
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) {
|
||||
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 (stage == Stage::Fragment) {
|
||||
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 (stage == Stage::Fragment) {
|
||||
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
||||
}
|
||||
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 &&
|
||||
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
|
||||
if (stage == Stage::Fragment) {
|
||||
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 &&
|
||||
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
|
||||
if (stage == Stage::Fragment) {
|
||||
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) {
|
||||
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) {
|
||||
if (!info.stores_frag_color[index]) {
|
||||
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);
|
||||
Name(frag_color[index], fmt::format("frag_color{}", index));
|
||||
}
|
||||
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);
|
||||
Name(frag_depth, "frag_depth");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@ public:
|
|||
|
||||
Id workgroup_id{};
|
||||
Id local_invocation_id{};
|
||||
Id invocation_id{};
|
||||
Id is_helper_invocation{};
|
||||
Id subgroup_local_invocation_id{};
|
||||
Id subgroup_mask_eq{};
|
||||
|
@ -162,6 +163,7 @@ public:
|
|||
Id base_vertex{};
|
||||
Id front_face{};
|
||||
Id point_coord{};
|
||||
Id tess_coord{};
|
||||
Id clip_distances{};
|
||||
Id layer{};
|
||||
Id viewport_index{};
|
||||
|
@ -204,6 +206,10 @@ public:
|
|||
Id output_position{};
|
||||
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{};
|
||||
Id frag_depth{};
|
||||
|
||||
|
@ -212,7 +218,7 @@ public:
|
|||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
void DefineCommonConstants();
|
||||
void DefineInterfaces(const Info& info);
|
||||
void DefineInterfaces(const IR::Program& program);
|
||||
void DefineLocalMemory(const IR::Program& program);
|
||||
void DefineSharedMemory(const IR::Program& program);
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
|
@ -226,7 +232,7 @@ private:
|
|||
void DefineLabels(IR::Program& program);
|
||||
|
||||
void DefineInputs(const Info& info);
|
||||
void DefineOutputs(const Info& info);
|
||||
void DefineOutputs(const IR::Program& program);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -45,6 +45,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
|||
return arg.Label();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::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>) {
|
||||
return arg.Reg();
|
||||
}
|
||||
|
@ -120,6 +122,30 @@ Id DefineMain(EmitContext& ctx, IR::Program& program) {
|
|||
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) {
|
||||
const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
|
||||
spv::ExecutionModel execution_model{};
|
||||
|
@ -134,6 +160,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
case Stage::VertexB:
|
||||
execution_model = spv::ExecutionModel::Vertex;
|
||||
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:
|
||||
execution_model = spv::ExecutionModel::Geometry;
|
||||
ctx.AddCapability(spv::Capability::Geometry);
|
||||
|
|
|
@ -55,6 +55,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
|||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, 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 EmitSetFragDepth(EmitContext& ctx, Id value);
|
||||
void EmitGetZFlag(EmitContext& ctx);
|
||||
|
@ -67,6 +69,7 @@ void EmitSetCFlag(EmitContext& ctx);
|
|||
void EmitSetOFlag(EmitContext& ctx);
|
||||
Id EmitWorkgroupId(EmitContext& ctx);
|
||||
Id EmitLocalInvocationId(EmitContext& ctx);
|
||||
Id EmitInvocationId(EmitContext& ctx);
|
||||
Id EmitIsHelperInvocation(EmitContext& ctx);
|
||||
Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
|
||||
void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
|
||||
|
|
|
@ -32,13 +32,26 @@ std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
|||
|
||||
template <typename... 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)...);
|
||||
} else {
|
||||
default:
|
||||
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) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
|
@ -49,7 +62,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
|||
} else {
|
||||
const u32 index_element{element - info.first_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) {
|
||||
|
@ -61,7 +74,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
|||
case IR::Attribute::PositionW: {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
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::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 index{static_cast<u32>(attr) - base};
|
||||
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:
|
||||
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.u32_zero_value);
|
||||
case IR::Attribute::PointSpriteS:
|
||||
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord,
|
||||
ctx.u32_zero_value));
|
||||
return ctx.OpLoad(ctx.F32[1],
|
||||
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
|
||||
case IR::Attribute::PointSpriteT:
|
||||
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord,
|
||||
ctx.Constant(ctx.U32[1], 1U)));
|
||||
return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord,
|
||||
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:
|
||||
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) {
|
||||
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);
|
||||
} else {
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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)};
|
||||
|
@ -301,6 +363,10 @@ Id EmitLocalInvocationId(EmitContext& ctx) {
|
|||
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) {
|
||||
return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation);
|
||||
}
|
||||
|
|
|
@ -331,6 +331,14 @@ void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value, c
|
|||
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) {
|
||||
Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value);
|
||||
}
|
||||
|
@ -363,6 +371,10 @@ U32 IREmitter::LocalInvocationIdZ() {
|
|||
return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)};
|
||||
}
|
||||
|
||||
U32 IREmitter::InvocationId() {
|
||||
return Inst<U32>(Opcode::InvocationId);
|
||||
}
|
||||
|
||||
U1 IREmitter::IsHelperInvocation() {
|
||||
return Inst<U1>(Opcode::IsHelperInvocation);
|
||||
}
|
||||
|
|
|
@ -84,6 +84,9 @@ public:
|
|||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, 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 SetFragDepth(const F32& value);
|
||||
|
||||
|
@ -95,6 +98,7 @@ public:
|
|||
[[nodiscard]] U32 LocalInvocationIdY();
|
||||
[[nodiscard]] U32 LocalInvocationIdZ();
|
||||
|
||||
[[nodiscard]] U32 InvocationId();
|
||||
[[nodiscard]] U1 IsHelperInvocation();
|
||||
|
||||
[[nodiscard]] U32 LaneId();
|
||||
|
|
|
@ -73,6 +73,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
|
|||
case Opcode::EndPrimitive:
|
||||
case Opcode::SetAttribute:
|
||||
case Opcode::SetAttributeIndexed:
|
||||
case Opcode::SetPatch:
|
||||
case Opcode::SetFragColor:
|
||||
case Opcode::SetFragDepth:
|
||||
case Opcode::WriteGlobalU8:
|
||||
|
|
|
@ -24,6 +24,7 @@ constexpr Type Label{Type::Label};
|
|||
constexpr Type Reg{Type::Reg};
|
||||
constexpr Type Pred{Type::Pred};
|
||||
constexpr Type Attribute{Type::Attribute};
|
||||
constexpr Type Patch{Type::Patch};
|
||||
constexpr Type U1{Type::U1};
|
||||
constexpr Type U8{Type::U8};
|
||||
constexpr Type U16{Type::U16};
|
||||
|
|
|
@ -48,6 +48,8 @@ OPCODE(GetAttribute, F32, Attr
|
|||
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
|
||||
OPCODE(GetAttributeIndexed, F32, U32, U32, )
|
||||
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
|
||||
OPCODE(GetPatch, F32, Patch, )
|
||||
OPCODE(SetPatch, Void, Patch, F32, )
|
||||
OPCODE(SetFragColor, Void, U32, U32, F32, )
|
||||
OPCODE(SetFragDepth, Void, F32, )
|
||||
OPCODE(GetZFlag, U1, Void, )
|
||||
|
@ -60,6 +62,7 @@ OPCODE(SetCFlag, Void, U1,
|
|||
OPCODE(SetOFlag, Void, U1, )
|
||||
OPCODE(WorkgroupId, U32x3, )
|
||||
OPCODE(LocalInvocationId, U32x3, )
|
||||
OPCODE(InvocationId, U32, )
|
||||
OPCODE(IsHelperInvocation, U1, )
|
||||
|
||||
// Undefined
|
||||
|
|
28
src/shader_recompiler/frontend/ir/patch.cpp
Normal file
28
src/shader_recompiler/frontend/ir/patch.cpp
Normal 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
|
149
src/shader_recompiler/frontend/ir/patch.h
Normal file
149
src/shader_recompiler/frontend/ir/patch.h
Normal 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
|
|
@ -20,26 +20,27 @@ enum class Type {
|
|||
Reg = 1 << 2,
|
||||
Pred = 1 << 3,
|
||||
Attribute = 1 << 4,
|
||||
U1 = 1 << 5,
|
||||
U8 = 1 << 6,
|
||||
U16 = 1 << 7,
|
||||
U32 = 1 << 8,
|
||||
U64 = 1 << 9,
|
||||
F16 = 1 << 10,
|
||||
F32 = 1 << 11,
|
||||
F64 = 1 << 12,
|
||||
U32x2 = 1 << 13,
|
||||
U32x3 = 1 << 14,
|
||||
U32x4 = 1 << 15,
|
||||
F16x2 = 1 << 16,
|
||||
F16x3 = 1 << 17,
|
||||
F16x4 = 1 << 18,
|
||||
F32x2 = 1 << 19,
|
||||
F32x3 = 1 << 20,
|
||||
F32x4 = 1 << 21,
|
||||
F64x2 = 1 << 22,
|
||||
F64x3 = 1 << 23,
|
||||
F64x4 = 1 << 24,
|
||||
Patch = 1 << 5,
|
||||
U1 = 1 << 6,
|
||||
U8 = 1 << 7,
|
||||
U16 = 1 << 8,
|
||||
U32 = 1 << 9,
|
||||
U64 = 1 << 10,
|
||||
F16 = 1 << 11,
|
||||
F32 = 1 << 12,
|
||||
F64 = 1 << 13,
|
||||
U32x2 = 1 << 14,
|
||||
U32x3 = 1 << 15,
|
||||
U32x4 = 1 << 16,
|
||||
F16x2 = 1 << 17,
|
||||
F16x3 = 1 << 18,
|
||||
F16x4 = 1 << 19,
|
||||
F32x2 = 1 << 20,
|
||||
F32x3 = 1 << 21,
|
||||
F32x4 = 1 << 22,
|
||||
F64x2 = 1 << 23,
|
||||
F64x3 = 1 << 24,
|
||||
F64x4 = 1 << 25,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(Type)
|
||||
|
||||
|
|
|
@ -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::Patch value) noexcept : type{Type::Patch}, patch{value} {}
|
||||
|
||||
Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
|
||||
|
||||
Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
|
||||
|
@ -109,6 +111,11 @@ IR::Attribute Value::Attribute() const {
|
|||
return attribute;
|
||||
}
|
||||
|
||||
IR::Patch Value::Patch() const {
|
||||
ValidateAccess(Type::Patch);
|
||||
return patch;
|
||||
}
|
||||
|
||||
bool Value::U1() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U1();
|
||||
|
@ -182,6 +189,8 @@ bool Value::operator==(const Value& other) const {
|
|||
return pred == other.pred;
|
||||
case Type::Attribute:
|
||||
return attribute == other.attribute;
|
||||
case Type::Patch:
|
||||
return patch == other.patch;
|
||||
case Type::U1:
|
||||
return imm_u1 == other.imm_u1;
|
||||
case Type::U8:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
#include "shader_recompiler/frontend/ir/pred.h"
|
||||
#include "shader_recompiler/frontend/ir/reg.h"
|
||||
#include "shader_recompiler/frontend/ir/patch.h"
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
@ -24,6 +25,7 @@ public:
|
|||
explicit Value(IR::Reg value) noexcept;
|
||||
explicit Value(IR::Pred value) noexcept;
|
||||
explicit Value(IR::Attribute value) noexcept;
|
||||
explicit Value(IR::Patch value) noexcept;
|
||||
explicit Value(bool value) noexcept;
|
||||
explicit Value(u8 value) noexcept;
|
||||
explicit Value(u16 value) noexcept;
|
||||
|
@ -46,6 +48,7 @@ public:
|
|||
[[nodiscard]] IR::Reg Reg() const;
|
||||
[[nodiscard]] IR::Pred Pred() const;
|
||||
[[nodiscard]] IR::Attribute Attribute() const;
|
||||
[[nodiscard]] IR::Patch Patch() const;
|
||||
[[nodiscard]] bool U1() const;
|
||||
[[nodiscard]] u8 U8() const;
|
||||
[[nodiscard]] u16 U16() const;
|
||||
|
@ -67,6 +70,7 @@ private:
|
|||
IR::Reg reg;
|
||||
IR::Pred pred;
|
||||
IR::Attribute attribute;
|
||||
IR::Patch patch;
|
||||
bool imm_u1;
|
||||
u8 imm_u8;
|
||||
u16 imm_u16;
|
||||
|
|
|
@ -70,6 +70,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
|||
program.stage = env.ShaderStage();
|
||||
program.local_memory_size = env.LocalMemorySize();
|
||||
switch (program.stage) {
|
||||
case Stage::TessellationControl: {
|
||||
const ProgramHeader& sph{env.SPH()};
|
||||
program.invocations = sph.common2.threads_per_input_primitive;
|
||||
break;
|
||||
}
|
||||
case Stage::Geometry: {
|
||||
const ProgramHeader& sph{env.SPH()};
|
||||
program.output_topology = sph.common3.output_topology;
|
||||
|
|
|
@ -70,12 +70,6 @@ void TranslatorVisitor::ALD(u64 insn) {
|
|||
BitField<47, 2, Size> size;
|
||||
} const ald{insn};
|
||||
|
||||
if (ald.o != 0) {
|
||||
throw NotImplementedException("O");
|
||||
}
|
||||
if (ald.patch != 0) {
|
||||
throw NotImplementedException("P");
|
||||
}
|
||||
const u64 offset{ald.absolute_offset.Value()};
|
||||
if (offset % 4 != 0) {
|
||||
throw NotImplementedException("Unaligned absolute offset {}", offset);
|
||||
|
@ -84,11 +78,19 @@ void TranslatorVisitor::ALD(u64 insn) {
|
|||
const u32 num_elements{NumElements(ald.size)};
|
||||
if (ald.index_reg == IR::Reg::RZ) {
|
||||
for (u32 element = 0; element < num_elements; ++element) {
|
||||
const IR::Attribute attr{offset / 4 + element};
|
||||
F(ald.dest_reg + element, ir.GetAttribute(attr, vertex));
|
||||
if (ald.patch != 0) {
|
||||
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;
|
||||
}
|
||||
if (ald.patch != 0) {
|
||||
throw NotImplementedException("Indirect patch read");
|
||||
}
|
||||
HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
|
||||
F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex));
|
||||
});
|
||||
|
@ -106,9 +108,6 @@ void TranslatorVisitor::AST(u64 insn) {
|
|||
BitField<47, 2, Size> size;
|
||||
} const ast{insn};
|
||||
|
||||
if (ast.patch != 0) {
|
||||
throw NotImplementedException("P");
|
||||
}
|
||||
if (ast.index_reg != IR::Reg::RZ) {
|
||||
throw NotImplementedException("Indexed store");
|
||||
}
|
||||
|
@ -120,11 +119,19 @@ void TranslatorVisitor::AST(u64 insn) {
|
|||
const u32 num_elements{NumElements(ast.size)};
|
||||
if (ast.index_reg == IR::Reg::RZ) {
|
||||
for (u32 element = 0; element < num_elements; ++element) {
|
||||
const IR::Attribute attr{offset / 4 + element};
|
||||
ir.SetAttribute(attr, F(ast.src_reg + element), vertex);
|
||||
if (ast.patch != 0) {
|
||||
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;
|
||||
}
|
||||
if (ast.patch != 0) {
|
||||
throw NotImplementedException("Indexed tessellation patch store");
|
||||
}
|
||||
HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
|
||||
ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex);
|
||||
});
|
||||
|
|
|
@ -113,6 +113,8 @@ enum class SpecialRegister : u64 {
|
|||
|
||||
[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) {
|
||||
switch (special_register) {
|
||||
case SpecialRegister::SR_INVOCATION_ID:
|
||||
return ir.InvocationId();
|
||||
case SpecialRegister::SR_THREAD_KILL:
|
||||
return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
|
||||
case SpecialRegister::SR_INVOCATION_INFO:
|
||||
|
|
|
@ -53,6 +53,10 @@ void GetAttribute(Info& info, IR::Attribute attribute) {
|
|||
case IR::Attribute::PointSpriteT:
|
||||
info.loads_point_coord = true;
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
info.loads_tess_coord = true;
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::CompositeConstructF16x2:
|
||||
|
@ -350,6 +382,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
|||
case IR::Opcode::SetAttribute:
|
||||
SetAttribute(info, inst.Arg(0).Attribute());
|
||||
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:
|
||||
info.loads_indexed_attributes = true;
|
||||
break;
|
||||
|
@ -368,6 +406,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
|||
case IR::Opcode::LocalInvocationId:
|
||||
info.uses_local_invocation_id = true;
|
||||
break;
|
||||
case IR::Opcode::InvocationId:
|
||||
info.uses_invocation_id = true;
|
||||
break;
|
||||
case IR::Opcode::IsHelperInvocation:
|
||||
info.uses_is_helper_invocation = true;
|
||||
break;
|
||||
|
|
|
@ -38,6 +38,18 @@ enum class CompareFunction {
|
|||
Always,
|
||||
};
|
||||
|
||||
enum class TessPrimitive {
|
||||
Isolines,
|
||||
Triangles,
|
||||
Quads,
|
||||
};
|
||||
|
||||
enum class TessSpacing {
|
||||
Equal,
|
||||
FractionalOdd,
|
||||
FractionalEven,
|
||||
};
|
||||
|
||||
struct TransformFeedbackVarying {
|
||||
u32 buffer{};
|
||||
u32 stride{};
|
||||
|
@ -74,6 +86,10 @@ struct Profile {
|
|||
bool convert_depth_mode{};
|
||||
bool force_early_z{};
|
||||
|
||||
TessPrimitive tess_primitive{};
|
||||
TessSpacing tess_spacing{};
|
||||
bool tess_clockwise{};
|
||||
|
||||
InputTopology input_topology{};
|
||||
|
||||
std::optional<float> fixed_state_point_size;
|
||||
|
|
|
@ -101,8 +101,10 @@ struct Info {
|
|||
|
||||
bool uses_workgroup_id{};
|
||||
bool uses_local_invocation_id{};
|
||||
bool uses_invocation_id{};
|
||||
bool uses_is_helper_invocation{};
|
||||
bool uses_subgroup_invocation_id{};
|
||||
std::array<bool, 30> uses_patches{};
|
||||
|
||||
std::array<InputVarying, 32> input_generics{};
|
||||
bool loads_position{};
|
||||
|
@ -110,6 +112,7 @@ struct Info {
|
|||
bool loads_vertex_id{};
|
||||
bool loads_front_face{};
|
||||
bool loads_point_coord{};
|
||||
bool loads_tess_coord{};
|
||||
bool loads_indexed_attributes{};
|
||||
|
||||
std::array<bool, 8> stores_frag_color{};
|
||||
|
@ -120,6 +123,8 @@ struct Info {
|
|||
bool stores_clip_distance{};
|
||||
bool stores_layer{};
|
||||
bool stores_viewport_index{};
|
||||
bool stores_tess_level_outer{};
|
||||
bool stores_tess_level_inner{};
|
||||
bool stores_indexed_attributes{};
|
||||
|
||||
bool uses_fp16{};
|
||||
|
|
|
@ -685,6 +685,19 @@ VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face) {
|
|||
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) {
|
||||
switch (swizzle) {
|
||||
case Tegra::Texture::SwizzleSource::Zero:
|
||||
|
|
|
@ -65,6 +65,8 @@ VkFrontFace FrontFace(Maxwell::FrontFace front_face);
|
|||
|
||||
VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face);
|
||||
|
||||
VkPolygonMode PolygonMode(Maxwell::PolygonMode polygon_mode);
|
||||
|
||||
VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
|
||||
|
||||
VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle);
|
||||
|
|
|
@ -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),
|
||||
.rasterizerDiscardEnable =
|
||||
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>(
|
||||
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
|
||||
.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
|
||||
|
|
|
@ -1040,6 +1040,36 @@ Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key,
|
|||
std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(),
|
||||
&CastAttributeType);
|
||||
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:
|
||||
if (program.output_topology == Shader::OutputTopology::PointList) {
|
||||
profile.fixed_state_point_size = point_size;
|
||||
|
|
|
@ -91,7 +91,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
|
|||
.flags = 0,
|
||||
.size = STREAM_BUFFER_SIZE,
|
||||
.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,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
|
|
|
@ -225,7 +225,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||
.drawIndirectFirstInstance = false,
|
||||
.depthClamp = true,
|
||||
.depthBiasClamp = true,
|
||||
.fillModeNonSolid = false,
|
||||
.fillModeNonSolid = true,
|
||||
.depthBounds = false,
|
||||
.wideLines = false,
|
||||
.largePoints = true,
|
||||
|
@ -670,6 +670,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
|
|||
std::make_pair(features.largePoints, "largePoints"),
|
||||
std::make_pair(features.multiViewport, "multiViewport"),
|
||||
std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
|
||||
std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"),
|
||||
std::make_pair(features.geometryShader, "geometryShader"),
|
||||
std::make_pair(features.tessellationShader, "tessellationShader"),
|
||||
std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
|
||||
|
|
Loading…
Reference in a new issue