mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
shader: Implement tessellation shaders, polygon mode and invocation id
This commit is contained in:
parent
34519d3fc6
commit
183855e396
28 changed files with 605 additions and 91 deletions
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
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)};
|
const u32 num_vertices{NumVertices(ctx.profile.input_topology)};
|
||||||
type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices));
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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
|
||||||
|
|
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,
|
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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
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};
|
const IR::Attribute attr{offset / 4 + element};
|
||||||
F(ald.dest_reg + element, ir.GetAttribute(attr, vertex));
|
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) {
|
||||||
|
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};
|
const IR::Attribute attr{offset / 4 + element};
|
||||||
ir.SetAttribute(attr, F(ast.src_reg + element), vertex);
|
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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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{};
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in a new issue