mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-16 19:43:31 +00:00
We don't want the WGSL parser to have to maintain type lookups. If the WGSL language is updated to allow module-scope variables to be declared in any order, then the single-pass approach is going to fail horribly. Instead do the check in the Resovler. With this change, the AST nodes actually contain the correctly declared storage class. Fix up the SPIR-V reader to generate StorageClass::kNone for handle types. Fix all tests. Bug: tint:724 Change-Id: I102e30c9bbef32de40e123c2676ea9a281dee74d Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50306 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Reviewed-by: James Price <jrprice@google.com>
431 lines
15 KiB
C++
431 lines
15 KiB
C++
// Copyright 2020 The Tint Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "src/reader/spirv/enum_converter.h"
|
|
|
|
#include <string>
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
namespace tint {
|
|
namespace reader {
|
|
namespace spirv {
|
|
namespace {
|
|
|
|
// Pipeline stage
|
|
|
|
struct PipelineStageCase {
|
|
SpvExecutionModel model;
|
|
bool expect_success;
|
|
ast::PipelineStage expected;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& out, PipelineStageCase psc) {
|
|
out << "PipelineStageCase{ SpvExecutionModel:" << int(psc.model)
|
|
<< " expect_success?:" << int(psc.expect_success)
|
|
<< " expected:" << int(psc.expected) << "}";
|
|
return out;
|
|
}
|
|
|
|
class SpvPipelineStageTest : public testing::TestWithParam<PipelineStageCase> {
|
|
public:
|
|
SpvPipelineStageTest()
|
|
: success_(true),
|
|
fail_stream_(&success_, &errors_),
|
|
converter_(fail_stream_) {}
|
|
|
|
std::string error() const { return errors_.str(); }
|
|
|
|
protected:
|
|
bool success_ = true;
|
|
std::stringstream errors_;
|
|
FailStream fail_stream_;
|
|
EnumConverter converter_;
|
|
};
|
|
|
|
TEST_P(SpvPipelineStageTest, Samples) {
|
|
const auto params = GetParam();
|
|
|
|
const auto result = converter_.ToPipelineStage(params.model);
|
|
EXPECT_EQ(success_, params.expect_success);
|
|
if (params.expect_success) {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_TRUE(error().empty());
|
|
} else {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_THAT(error(),
|
|
::testing::StartsWith("unknown SPIR-V execution model:"));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood,
|
|
SpvPipelineStageTest,
|
|
testing::Values(PipelineStageCase{SpvExecutionModelVertex, true,
|
|
ast::PipelineStage::kVertex},
|
|
PipelineStageCase{SpvExecutionModelFragment, true,
|
|
ast::PipelineStage::kFragment},
|
|
PipelineStageCase{SpvExecutionModelGLCompute, true,
|
|
ast::PipelineStage::kCompute}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterBad,
|
|
SpvPipelineStageTest,
|
|
testing::Values(PipelineStageCase{static_cast<SpvExecutionModel>(9999),
|
|
false, ast::PipelineStage::kNone},
|
|
PipelineStageCase{SpvExecutionModelTessellationControl,
|
|
false, ast::PipelineStage::kNone}));
|
|
|
|
// Storage class
|
|
|
|
struct StorageClassCase {
|
|
SpvStorageClass sc;
|
|
bool expect_success;
|
|
ast::StorageClass expected;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& out, StorageClassCase scc) {
|
|
out << "StorageClassCase{ SpvStorageClass:" << int(scc.sc)
|
|
<< " expect_success?:" << int(scc.expect_success)
|
|
<< " expected:" << int(scc.expected) << "}";
|
|
return out;
|
|
}
|
|
|
|
class SpvStorageClassTest : public testing::TestWithParam<StorageClassCase> {
|
|
public:
|
|
SpvStorageClassTest()
|
|
: success_(true),
|
|
fail_stream_(&success_, &errors_),
|
|
converter_(fail_stream_) {}
|
|
|
|
std::string error() const { return errors_.str(); }
|
|
|
|
protected:
|
|
bool success_ = true;
|
|
std::stringstream errors_;
|
|
FailStream fail_stream_;
|
|
EnumConverter converter_;
|
|
};
|
|
|
|
TEST_P(SpvStorageClassTest, Samples) {
|
|
const auto params = GetParam();
|
|
|
|
const auto result = converter_.ToStorageClass(params.sc);
|
|
EXPECT_EQ(success_, params.expect_success);
|
|
if (params.expect_success) {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_TRUE(error().empty());
|
|
} else {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_THAT(error(),
|
|
::testing::StartsWith("unknown SPIR-V storage class: "));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood,
|
|
SpvStorageClassTest,
|
|
testing::Values(
|
|
StorageClassCase{SpvStorageClassInput, true, ast::StorageClass::kInput},
|
|
StorageClassCase{SpvStorageClassOutput, true,
|
|
ast::StorageClass::kOutput},
|
|
StorageClassCase{SpvStorageClassUniform, true,
|
|
ast::StorageClass::kUniform},
|
|
StorageClassCase{SpvStorageClassWorkgroup, true,
|
|
ast::StorageClass::kWorkgroup},
|
|
StorageClassCase{SpvStorageClassUniformConstant, true,
|
|
ast::StorageClass::kNone},
|
|
StorageClassCase{SpvStorageClassStorageBuffer, true,
|
|
ast::StorageClass::kStorage},
|
|
StorageClassCase{SpvStorageClassImage, true, ast::StorageClass::kImage},
|
|
StorageClassCase{SpvStorageClassPrivate, true,
|
|
ast::StorageClass::kPrivate},
|
|
StorageClassCase{SpvStorageClassFunction, true,
|
|
ast::StorageClass::kFunction}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(EnumConverterBad,
|
|
SpvStorageClassTest,
|
|
testing::Values(StorageClassCase{
|
|
static_cast<SpvStorageClass>(9999), false,
|
|
ast::StorageClass::kInvalid}));
|
|
|
|
// Builtin
|
|
|
|
struct BuiltinCase {
|
|
SpvBuiltIn builtin;
|
|
bool expect_success;
|
|
ast::Builtin expected;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& out, BuiltinCase bc) {
|
|
out << "BuiltinCase{ SpvBuiltIn:" << int(bc.builtin)
|
|
<< " expect_success?:" << int(bc.expect_success)
|
|
<< " expected:" << int(bc.expected) << "}";
|
|
return out;
|
|
}
|
|
|
|
class SpvBuiltinTest : public testing::TestWithParam<BuiltinCase> {
|
|
public:
|
|
SpvBuiltinTest()
|
|
: success_(true),
|
|
fail_stream_(&success_, &errors_),
|
|
converter_(fail_stream_) {}
|
|
|
|
std::string error() const { return errors_.str(); }
|
|
|
|
protected:
|
|
bool success_ = true;
|
|
std::stringstream errors_;
|
|
FailStream fail_stream_;
|
|
EnumConverter converter_;
|
|
};
|
|
|
|
TEST_P(SpvBuiltinTest, Samples) {
|
|
const auto params = GetParam();
|
|
|
|
const auto result = converter_.ToBuiltin(params.builtin);
|
|
EXPECT_EQ(success_, params.expect_success);
|
|
if (params.expect_success) {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_TRUE(error().empty());
|
|
} else {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_THAT(error(), ::testing::StartsWith("unknown SPIR-V builtin: "));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood_Input,
|
|
SpvBuiltinTest,
|
|
testing::Values(
|
|
BuiltinCase{SpvBuiltInPosition, true, ast::Builtin::kPosition},
|
|
BuiltinCase{SpvBuiltInInstanceIndex, true,
|
|
ast::Builtin::kInstanceIndex},
|
|
BuiltinCase{SpvBuiltInFrontFacing, true, ast::Builtin::kFrontFacing},
|
|
BuiltinCase{SpvBuiltInFragCoord, true, ast::Builtin::kPosition},
|
|
BuiltinCase{SpvBuiltInLocalInvocationId, true,
|
|
ast::Builtin::kLocalInvocationId},
|
|
BuiltinCase{SpvBuiltInLocalInvocationIndex, true,
|
|
ast::Builtin::kLocalInvocationIndex},
|
|
BuiltinCase{SpvBuiltInGlobalInvocationId, true,
|
|
ast::Builtin::kGlobalInvocationId},
|
|
BuiltinCase{SpvBuiltInWorkgroupId, true, ast::Builtin::kWorkgroupId},
|
|
BuiltinCase{SpvBuiltInSampleId, true, ast::Builtin::kSampleIndex},
|
|
BuiltinCase{SpvBuiltInSampleMask, true, ast::Builtin::kSampleMask}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood_Output,
|
|
SpvBuiltinTest,
|
|
testing::Values(
|
|
BuiltinCase{SpvBuiltInPosition, true, ast::Builtin::kPosition},
|
|
BuiltinCase{SpvBuiltInFragDepth, true, ast::Builtin::kFragDepth},
|
|
BuiltinCase{SpvBuiltInSampleMask, true, ast::Builtin::kSampleMask}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterBad,
|
|
SpvBuiltinTest,
|
|
testing::Values(
|
|
BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::Builtin::kNone},
|
|
BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::Builtin::kNone},
|
|
BuiltinCase{SpvBuiltInNumWorkgroups, false, ast::Builtin::kNone}));
|
|
|
|
// Dim
|
|
|
|
struct DimCase {
|
|
SpvDim dim;
|
|
bool arrayed;
|
|
bool expect_success;
|
|
ast::TextureDimension expected;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& out, DimCase dc) {
|
|
out << "DimCase{ SpvDim:" << int(dc.dim) << " arrayed?:" << int(dc.arrayed)
|
|
<< " expect_success?:" << int(dc.expect_success)
|
|
<< " expected:" << int(dc.expected) << "}";
|
|
return out;
|
|
}
|
|
|
|
class SpvDimTest : public testing::TestWithParam<DimCase> {
|
|
public:
|
|
SpvDimTest()
|
|
: success_(true),
|
|
fail_stream_(&success_, &errors_),
|
|
converter_(fail_stream_) {}
|
|
|
|
std::string error() const { return errors_.str(); }
|
|
|
|
protected:
|
|
bool success_ = true;
|
|
std::stringstream errors_;
|
|
FailStream fail_stream_;
|
|
EnumConverter converter_;
|
|
};
|
|
|
|
TEST_P(SpvDimTest, Samples) {
|
|
const auto params = GetParam();
|
|
|
|
const auto result = converter_.ToDim(params.dim, params.arrayed);
|
|
EXPECT_EQ(success_, params.expect_success);
|
|
if (params.expect_success) {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_TRUE(error().empty());
|
|
} else {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_THAT(error(), ::testing::HasSubstr("dimension"));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood,
|
|
SpvDimTest,
|
|
testing::Values(
|
|
// Non-arrayed
|
|
DimCase{SpvDim1D, false, true, ast::TextureDimension::k1d},
|
|
DimCase{SpvDim2D, false, true, ast::TextureDimension::k2d},
|
|
DimCase{SpvDim3D, false, true, ast::TextureDimension::k3d},
|
|
DimCase{SpvDimCube, false, true, ast::TextureDimension::kCube},
|
|
// Arrayed
|
|
DimCase{SpvDim2D, true, true, ast::TextureDimension::k2dArray},
|
|
DimCase{SpvDimCube, true, true, ast::TextureDimension::kCubeArray}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterBad,
|
|
SpvDimTest,
|
|
testing::Values(
|
|
// Invalid SPIR-V dimensionality.
|
|
DimCase{SpvDimMax, false, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimMax, true, false, ast::TextureDimension::kNone},
|
|
// Vulkan non-arrayed dimensionalities not supported by WGSL.
|
|
DimCase{SpvDimRect, false, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimBuffer, false, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimSubpassData, false, false, ast::TextureDimension::kNone},
|
|
// Arrayed dimensionalities not supported by WGSL
|
|
DimCase{SpvDim3D, true, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimRect, true, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimBuffer, true, false, ast::TextureDimension::kNone},
|
|
DimCase{SpvDimSubpassData, true, false, ast::TextureDimension::kNone}));
|
|
|
|
// ImageFormat
|
|
|
|
struct ImageFormatCase {
|
|
SpvImageFormat format;
|
|
bool expect_success;
|
|
ast::ImageFormat expected;
|
|
};
|
|
inline std::ostream& operator<<(std::ostream& out, ImageFormatCase ifc) {
|
|
out << "ImageFormatCase{ SpvImageFormat:" << int(ifc.format)
|
|
<< " expect_success?:" << int(ifc.expect_success)
|
|
<< " expected:" << int(ifc.expected) << "}";
|
|
return out;
|
|
}
|
|
|
|
class SpvImageFormatTest : public testing::TestWithParam<ImageFormatCase> {
|
|
public:
|
|
SpvImageFormatTest()
|
|
: success_(true),
|
|
fail_stream_(&success_, &errors_),
|
|
converter_(fail_stream_) {}
|
|
|
|
std::string error() const { return errors_.str(); }
|
|
|
|
protected:
|
|
bool success_ = true;
|
|
std::stringstream errors_;
|
|
FailStream fail_stream_;
|
|
EnumConverter converter_;
|
|
};
|
|
|
|
TEST_P(SpvImageFormatTest, Samples) {
|
|
const auto params = GetParam();
|
|
|
|
const auto result = converter_.ToImageFormat(params.format);
|
|
EXPECT_EQ(success_, params.expect_success) << params;
|
|
if (params.expect_success) {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_TRUE(error().empty());
|
|
} else {
|
|
EXPECT_EQ(result, params.expected);
|
|
EXPECT_THAT(error(), ::testing::StartsWith("invalid image format: "));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterGood,
|
|
SpvImageFormatTest,
|
|
testing::Values(
|
|
// Unknown. This is used for sampled images.
|
|
ImageFormatCase{SpvImageFormatUnknown, true, ast::ImageFormat::kNone},
|
|
// 8 bit channels
|
|
ImageFormatCase{SpvImageFormatRgba8, true,
|
|
ast::ImageFormat::kRgba8Unorm},
|
|
ImageFormatCase{SpvImageFormatRgba8Snorm, true,
|
|
ast::ImageFormat::kRgba8Snorm},
|
|
ImageFormatCase{SpvImageFormatRgba8ui, true,
|
|
ast::ImageFormat::kRgba8Uint},
|
|
ImageFormatCase{SpvImageFormatRgba8i, true,
|
|
ast::ImageFormat::kRgba8Sint},
|
|
// 16 bit channels
|
|
ImageFormatCase{SpvImageFormatRgba16ui, true,
|
|
ast::ImageFormat::kRgba16Uint},
|
|
ImageFormatCase{SpvImageFormatRgba16i, true,
|
|
ast::ImageFormat::kRgba16Sint},
|
|
ImageFormatCase{SpvImageFormatRgba16f, true,
|
|
ast::ImageFormat::kRgba16Float},
|
|
// 32 bit channels
|
|
// ... 1 channel
|
|
ImageFormatCase{SpvImageFormatR32ui, true, ast::ImageFormat::kR32Uint},
|
|
ImageFormatCase{SpvImageFormatR32i, true, ast::ImageFormat::kR32Sint},
|
|
ImageFormatCase{SpvImageFormatR32f, true, ast::ImageFormat::kR32Float},
|
|
// ... 2 channels
|
|
ImageFormatCase{SpvImageFormatRg32ui, true,
|
|
ast::ImageFormat::kRg32Uint},
|
|
ImageFormatCase{SpvImageFormatRg32i, true, ast::ImageFormat::kRg32Sint},
|
|
ImageFormatCase{SpvImageFormatRg32f, true,
|
|
ast::ImageFormat::kRg32Float},
|
|
// ... 4 channels
|
|
ImageFormatCase{SpvImageFormatRgba32ui, true,
|
|
ast::ImageFormat::kRgba32Uint},
|
|
ImageFormatCase{SpvImageFormatRgba32i, true,
|
|
ast::ImageFormat::kRgba32Sint},
|
|
ImageFormatCase{SpvImageFormatRgba32f, true,
|
|
ast::ImageFormat::kRgba32Float}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
EnumConverterBad,
|
|
SpvImageFormatTest,
|
|
testing::Values(
|
|
// Scanning in order from the SPIR-V spec.
|
|
ImageFormatCase{SpvImageFormatRg16f, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatR11fG11fB10f, false,
|
|
ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatR16f, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRgb10A2, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg16, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg8, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatR16, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatR8, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRgba16Snorm, false,
|
|
ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg16Snorm, false,
|
|
ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg8Snorm, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg16i, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg8i, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatR8i, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRgb10a2ui, false,
|
|
ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg16ui, false, ast::ImageFormat::kNone},
|
|
ImageFormatCase{SpvImageFormatRg8ui, false, ast::ImageFormat::kNone}));
|
|
|
|
} // namespace
|
|
} // namespace spirv
|
|
} // namespace reader
|
|
} // namespace tint
|