transform/Spirv: Add EmitVertexPointSize logic

This is disabled by default and can be enabled via transform data. The
separate EmitVertexPointSize transform will be removed once Dawn
starts using the Spirv sanitizer to do this.

Bug: tint:753
Change-Id: I676c4cef5bc53f2dbf2330645faa4a0f2bfe11bd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49980
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price 2021-05-05 16:54:52 +00:00 committed by Commit Bot service account
parent 6c1cf6569e
commit 8c6808fa13
4 changed files with 216 additions and 10 deletions

View File

@ -20,6 +20,7 @@
namespace tint {
namespace transform {
/// DEPRECATED: This should now be performed using the SPIR-V sanitizer.
/// EmitVertexPointSize is a Transform that adds a PointSize builtin global
/// output variable to the program which is assigned 1.0 as the new first
/// statement for all vertex stage entry points.

View File

@ -26,13 +26,17 @@
#include "src/sem/struct.h"
#include "src/sem/variable.h"
TINT_INSTANTIATE_TYPEINFO(tint::transform::Spirv::Config);
namespace tint {
namespace transform {
Spirv::Spirv() = default;
Spirv::~Spirv() = default;
Output Spirv::Run(const Program* in, const DataMap&) {
Output Spirv::Run(const Program* in, const DataMap& data) {
auto* cfg = data.Get<Config>();
ProgramBuilder out;
CloneContext ctx(&out, in);
HandleEntryPointIOTypes(ctx);
@ -45,6 +49,9 @@ Output Spirv::Run(const Program* in, const DataMap&) {
CloneContext ctx2(&out2, &tmp);
HandleSampleMaskBuiltins(ctx2);
AddEmptyEntryPoint(ctx2);
if (cfg && cfg->emit_vertex_point_size) {
EmitVertexPointSize(ctx2);
}
ctx2.Clone();
return Output{Program(std::move(out2))};
@ -242,6 +249,29 @@ void Spirv::HandleSampleMaskBuiltins(CloneContext& ctx) const {
}
}
void Spirv::EmitVertexPointSize(CloneContext& ctx) const {
// No-op if there are no vertex stages in the module.
if (!ctx.src->AST().Functions().HasStage(ast::PipelineStage::kVertex)) {
return;
}
// Create a module-scope pointsize builtin output variable.
Symbol pointsize = ctx.dst->Symbols().New("tint_pointsize");
ctx.dst->Global(pointsize, ctx.dst->ty.f32(), ast::StorageClass::kOutput,
nullptr, {ctx.dst->Builtin(ast::Builtin::kPointSize)});
// Assign 1.0 to the global at the start of all vertex shader entry points.
ctx.ReplaceAll([&ctx, pointsize](ast::Function* func) -> ast::Function* {
if (func->pipeline_stage() != ast::PipelineStage::kVertex) {
return nullptr;
}
return CloneWithStatementsAtStart(&ctx, func,
{
ctx.dst->Assign(pointsize, 1.0f),
});
});
}
void Spirv::AddEmptyEntryPoint(CloneContext& ctx) const {
for (auto* func : ctx.src->AST().Functions()) {
if (func->IsEntryPoint()) {
@ -347,5 +377,12 @@ void Spirv::HoistToOutputVariables(CloneContext& ctx,
}
}
Spirv::Config::Config(bool emit_vertex_point_size)
: emit_vertex_point_size(emit_vertex_point_size) {}
Spirv::Config::Config(const Config&) = default;
Spirv::Config::~Config() = default;
Spirv::Config& Spirv::Config::operator=(const Config&) = default;
} // namespace transform
} // namespace tint

View File

@ -31,6 +31,27 @@ namespace transform {
/// undefined behavior.
class Spirv : public Transform {
public:
/// Configuration options for the transform.
struct Config : public Castable<Config, Data> {
/// Constructor
/// @param emit_vertex_point_size `true` to generate a PointSize builtin
explicit Config(bool emit_vertex_point_size = false);
/// Copy constructor.
Config(const Config&);
/// Destructor.
~Config() override;
/// Assignment operator.
/// @returns this Config
Config& operator=(const Config&);
/// Set to `true` to generate a PointSize builtin and have it set to 1.0
/// from all vertex shaders in the module.
bool emit_vertex_point_size;
};
/// Constructor
Spirv();
~Spirv() override;
@ -47,6 +68,9 @@ class Spirv : public Transform {
void HandleEntryPointIOTypes(CloneContext& ctx) const;
/// Change type of sample mask builtin variables to single element arrays.
void HandleSampleMaskBuiltins(CloneContext& ctx) const;
/// Add a PointSize builtin output to the module and set it to 1.0 from all
/// vertex stage entry points.
void EmitVertexPointSize(CloneContext& ctx) const;
/// Add an empty shader entry point if none exist in the module.
void AddEmptyEntryPoint(CloneContext& ctx) const;

View File

@ -566,6 +566,128 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(SpirvTest, EmitVertexPointSize_Basic) {
auto* src = R"(
fn non_entry_point() {
}
[[stage(vertex)]]
fn main() -> [[builtin(position)]] vec4<f32> {
non_entry_point();
return vec4<f32>();
}
)";
auto* expect = R"(
[[builtin(pointsize)]] var<out> tint_pointsize : f32;
fn non_entry_point() {
}
[[builtin(position)]] var<out> tint_symbol_1 : vec4<f32>;
fn tint_symbol_2(tint_symbol : vec4<f32>) {
tint_symbol_1 = tint_symbol;
}
[[stage(vertex)]]
fn main() {
tint_pointsize = 1.0;
non_entry_point();
tint_symbol_2(vec4<f32>());
return;
}
)";
DataMap data;
data.Add<Spirv::Config>(true);
auto got = Run<Spirv>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SpirvTest, EmitVertexPointSize_MultipleVertexShaders) {
auto* src = R"(
[[stage(vertex)]]
fn main1() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>();
}
[[stage(vertex)]]
fn main2() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>();
}
[[stage(vertex)]]
fn main3() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = R"(
[[builtin(pointsize)]] var<out> tint_pointsize : f32;
[[builtin(position)]] var<out> tint_symbol_1 : vec4<f32>;
fn tint_symbol_2(tint_symbol : vec4<f32>) {
tint_symbol_1 = tint_symbol;
}
[[stage(vertex)]]
fn main1() {
tint_pointsize = 1.0;
tint_symbol_2(vec4<f32>());
return;
}
[[builtin(position)]] var<out> tint_symbol_4 : vec4<f32>;
fn tint_symbol_5(tint_symbol_3 : vec4<f32>) {
tint_symbol_4 = tint_symbol_3;
}
[[stage(vertex)]]
fn main2() {
tint_pointsize = 1.0;
tint_symbol_5(vec4<f32>());
return;
}
[[builtin(position)]] var<out> tint_symbol_7 : vec4<f32>;
fn tint_symbol_8(tint_symbol_6 : vec4<f32>) {
tint_symbol_7 = tint_symbol_6;
}
[[stage(vertex)]]
fn main3() {
tint_pointsize = 1.0;
tint_symbol_8(vec4<f32>());
return;
}
)";
DataMap data;
data.Add<Spirv::Config>(true);
auto got = Run<Spirv>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SpirvTest, EmitVertexPointSize_NoVertexShaders) {
auto* src = R"(
[[stage(compute)]]
fn main() {
}
)";
DataMap data;
data.Add<Spirv::Config>(true);
auto got = Run<Spirv>(src, data);
EXPECT_EQ(src, str(got));
}
TEST_F(SpirvTest, AddEmptyEntryPoint) {
auto* src = R"()";
@ -583,8 +705,13 @@ fn _tint_unused_entry_point() {
// Test that different transforms within the sanitizer interact correctly.
TEST_F(SpirvTest, MultipleTransforms) {
auto* src = R"(
[[stage(vertex)]]
fn vert_main() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>();
}
[[stage(fragment)]]
fn main([[builtin(sample_index)]] sample_index : u32,
fn frag_main([[builtin(sample_index)]] sample_index : u32,
[[builtin(sample_mask)]] mask_in : u32)
-> [[builtin(sample_mask)]] u32 {
return mask_in;
@ -592,24 +719,41 @@ fn main([[builtin(sample_index)]] sample_index : u32,
)";
auto* expect = R"(
[[builtin(sample_index)]] var<in> tint_symbol : u32;
[[builtin(pointsize)]] var<out> tint_pointsize : f32;
[[builtin(sample_mask)]] var<in> tint_symbol_1 : array<u32, 1>;
[[builtin(position)]] var<out> tint_symbol_1 : vec4<f32>;
[[builtin(sample_mask)]] var<out> tint_symbol_3 : array<u32, 1>;
fn tint_symbol_2(tint_symbol : vec4<f32>) {
tint_symbol_1 = tint_symbol;
}
fn tint_symbol_4(tint_symbol_2 : u32) {
tint_symbol_3[0] = tint_symbol_2;
[[stage(vertex)]]
fn vert_main() {
tint_pointsize = 1.0;
tint_symbol_2(vec4<f32>());
return;
}
[[builtin(sample_index)]] var<in> tint_symbol_3 : u32;
[[builtin(sample_mask)]] var<in> tint_symbol_4 : array<u32, 1>;
[[builtin(sample_mask)]] var<out> tint_symbol_6 : array<u32, 1>;
fn tint_symbol_7(tint_symbol_5 : u32) {
tint_symbol_6[0] = tint_symbol_5;
}
[[stage(fragment)]]
fn main() {
tint_symbol_4(tint_symbol_1[0]);
fn frag_main() {
tint_symbol_7(tint_symbol_4[0]);
return;
}
)";
auto got = Run<Spirv>(src);
DataMap data;
data.Add<Spirv::Config>(true);
auto got = Run<Spirv>(src, data);
EXPECT_EQ(expect, str(got));
}