mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-21 02:39:11 +00:00
tint: Add bgra8unorm storage texture support
Polyfill this for the SPIR-V, HLSL and GLSL backends by replacing bgra8unorm with rgba8unorm, and swizzling. Bug: tint:1804 Change-Id: I36638202840d7313001dff6c5b60dcb948988c34 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117204 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
committed by
Dawn LUCI CQ
parent
e2be18a7fd
commit
d03dceebf3
@@ -833,160 +833,206 @@ Transform::ApplyResult BuiltinPolyfill::Apply(const Program* src,
|
||||
|
||||
bool made_changes = false;
|
||||
for (auto* node : src->ASTNodes().Objects()) {
|
||||
auto* expr = src->Sem().Get<sem::Expression>(node);
|
||||
if (!expr || expr->Stage() == sem::EvaluationStage::kConstant ||
|
||||
expr->Stage() == sem::EvaluationStage::kNotEvaluated) {
|
||||
continue; // Don't polyfill @const expressions
|
||||
}
|
||||
|
||||
if (auto* call = expr->As<sem::Call>()) {
|
||||
auto* builtin = call->Target()->As<sem::Builtin>();
|
||||
if (!builtin) {
|
||||
continue;
|
||||
}
|
||||
Symbol fn;
|
||||
switch (builtin->Type()) {
|
||||
case sem::BuiltinType::kAcosh:
|
||||
if (polyfill.acosh != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.acosh(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kAsinh:
|
||||
if (polyfill.asinh) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.asinh(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kAtanh:
|
||||
if (polyfill.atanh != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.atanh(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kClamp:
|
||||
if (polyfill.clamp_int) {
|
||||
auto& sig = builtin->Signature();
|
||||
if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
|
||||
Switch(
|
||||
node,
|
||||
[&](const ast::CallExpression* expr) {
|
||||
auto* call = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
|
||||
if (!call || call->Stage() == sem::EvaluationStage::kConstant ||
|
||||
call->Stage() == sem::EvaluationStage::kNotEvaluated) {
|
||||
return; // Don't polyfill @const expressions
|
||||
}
|
||||
auto* builtin = call->Target()->As<sem::Builtin>();
|
||||
if (!builtin) {
|
||||
return;
|
||||
}
|
||||
Symbol fn;
|
||||
switch (builtin->Type()) {
|
||||
case sem::BuiltinType::kAcosh:
|
||||
if (polyfill.acosh != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
|
||||
builtin, [&] { return s.acosh(builtin->ReturnType()); });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kCountLeadingZeros:
|
||||
if (polyfill.count_leading_zeros) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.countLeadingZeros(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kCountTrailingZeros:
|
||||
if (polyfill.count_trailing_zeros) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.countTrailingZeros(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kExtractBits:
|
||||
if (polyfill.extract_bits != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.extractBits(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kFirstLeadingBit:
|
||||
if (polyfill.first_leading_bit) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kFirstTrailingBit:
|
||||
if (polyfill.first_trailing_bit) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kInsertBits:
|
||||
if (polyfill.insert_bits != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.insertBits(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kSaturate:
|
||||
if (polyfill.saturate) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.saturate(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kSign:
|
||||
if (polyfill.sign_int) {
|
||||
auto* ty = builtin->ReturnType();
|
||||
if (ty->is_signed_integer_scalar_or_vector()) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin,
|
||||
[&] { return s.sign_int(ty); });
|
||||
break;
|
||||
case sem::BuiltinType::kAsinh:
|
||||
if (polyfill.asinh) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.asinh(builtin->ReturnType()); });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kTextureSampleBaseClampToEdge:
|
||||
if (polyfill.texture_sample_base_clamp_to_edge_2d_f32) {
|
||||
auto& sig = builtin->Signature();
|
||||
auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
|
||||
if (auto* stex = tex->Type()->As<type::SampledTexture>()) {
|
||||
if (stex->type()->Is<type::F32>()) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin, [&] {
|
||||
return s.textureSampleBaseClampToEdge_2d_f32();
|
||||
});
|
||||
break;
|
||||
case sem::BuiltinType::kAtanh:
|
||||
if (polyfill.atanh != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.atanh(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kClamp:
|
||||
if (polyfill.clamp_int) {
|
||||
auto& sig = builtin->Signature();
|
||||
if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kQuantizeToF16:
|
||||
if (polyfill.quantize_to_vec_f16) {
|
||||
if (auto* vec = builtin->ReturnType()->As<type::Vector>()) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.quantizeToF16(vec); });
|
||||
break;
|
||||
case sem::BuiltinType::kCountLeadingZeros:
|
||||
if (polyfill.count_leading_zeros) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin, [&] {
|
||||
return s.countLeadingZeros(builtin->ReturnType());
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case sem::BuiltinType::kCountTrailingZeros:
|
||||
if (polyfill.count_trailing_zeros) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin, [&] {
|
||||
return s.countTrailingZeros(builtin->ReturnType());
|
||||
});
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kExtractBits:
|
||||
if (polyfill.extract_bits != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.extractBits(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kFirstLeadingBit:
|
||||
if (polyfill.first_leading_bit) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kFirstTrailingBit:
|
||||
if (polyfill.first_trailing_bit) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kInsertBits:
|
||||
if (polyfill.insert_bits != Level::kNone) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.insertBits(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kSaturate:
|
||||
if (polyfill.saturate) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.saturate(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kSign:
|
||||
if (polyfill.sign_int) {
|
||||
auto* ty = builtin->ReturnType();
|
||||
if (ty->is_signed_integer_scalar_or_vector()) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin,
|
||||
[&] { return s.sign_int(ty); });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kTextureSampleBaseClampToEdge:
|
||||
if (polyfill.texture_sample_base_clamp_to_edge_2d_f32) {
|
||||
auto& sig = builtin->Signature();
|
||||
auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
|
||||
if (auto* stex = tex->Type()->As<type::SampledTexture>()) {
|
||||
if (stex->type()->Is<type::F32>()) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin, [&] {
|
||||
return s.textureSampleBaseClampToEdge_2d_f32();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kTextureStore:
|
||||
if (polyfill.bgra8unorm) {
|
||||
auto& sig = builtin->Signature();
|
||||
auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
|
||||
if (auto* stex = tex->Type()->As<type::StorageTexture>()) {
|
||||
if (stex->texel_format() == ast::TexelFormat::kBgra8Unorm) {
|
||||
size_t value_idx = static_cast<size_t>(
|
||||
sig.IndexOf(sem::ParameterUsage::kValue));
|
||||
ctx.Replace(expr, [&ctx, expr, value_idx] {
|
||||
utils::Vector<const ast::Expression*, 3> args;
|
||||
for (auto* arg : expr->args) {
|
||||
arg = ctx.Clone(arg);
|
||||
if (args.Length() == value_idx) { // value
|
||||
arg = ctx.dst->MemberAccessor(arg, "bgra");
|
||||
}
|
||||
args.Push(arg);
|
||||
}
|
||||
return ctx.dst->Call(
|
||||
utils::ToString(sem::BuiltinType::kTextureStore),
|
||||
std::move(args));
|
||||
});
|
||||
made_changes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kQuantizeToF16:
|
||||
if (polyfill.quantize_to_vec_f16) {
|
||||
if (auto* vec = builtin->ReturnType()->As<type::Vector>()) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.quantizeToF16(vec); });
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case sem::BuiltinType::kWorkgroupUniformLoad:
|
||||
if (polyfill.workgroup_uniform_load) {
|
||||
fn = builtin_polyfills.GetOrCreate(
|
||||
builtin, [&] { return s.workgroupUniformLoad(builtin->ReturnType()); });
|
||||
}
|
||||
break;
|
||||
case sem::BuiltinType::kWorkgroupUniformLoad:
|
||||
if (polyfill.workgroup_uniform_load) {
|
||||
fn = builtin_polyfills.GetOrCreate(builtin, [&] {
|
||||
return s.workgroupUniformLoad(builtin->ReturnType());
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fn.IsValid()) {
|
||||
auto* replacement = b.Call(fn, ctx.Clone(call->Declaration()->args));
|
||||
ctx.Replace(call->Declaration(), replacement);
|
||||
made_changes = true;
|
||||
}
|
||||
} else if (auto* bin_op = node->As<ast::BinaryExpression>()) {
|
||||
switch (bin_op->op) {
|
||||
case ast::BinaryOp::kShiftLeft:
|
||||
case ast::BinaryOp::kShiftRight: {
|
||||
if (polyfill.bitshift_modulo) {
|
||||
ctx.Replace(bin_op, [bin_op, &s] { return s.BitshiftModulo(bin_op); });
|
||||
made_changes = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case ast::BinaryOp::kDivide:
|
||||
case ast::BinaryOp::kModulo: {
|
||||
if (polyfill.int_div_mod) {
|
||||
auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
|
||||
if (lhs_ty->is_integer_scalar_or_vector()) {
|
||||
ctx.Replace(bin_op, [bin_op, &s] { return s.IntDivMod(bin_op); });
|
||||
|
||||
if (fn.IsValid()) {
|
||||
ctx.Replace(call->Declaration(), [&ctx, fn, expr] {
|
||||
return ctx.dst->Call(fn, ctx.Clone(expr->args));
|
||||
});
|
||||
made_changes = true;
|
||||
}
|
||||
},
|
||||
[&](const ast::BinaryExpression* bin_op) {
|
||||
if (auto* sem = src->Sem().Get(bin_op);
|
||||
!sem || sem->Stage() == sem::EvaluationStage::kConstant ||
|
||||
sem->Stage() == sem::EvaluationStage::kNotEvaluated) {
|
||||
return; // Don't polyfill @const expressions
|
||||
}
|
||||
switch (bin_op->op) {
|
||||
case ast::BinaryOp::kShiftLeft:
|
||||
case ast::BinaryOp::kShiftRight: {
|
||||
if (polyfill.bitshift_modulo) {
|
||||
ctx.Replace(bin_op, [bin_op, &s] { return s.BitshiftModulo(bin_op); });
|
||||
made_changes = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ast::BinaryOp::kDivide:
|
||||
case ast::BinaryOp::kModulo: {
|
||||
if (polyfill.int_div_mod) {
|
||||
auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
|
||||
if (lhs_ty->is_integer_scalar_or_vector()) {
|
||||
ctx.Replace(bin_op, [bin_op, &s] { return s.IntDivMod(bin_op); });
|
||||
made_changes = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[&](const ast::StorageTexture* tex) {
|
||||
if (polyfill.bgra8unorm && tex->format == ast::TexelFormat::kBgra8Unorm) {
|
||||
ctx.Replace(tex, [&ctx, tex] {
|
||||
return ctx.dst->ty.storage_texture(tex->dim, ast::TexelFormat::kRgba8Unorm,
|
||||
tex->access);
|
||||
});
|
||||
made_changes = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!made_changes) {
|
||||
|
||||
@@ -47,6 +47,8 @@ class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
|
||||
bool asinh = false;
|
||||
/// What level should `atanh` be polyfilled?
|
||||
Level atanh = Level::kNone;
|
||||
/// Should storage textures of format 'bgra8unorm' be replaced with 'rgba8unorm'?
|
||||
bool bgra8unorm = false;
|
||||
/// Should the RHS of `<<` and `>>` be wrapped in a modulo bit-width of LHS?
|
||||
bool bitshift_modulo = false;
|
||||
/// Should `clamp()` be polyfilled for integer values (scalar or vector)?
|
||||
|
||||
@@ -380,6 +380,138 @@ fn f() {
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// bgra8unorm
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DataMap polyfillBgra8unorm() {
|
||||
BuiltinPolyfill::Builtins builtins;
|
||||
builtins.bgra8unorm = true;
|
||||
DataMap data;
|
||||
data.Add<BuiltinPolyfill::Config>(builtins);
|
||||
return data;
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureVar) {
|
||||
auto* src = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
|
||||
)";
|
||||
|
||||
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
|
||||
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureParam) {
|
||||
auto* src = R"(
|
||||
fn f(tex : texture_storage_3d<bgra8unorm, write>) {
|
||||
}
|
||||
)";
|
||||
|
||||
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
|
||||
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureVar) {
|
||||
auto* src = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_3d<rgba8unorm, write>;
|
||||
)";
|
||||
|
||||
auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureParam) {
|
||||
auto* src = R"(
|
||||
fn f(tex : texture_storage_3d<bgra8unorm, write>) {
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f(tex : texture_storage_3d<rgba8unorm, write>) {
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore) {
|
||||
auto* src = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
|
||||
|
||||
fn f(coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, value);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
fn f(coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, value.bgra);
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_Param) {
|
||||
auto* src = R"(
|
||||
fn f(tex : texture_storage_2d<bgra8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, value);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f(tex : texture_storage_2d<rgba8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, value.bgra);
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_WithAtanh) {
|
||||
auto* src = R"(
|
||||
@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
|
||||
|
||||
fn f(coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, atanh(value));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn tint_atanh(x : vec4<f32>) -> vec4<f32> {
|
||||
return (log(((1 + x) / (1 - x))) * 0.5);
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
fn f(coords : vec2<i32>, value : vec4<f32>) {
|
||||
textureStore(tex, coords, tint_atanh(value).bgra);
|
||||
}
|
||||
)";
|
||||
|
||||
BuiltinPolyfill::Builtins builtins;
|
||||
builtins.atanh = BuiltinPolyfill::Level::kFull;
|
||||
builtins.bgra8unorm = true;
|
||||
DataMap data;
|
||||
data.Add<BuiltinPolyfill::Config>(builtins);
|
||||
|
||||
auto got = Run<BuiltinPolyfill>(src, std::move(data));
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// bitshiftModulo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user