glsl: Implement tessellation shaders

This commit is contained in:
ameerj 2021-05-31 23:07:13 -04:00
parent c7d085b505
commit 8c684b3e23
5 changed files with 146 additions and 27 deletions

View File

@ -21,10 +21,21 @@ std::string_view InterpDecorator(Interpolation interp) {
throw InvalidArgument("Invalid interpolation {}", interp); throw InvalidArgument("Invalid interpolation {}", interp);
} }
std::string_view ArrayDecorator(Stage stage) { std::string_view InputArrayDecorator(Stage stage) {
switch (stage) { switch (stage) {
case Stage::Geometry: case Stage::Geometry:
return "[1]"; case Stage::TessellationControl:
case Stage::TessellationEval:
return "[]";
default:
return "";
}
}
std::string OutputDecorator(Stage stage, u32 size) {
switch (stage) {
case Stage::TessellationControl:
return fmt::format("[{}]", size);
default: default:
return ""; return "";
} }
@ -73,6 +84,30 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
} }
} }
std::string_view GetTessMode(TessPrimitive primitive) {
switch (primitive) {
case TessPrimitive::Triangles:
return "triangles";
case TessPrimitive::Quads:
return "quads";
case TessPrimitive::Isolines:
return "isolines";
}
throw InvalidArgument("Invalid tessellation primitive {}", primitive);
}
std::string_view GetTessSpacing(TessSpacing spacing) {
switch (spacing) {
case TessSpacing::Equal:
return "equal_spacing";
case TessSpacing::FractionalOdd:
return "fractional_odd_spacing";
case TessSpacing::FractionalEven:
return "fractional_even_spacing";
}
throw InvalidArgument("Invalid tessellation spacing {}", spacing);
}
std::string_view InputPrimitive(InputTopology topology) { std::string_view InputPrimitive(InputTopology topology) {
switch (topology) { switch (topology) {
case InputTopology::Points: case InputTopology::Points:
@ -100,6 +135,23 @@ std::string_view OutputPrimitive(OutputTopology topology) {
} }
throw InvalidArgument("Invalid output topology {}", topology); throw InvalidArgument("Invalid output topology {}", topology);
} }
void SetupOutPerVertex(Stage stage, const Info& info, std::string& header) {
if (stage != Stage::VertexA && stage != Stage::VertexB && stage != Stage::Geometry) {
return;
}
header += "out gl_PerVertex{";
if (info.stores_position) {
header += "vec4 gl_Position;";
}
if (info.stores_point_size) {
header += "float gl_PointSize;";
}
if (info.stores_clip_distance) {
header += "float gl_ClipDistance[];";
}
header += "};";
}
} // namespace } // namespace
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
@ -111,13 +163,16 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
case Stage::VertexA: case Stage::VertexA:
case Stage::VertexB: case Stage::VertexB:
stage_name = "vs"; stage_name = "vs";
// TODO: add only what's used by the shader
header +=
"out gl_PerVertex {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];};";
break; break;
case Stage::TessellationControl: case Stage::TessellationControl:
stage_name = "tsc";
header += fmt::format("layout(vertices={})out;\n", program.invocations);
break;
case Stage::TessellationEval: case Stage::TessellationEval:
stage_name = "ts"; stage_name = "tse";
header += fmt::format("layout({},{},{})in;\n", GetTessMode(runtime_info.tess_primitive),
GetTessSpacing(runtime_info.tess_spacing),
runtime_info.tess_clockwise ? "cw" : "ccw");
break; break;
case Stage::Geometry: case Stage::Geometry:
stage_name = "gs"; stage_name = "gs";
@ -135,12 +190,23 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
program.workgroup_size[2]); program.workgroup_size[2]);
break; break;
} }
SetupOutPerVertex(stage, info, header);
for (size_t index = 0; index < info.input_generics.size(); ++index) { for (size_t index = 0; index < info.input_generics.size(); ++index) {
const auto& generic{info.input_generics[index]}; const auto& generic{info.input_generics[index]};
if (generic.used) { if (generic.used) {
header += header += fmt::format("layout(location={}){} in vec4 in_attr{}{};", index,
fmt::format("layout(location={}){} in vec4 in_attr{}{};", index, InterpDecorator(generic.interpolation), index,
InterpDecorator(generic.interpolation), index, ArrayDecorator(stage)); InputArrayDecorator(stage));
}
}
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
if (!info.uses_patches[index]) {
continue;
}
if (stage == Stage::TessellationControl) {
header += fmt::format("layout(location={})patch out vec4 patch{};", index, index);
} else {
header += fmt::format("layout(location={})patch in vec4 patch{};", index, index);
} }
} }
for (size_t index = 0; index < info.stores_frag_color.size(); ++index) { for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
@ -151,8 +217,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
} }
for (size_t index = 0; index < info.stores_generics.size(); ++index) { for (size_t index = 0; index < info.stores_generics.size(); ++index) {
// TODO: Properly resolve attribute issues // TODO: Properly resolve attribute issues
const auto declaration{ const auto declaration{fmt::format("layout(location={}) out vec4 out_attr{}{};", index,
fmt::format("layout(location={}) out vec4 out_attr{};", index, index)}; index, OutputDecorator(stage, program.invocations))};
if (info.stores_generics[index] || stage == Stage::VertexA || stage == Stage::VertexB) { if (info.stores_generics[index] || stage == Stage::VertexA || stage == Stage::VertexB) {
header += declaration; header += declaration;
} }

View File

@ -25,9 +25,24 @@ bool IsInputArray(Stage stage) {
stage == Stage::TessellationEval; stage == Stage::TessellationEval;
} }
std::string VertexIndex(EmitContext& ctx, std::string_view vertex) { std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : ""; return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
} }
bool IsOutputArray(Stage stage) {
return stage == Stage::Geometry || stage == Stage::TessellationControl;
}
std::string OutputVertexIndex(EmitContext& ctx, std::string_view vertex) {
switch (ctx.stage) {
case Stage::Geometry:
return fmt::format("[{}]", vertex);
case Stage::TessellationControl:
return "[gl_InvocationID]";
default:
return "";
}
}
} // namespace } // namespace
void EmitGetCbufU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, void EmitGetCbufU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
@ -132,12 +147,12 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
} }
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
[[maybe_unused]] std::string_view vertex) { std::string_view vertex) {
const u32 element{static_cast<u32>(attr) % 4}; const u32 element{static_cast<u32>(attr) % 4};
const char swizzle{"xyzw"[element]}; const char swizzle{"xyzw"[element]};
if (IR::IsGeneric(attr)) { if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, VertexIndex(ctx, vertex), swizzle); ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
return; return;
} }
switch (attr) { switch (attr) {
@ -150,6 +165,10 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
case Stage::VertexB: case Stage::VertexB:
ctx.AddF32("{}=gl_Position.{};", inst, swizzle); ctx.AddF32("{}=gl_Position.{};", inst, swizzle);
break; break;
case Stage::TessellationEval:
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
break;
case Stage::TessellationControl:
case Stage::Geometry: case Stage::Geometry:
ctx.AddF32("{}=gl_in[{}].gl_Position.{};", inst, vertex, swizzle); ctx.AddF32("{}=gl_in[{}].gl_Position.{};", inst, vertex, swizzle);
break; break;
@ -173,6 +192,10 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
case IR::Attribute::FrontFace: case IR::Attribute::FrontFace:
ctx.AddF32("{}=intBitsToFloat(gl_FrontFacing?-1:0);", inst); ctx.AddF32("{}=intBitsToFloat(gl_FrontFacing?-1:0);", inst);
break; break;
case IR::Attribute::TessellationEvaluationPointU:
case IR::Attribute::TessellationEvaluationPointV:
ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
break;
default: default:
fmt::print("Get attribute {}", attr); fmt::print("Get attribute {}", attr);
throw NotImplementedException("Get attribute {}", attr); throw NotImplementedException("Get attribute {}", attr);
@ -185,7 +208,7 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
const char swizzle{"xyzw"[element]}; const char swizzle{"xyzw"[element]};
if (IR::IsGeneric(attr)) { if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
ctx.Add("out_attr{}.{}={};", index, swizzle, value); ctx.Add("out_attr{}{}.{}={};", index, OutputVertexIndex(ctx, vertex), swizzle, value);
return; return;
} }
switch (attr) { switch (attr) {
@ -219,6 +242,44 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
} }
} }
void EmitGetPatch([[maybe_unused]] EmitContext& ctx, IR::Inst& inst,
[[maybe_unused]] IR::Patch patch) {
if (!IR::IsGeneric(patch)) {
throw NotImplementedException("Non-generic patch load");
}
const u32 index{IR::GenericPatchIndex(patch)};
const u32 element{IR::GenericPatchElement(patch)};
const char swizzle{"xyzw"[element]};
ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
}
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
if (IR::IsGeneric(patch)) {
const u32 index{IR::GenericPatchIndex(patch)};
const u32 element{IR::GenericPatchElement(patch)};
ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
return;
}
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)};
ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
break;
}
case IR::Patch::TessellationLodInteriorU:
ctx.Add("gl_TessLevelInner[0]={};", value);
break;
case IR::Patch::TessellationLodInteriorV:
ctx.Add("gl_TessLevelInner[1]={};", value);
break;
default:
throw NotImplementedException("Patch {}", patch);
}
}
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) { void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
const char swizzle{"xyzw"[component]}; const char swizzle{"xyzw"[component]};
ctx.Add("frag_color{}.{}={};", index, swizzle, value); ctx.Add("frag_color{}.{}={};", index, swizzle, value);

View File

@ -161,7 +161,7 @@ void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
[[maybe_unused]] std::string_view value) { [[maybe_unused]] std::string_view value) {
ctx.AddF32("{}=(1.0f)/sqrt({});", inst, value); ctx.AddF32("{}=inversesqrt({});", inst, value);
} }
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,

View File

@ -75,7 +75,7 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
void EmitGetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view vertex); void EmitGetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view vertex);
void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view value, void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view value,
std::string_view vertex); std::string_view vertex);
void EmitGetPatch(EmitContext& ctx, IR::Patch patch); void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value); void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value);
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value); void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value);
void EmitSetSampleMask(EmitContext& ctx, std::string_view value); void EmitSetSampleMask(EmitContext& ctx, std::string_view value);

View File

@ -151,14 +151,6 @@ void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::str
NotImplemented(); NotImplemented();
} }
void EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
NotImplemented();
}
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
NotImplemented();
}
void EmitSetSampleMask(EmitContext& ctx, std::string_view value) { void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
NotImplemented(); NotImplemented();
} }
@ -204,7 +196,7 @@ void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
} }
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
NotImplemented(); ctx.AddU32("{}=uint(gl_InvocationID);", inst);
} }
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {