Implement textureSample builtins

Handle wsgl parsing and spirv writing of:
  textureSample(), textureSampleBias(), textureSampleLevel(),
  textureSampleGrad(), textureSampleCompare()

Handle the different signature for array texture types.
Includes offset overloads.

Change-Id: I6802d97cd9a7083f12439b32725b9a4b666b8c63
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32985
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2020-11-18 21:19:22 +00:00 committed by Commit Bot service account
parent 2f4096b0d7
commit 3ea3c997b5
13 changed files with 3745 additions and 674 deletions

View File

@ -755,6 +755,8 @@ source_set("tint_unittests_core_src") {
"src/ast/function_test.cc", "src/ast/function_test.cc",
"src/ast/identifier_expression_test.cc", "src/ast/identifier_expression_test.cc",
"src/ast/if_statement_test.cc", "src/ast/if_statement_test.cc",
"src/ast/intrinsic_texture_helper_test.cc",
"src/ast/intrinsic_texture_helper_test.h",
"src/ast/int_literal_test.cc", "src/ast/int_literal_test.cc",
"src/ast/location_decoration_test.cc", "src/ast/location_decoration_test.cc",
"src/ast/loop_statement_test.cc", "src/ast/loop_statement_test.cc",

View File

@ -364,6 +364,8 @@ set(TINT_TEST_SRCS
ast/function_test.cc ast/function_test.cc
ast/identifier_expression_test.cc ast/identifier_expression_test.cc
ast/if_statement_test.cc ast/if_statement_test.cc
ast/intrinsic_texture_helper_test.cc
ast/intrinsic_texture_helper_test.h
ast/int_literal_test.cc ast/int_literal_test.cc
ast/location_decoration_test.cc ast/location_decoration_test.cc
ast/loop_statement_test.cc ast/loop_statement_test.cc

View File

@ -15,7 +15,9 @@
#ifndef SRC_AST_IDENTIFIER_EXPRESSION_H_ #ifndef SRC_AST_IDENTIFIER_EXPRESSION_H_
#define SRC_AST_IDENTIFIER_EXPRESSION_H_ #define SRC_AST_IDENTIFIER_EXPRESSION_H_
#include <memory>
#include <string> #include <string>
#include <utility>
#include "src/ast/expression.h" #include "src/ast/expression.h"
#include "src/ast/intrinsic.h" #include "src/ast/intrinsic.h"
@ -45,6 +47,17 @@ class IdentifierExpression : public Expression {
void set_intrinsic(Intrinsic i) { intrinsic_ = i; } void set_intrinsic(Intrinsic i) { intrinsic_ = i; }
/// @returns the intrinsic this identifier represents /// @returns the intrinsic this identifier represents
Intrinsic intrinsic() const { return intrinsic_; } Intrinsic intrinsic() const { return intrinsic_; }
/// Sets the intrinsic signature
/// @param s the intrinsic signature to set
void set_intrinsic_signature(std::unique_ptr<intrinsic::Signature> s) {
intrinsic_sig_ = std::move(s);
}
/// @returns the intrinsic signature for this identifier.
const intrinsic::Signature* intrinsic_signature() const {
return intrinsic_sig_.get();
}
/// @returns true if this identifier is for an intrinsic /// @returns true if this identifier is for an intrinsic
bool IsIntrinsic() const { return intrinsic_ != Intrinsic::kNone; } bool IsIntrinsic() const { return intrinsic_ != Intrinsic::kNone; }
@ -63,6 +76,7 @@ class IdentifierExpression : public Expression {
IdentifierExpression(const IdentifierExpression&) = delete; IdentifierExpression(const IdentifierExpression&) = delete;
Intrinsic intrinsic_ = Intrinsic::kNone; Intrinsic intrinsic_ = Intrinsic::kNone;
std::unique_ptr<intrinsic::Signature> intrinsic_sig_;
std::string name_; std::string name_;
}; };

View File

@ -216,6 +216,9 @@ std::ostream& operator<<(std::ostream& out, Intrinsic i) {
case Intrinsic::kTextureSampleCompare: case Intrinsic::kTextureSampleCompare:
out << "textureSampleCompare"; out << "textureSampleCompare";
break; break;
case Intrinsic::kTextureSampleGrad:
out << "textureSampleGrad";
break;
case Intrinsic::kTextureSampleLevel: case Intrinsic::kTextureSampleLevel:
out << "textureSampleLevel"; out << "textureSampleLevel";
break; break;
@ -231,6 +234,12 @@ std::ostream& operator<<(std::ostream& out, Intrinsic i) {
namespace intrinsic { namespace intrinsic {
Signature::~Signature() = default;
TextureSignature::~TextureSignature() = default;
TextureSignature::Parameters::Index::Index() = default;
TextureSignature::Parameters::Index::Index(const Index&) = default;
bool IsCoarseDerivative(ast::Intrinsic i) { bool IsCoarseDerivative(ast::Intrinsic i) {
return i == Intrinsic::kDpdxCoarse || i == Intrinsic::kDpdyCoarse || return i == Intrinsic::kDpdxCoarse || i == Intrinsic::kDpdyCoarse ||
i == Intrinsic::kFwidthCoarse; i == Intrinsic::kFwidthCoarse;
@ -256,7 +265,8 @@ bool IsTextureIntrinsic(ast::Intrinsic i) {
return i == Intrinsic::kTextureLoad || i == Intrinsic::kTextureSample || return i == Intrinsic::kTextureLoad || i == Intrinsic::kTextureSample ||
i == Intrinsic::kTextureSampleLevel || i == Intrinsic::kTextureSampleLevel ||
i == Intrinsic::kTextureSampleBias || i == Intrinsic::kTextureSampleBias ||
i == Intrinsic::kTextureSampleCompare; i == Intrinsic::kTextureSampleCompare ||
i == Intrinsic::kTextureSampleGrad;
} }
} // namespace intrinsic } // namespace intrinsic

View File

@ -89,6 +89,7 @@ enum class Intrinsic {
kTextureSample, kTextureSample,
kTextureSampleBias, kTextureSampleBias,
kTextureSampleCompare, kTextureSampleCompare,
kTextureSampleGrad,
kTextureSampleLevel, kTextureSampleLevel,
kTrunc kTrunc
}; };
@ -99,6 +100,64 @@ std::ostream& operator<<(std::ostream& out, Intrinsic i);
namespace intrinsic { namespace intrinsic {
/// Signature is the base struct for all intrinsic signature types.
/// Signatures are used to identify the particular overload for intrinsics that
/// have different signatures with the same function name.
struct Signature {
virtual ~Signature();
};
/// TextureSignature describes the signature of a texture intrinsic function.
struct TextureSignature : public Signature {
/// Parameters describes the parameters for the texture function.
struct Parameters {
/// kNotUsed is the constant that indicates the given parameter is not part
/// of the texture function signature.
static constexpr const size_t kNotUsed = ~static_cast<size_t>(0u);
/// Index holds each of the possible parameter indices. If a parameter index
/// is equal to `kNotUsed` then this parameter is not used by the function.
struct Index {
/// Constructor
Index();
/// Copy constructor
Index(const Index&);
/// `array_index` parameter index.
size_t array_index = kNotUsed;
/// `bias` parameter index.
size_t bias = kNotUsed;
/// `coords` parameter index.
size_t coords = kNotUsed;
/// `depth_ref` parameter index.
size_t depth_ref = kNotUsed;
/// `ddx` parameter index.
size_t ddx = kNotUsed;
/// `ddy` parameter index.
size_t ddy = kNotUsed;
/// `level` parameter index.
size_t level = kNotUsed;
/// `offset` parameter index.
size_t offset = kNotUsed;
/// `sampler` parameter index.
size_t sampler = kNotUsed;
/// `texture` parameter index.
size_t texture = kNotUsed;
};
/// The indices of all possible parameters.
Index idx;
/// Total number of parameters.
size_t count = 0;
};
/// Construct an immutable `TextureSignature`.
/// @param p the texture intrinsic parameter signature.
explicit TextureSignature(const Parameters& p) : params(p) {}
~TextureSignature() override;
/// The texture intrinsic parameter signature.
const Parameters params;
};
/// Determines if the given |name| is a coarse derivative /// Determines if the given |name| is a coarse derivative
/// @param i the intrinsic /// @param i the intrinsic
/// @returns true if the given derivative is coarse. /// @returns true if the given derivative is coarse.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,171 @@
// 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.
#ifndef SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_
#define SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_
#include <functional>
#include <vector>
#include "src/ast/builder.h"
#include "src/ast/type/sampler_type.h"
#include "src/ast/type/texture_type.h"
namespace tint {
namespace ast {
namespace intrinsic {
namespace test {
enum class TextureKind { kRegular, kDepth };
inline std::ostream& operator<<(std::ostream& out, const TextureKind& kind) {
switch (kind) {
case TextureKind::kRegular:
out << "regular";
break;
case TextureKind::kDepth:
out << "depth";
break;
}
return out;
}
enum class TextureDataType { kF32, kU32, kI32 };
inline std::ostream& operator<<(std::ostream& out, const TextureDataType& ty) {
switch (ty) {
case TextureDataType::kF32:
out << "f32";
break;
case TextureDataType::kU32:
out << "u32";
break;
case TextureDataType::kI32:
out << "i32";
break;
}
return out;
}
enum class ValidTextureOverload {
kSample1dF32,
kSample1dArrayF32,
kSample2dF32,
kSample2dOffsetF32,
kSample2dArrayF32,
kSample2dArrayOffsetF32,
kSample3dF32,
kSample3dOffsetF32,
kSampleCubeF32,
kSampleCubeArrayF32,
kSampleDepth2dF32,
kSampleDepth2dOffsetF32,
kSampleDepth2dArrayF32,
kSampleDepth2dArrayOffsetF32,
kSampleDepthCubeF32,
kSampleDepthCubeArrayF32,
kSampleBias2dF32,
kSampleBias2dOffsetF32,
kSampleBias2dArrayF32,
kSampleBias2dArrayOffsetF32,
kSampleBias3dF32,
kSampleBias3dOffsetF32,
kSampleBiasCubeF32,
kSampleBiasCubeArrayF32,
kSampleLevel2dF32,
kSampleLevel2dOffsetF32,
kSampleLevel2dArrayF32,
kSampleLevel2dArrayOffsetF32,
kSampleLevel3dF32,
kSampleLevel3dOffsetF32,
kSampleLevelCubeF32,
kSampleLevelCubeArrayF32,
kSampleLevelDepth2dF32,
kSampleLevelDepth2dOffsetF32,
kSampleLevelDepth2dArrayF32,
kSampleLevelDepth2dArrayOffsetF32,
kSampleLevelDepthCubeF32,
kSampleLevelDepthCubeArrayF32,
kSampleGrad2dF32,
kSampleGrad2dOffsetF32,
kSampleGrad2dArrayF32,
kSampleGrad2dArrayOffsetF32,
kSampleGrad3dF32,
kSampleGrad3dOffsetF32,
kSampleGradCubeF32,
kSampleGradCubeArrayF32,
kSampleGradDepth2dF32,
kSampleGradDepth2dOffsetF32,
kSampleGradDepth2dArrayF32,
kSampleGradDepth2dArrayOffsetF32,
kSampleGradDepthCubeF32,
kSampleGradDepthCubeArrayF32,
};
/// Describes a texture intrinsic overload
struct TextureOverloadCase {
/// Constructor
TextureOverloadCase();
/// Constructor
TextureOverloadCase(ValidTextureOverload,
const char*,
TextureKind,
ast::type::SamplerKind,
ast::type::TextureDimension,
TextureDataType,
const char*,
std::function<ast::ExpressionList(ast::Builder*)>);
/// Copy constructor
TextureOverloadCase(const TextureOverloadCase&);
/// Destructor
~TextureOverloadCase();
/// @return a vector containing a large number of valid texture overloads
static std::vector<TextureOverloadCase> ValidCases();
/// The enumerator for this overload
ValidTextureOverload overload;
/// A human readable description of the overload
const char* description;
/// The texture kind for the texture parameter
TextureKind texture_kind;
/// The sampler kind for the sampler parameter
ast::type::SamplerKind sampler_kind;
/// The dimensions of the texture parameter
ast::type::TextureDimension texture_dimension;
/// The data type of the texture parameter
TextureDataType texture_data_type;
/// Name of the function. e.g. `textureSample`, `textureSampleGrad`, etc
const char* function;
/// A function that builds the AST arguments for the overload
std::function<ast::ExpressionList(ast::Builder*)> args;
};
inline std::ostream& operator<<(std::ostream& out,
const TextureOverloadCase& data) {
out << "TextureOverloadCase" << static_cast<int>(data.overload) << "\n";
out << data.description << "\n";
out << "texture_kind: " << data.texture_kind << "\n";
out << "sampler_kind: " << data.sampler_kind << "\n";
out << "texture_dimension: " << data.texture_dimension << "\n";
out << "texture_data_type: " << data.texture_data_type << "\n";
return out;
}
} // namespace test
} // namespace intrinsic
} // namespace ast
} // namespace tint
#endif // SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_

View File

@ -529,6 +529,44 @@ class InspectorHelper {
body); body);
} }
/// Generates a function that references a specific sampler variable
/// @param func_name name of the function created
/// @param texture_name name of the texture to be sampled
/// @param sampler_name name of the sampler to use
/// @param coords_name name of the coords variable to use
/// @param array_index name of the array index variable to use
/// @returns a function that references all of the values specified
ast::Function* MakeSamplerReferenceBodyFunction(
const std::string& func_name,
const std::string& texture_name,
const std::string& sampler_name,
const std::string& coords_name,
const std::string& array_index,
ast::type::Type* base_type) {
std::string result_name = "sampler_result";
auto* body = create<ast::BlockStatement>();
auto* call_result = create<ast::Variable>(
"sampler_result", ast::StorageClass::kFunction, vec_type(base_type, 4));
body->append(create<ast::VariableDeclStatement>(call_result));
ast::ExpressionList call_params;
call_params.push_back(create<ast::IdentifierExpression>(texture_name));
call_params.push_back(create<ast::IdentifierExpression>(sampler_name));
call_params.push_back(create<ast::IdentifierExpression>(coords_name));
call_params.push_back(create<ast::IdentifierExpression>(array_index));
auto* call_expr = create<ast::CallExpression>(
create<ast::IdentifierExpression>("textureSample"), call_params);
body->append(create<ast::AssignmentStatement>(
create<ast::IdentifierExpression>("sampler_result"), call_expr));
body->append(create<ast::ReturnStatement>());
return create<ast::Function>(func_name, ast::VariableList(), void_type(),
body);
}
/// Generates a function that references a specific comparison sampler /// Generates a function that references a specific comparison sampler
/// variable. /// variable.
/// @param func_name name of the function created /// @param func_name name of the function created
@ -683,6 +721,9 @@ class InspectorGetComparisonSamplerResourceBindingsTest
public testing::Test {}; public testing::Test {};
class InspectorGetSampledTextureResourceBindingsTest : public InspectorHelper, class InspectorGetSampledTextureResourceBindingsTest : public InspectorHelper,
public testing::Test {}; public testing::Test {};
class InspectorGetSampledArrayTextureResourceBindingsTest
: public InspectorHelper,
public testing::Test {};
struct GetSampledTextureTestParams { struct GetSampledTextureTestParams {
ast::type::TextureDimension type_dim; ast::type::TextureDimension type_dim;
inspector::ResourceBinding::TextureDimension inspector_dim; inspector::ResourceBinding::TextureDimension inspector_dim;
@ -691,11 +732,19 @@ struct GetSampledTextureTestParams {
class InspectorGetSampledTextureResourceBindingsTestWithParam class InspectorGetSampledTextureResourceBindingsTestWithParam
: public InspectorHelper, : public InspectorHelper,
public testing::TestWithParam<GetSampledTextureTestParams> {}; public testing::TestWithParam<GetSampledTextureTestParams> {};
class InspectorGetSampledArrayTextureResourceBindingsTestWithParam
: public InspectorHelper,
public testing::TestWithParam<GetSampledTextureTestParams> {};
class InspectorGetMultisampledTextureResourceBindingsTest class InspectorGetMultisampledTextureResourceBindingsTest
: public InspectorHelper, : public InspectorHelper,
public testing::Test {}; public testing::Test {};
class InspectorGetMultisampledArrayTextureResourceBindingsTest
: public InspectorHelper,
public testing::Test {};
typedef GetSampledTextureTestParams GetMultisampledTextureTestParams; typedef GetSampledTextureTestParams GetMultisampledTextureTestParams;
class InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam
: public InspectorHelper,
public testing::TestWithParam<GetMultisampledTextureTestParams> {};
class InspectorGetMultisampledTextureResourceBindingsTestWithParam class InspectorGetMultisampledTextureResourceBindingsTestWithParam
: public InspectorHelper, : public InspectorHelper,
public testing::TestWithParam<GetMultisampledTextureTestParams> {}; public testing::TestWithParam<GetMultisampledTextureTestParams> {};
@ -2008,18 +2057,6 @@ INSTANTIATE_TEST_SUITE_P(
ast::type::TextureDimension::k1d, ast::type::TextureDimension::k1d,
inspector::ResourceBinding::TextureDimension::k1d, inspector::ResourceBinding::TextureDimension::k1d,
inspector::ResourceBinding::SampledKind::kUInt}, inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{ GetSampledTextureTestParams{
ast::type::TextureDimension::k2d, ast::type::TextureDimension::k2d,
inspector::ResourceBinding::TextureDimension::k2d, inspector::ResourceBinding::TextureDimension::k2d,
@ -2032,18 +2069,6 @@ INSTANTIATE_TEST_SUITE_P(
ast::type::TextureDimension::k2d, ast::type::TextureDimension::k2d,
inspector::ResourceBinding::TextureDimension::k2d, inspector::ResourceBinding::TextureDimension::k2d,
inspector::ResourceBinding::SampledKind::kUInt}, inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{ GetSampledTextureTestParams{
ast::type::TextureDimension::k3d, ast::type::TextureDimension::k3d,
inspector::ResourceBinding::TextureDimension::k3d, inspector::ResourceBinding::TextureDimension::k3d,
@ -2067,6 +2092,65 @@ INSTANTIATE_TEST_SUITE_P(
GetSampledTextureTestParams{ GetSampledTextureTestParams{
ast::type::TextureDimension::kCube, ast::type::TextureDimension::kCube,
inspector::ResourceBinding::TextureDimension::kCube, inspector::ResourceBinding::TextureDimension::kCube,
inspector::ResourceBinding::SampledKind::kUInt}));
TEST_P(InspectorGetSampledArrayTextureResourceBindingsTestWithParam,
textureSample) {
auto sampled_texture_type = MakeSampledTextureType(
GetParam().type_dim, GetBaseType(GetParam().sampled_kind));
AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
AddSampler("foo_sampler", 0, 1);
auto* coord_type =
GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
AddGlobalVariable("foo_coords", coord_type);
AddGlobalVariable("foo_array_index", u32_type());
auto* func = MakeSamplerReferenceBodyFunction(
"ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
GetBaseType(GetParam().sampled_kind));
func->add_decoration(
create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
mod()->AddFunction(func);
ASSERT_TRUE(td()->Determine()) << td()->error();
auto result = inspector()->GetSampledTextureResourceBindings("ep");
ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(GetParam().inspector_dim, result[0].dim);
EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind);
}
INSTANTIATE_TEST_SUITE_P(
InspectorGetSampledArrayTextureResourceBindingsTest,
InspectorGetSampledArrayTextureResourceBindingsTestWithParam,
testing::Values(
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetSampledTextureTestParams{
ast::type::TextureDimension::k2dArray,
inspector::ResourceBinding::TextureDimension::k2dArray,
inspector::ResourceBinding::SampledKind::kUInt}, inspector::ResourceBinding::SampledKind::kUInt},
GetSampledTextureTestParams{ GetSampledTextureTestParams{
ast::type::TextureDimension::kCubeArray, ast::type::TextureDimension::kCubeArray,
@ -2081,18 +2165,6 @@ INSTANTIATE_TEST_SUITE_P(
inspector::ResourceBinding::TextureDimension::kCubeArray, inspector::ResourceBinding::TextureDimension::kCubeArray,
inspector::ResourceBinding::SampledKind::kUInt})); inspector::ResourceBinding::SampledKind::kUInt}));
TEST_F(InspectorGetMultisampledTextureResourceBindingsTest, Empty) {
auto* foo = MakeEmptyBodyFunction("foo");
foo->add_decoration(
create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
mod()->AddFunction(foo);
auto result = inspector()->GetSampledTextureResourceBindings("foo");
ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
EXPECT_EQ(0u, result.size());
}
TEST_P(InspectorGetMultisampledTextureResourceBindingsTestWithParam, TEST_P(InspectorGetMultisampledTextureResourceBindingsTestWithParam,
textureSample) { textureSample) {
auto multisampled_texture_type = MakeMultisampledTextureType( auto multisampled_texture_type = MakeMultisampledTextureType(
@ -2138,18 +2210,6 @@ INSTANTIATE_TEST_SUITE_P(
ast::type::TextureDimension::k1d, ast::type::TextureDimension::k1d,
inspector::ResourceBinding::TextureDimension::k1d, inspector::ResourceBinding::TextureDimension::k1d,
inspector::ResourceBinding::SampledKind::kUInt}, inspector::ResourceBinding::SampledKind::kUInt},
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kUInt},
GetMultisampledTextureTestParams{ GetMultisampledTextureTestParams{
ast::type::TextureDimension::k2d, ast::type::TextureDimension::k2d,
inspector::ResourceBinding::TextureDimension::k2d, inspector::ResourceBinding::TextureDimension::k2d,
@ -2161,6 +2221,65 @@ INSTANTIATE_TEST_SUITE_P(
GetMultisampledTextureTestParams{ GetMultisampledTextureTestParams{
ast::type::TextureDimension::k2d, ast::type::TextureDimension::k2d,
inspector::ResourceBinding::TextureDimension::k2d, inspector::ResourceBinding::TextureDimension::k2d,
inspector::ResourceBinding::SampledKind::kUInt}));
TEST_F(InspectorGetMultisampledArrayTextureResourceBindingsTest, Empty) {
auto* foo = MakeEmptyBodyFunction("foo");
foo->add_decoration(
create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
mod()->AddFunction(foo);
auto result = inspector()->GetSampledTextureResourceBindings("foo");
ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
EXPECT_EQ(0u, result.size());
}
TEST_P(InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam,
textureSample) {
auto multisampled_texture_type = MakeMultisampledTextureType(
GetParam().type_dim, GetBaseType(GetParam().sampled_kind));
AddMultisampledTexture("foo_texture", multisampled_texture_type.get(), 0, 0);
AddSampler("foo_sampler", 0, 1);
auto* coord_type =
GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
AddGlobalVariable("foo_coords", coord_type);
AddGlobalVariable("foo_array_index", u32_type());
auto* func = MakeSamplerReferenceBodyFunction(
"ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
GetBaseType(GetParam().sampled_kind));
func->add_decoration(
create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
mod()->AddFunction(func);
ASSERT_TRUE(td()->Determine()) << td()->error();
auto result = inspector()->GetMultisampledTextureResourceBindings("ep");
ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].bind_group);
EXPECT_EQ(0u, result[0].binding);
EXPECT_EQ(GetParam().inspector_dim, result[0].dim);
EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind);
}
INSTANTIATE_TEST_SUITE_P(
InspectorGetMultisampledArrayTextureResourceBindingsTest,
InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam,
testing::Values(
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kFloat},
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kSInt},
GetMultisampledTextureTestParams{
ast::type::TextureDimension::k1dArray,
inspector::ResourceBinding::TextureDimension::k1dArray,
inspector::ResourceBinding::SampledKind::kUInt}, inspector::ResourceBinding::SampledKind::kUInt},
GetMultisampledTextureTestParams{ GetMultisampledTextureTestParams{
ast::type::TextureDimension::k2dArray, ast::type::TextureDimension::k2dArray,

View File

@ -552,26 +552,7 @@ bool TypeDeterminer::DetermineIntrinsic(ast::IdentifierExpression* ident,
return true; return true;
} }
if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) { if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) {
// TODO(dsinclair): Remove the LOD param from textureLoad on storage ast::intrinsic::TextureSignature::Parameters param;
// textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
uint32_t num_of_params =
(ident->intrinsic() == ast::Intrinsic::kTextureLoad ||
ident->intrinsic() == ast::Intrinsic::kTextureSample)
? 3
: 4;
if (expr->params().size() != num_of_params) {
set_error(expr->source(),
"incorrect number of parameters for " + ident->name() +
", got " + std::to_string(expr->params().size()) +
" and expected " + std::to_string(num_of_params));
return false;
}
if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
expr->func()->set_result_type(
ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
return true;
}
auto* texture_param = expr->params()[0]; auto* texture_param = expr->params()[0];
if (!texture_param->result_type()->UnwrapPtrIfNeeded()->IsTexture()) { if (!texture_param->result_type()->UnwrapPtrIfNeeded()->IsTexture()) {
@ -581,6 +562,115 @@ bool TypeDeterminer::DetermineIntrinsic(ast::IdentifierExpression* ident,
ast::type::TextureType* texture = ast::type::TextureType* texture =
texture_param->result_type()->UnwrapPtrIfNeeded()->AsTexture(); texture_param->result_type()->UnwrapPtrIfNeeded()->AsTexture();
bool is_array = false;
switch (texture->dim()) {
case ast::type::TextureDimension::k1dArray:
case ast::type::TextureDimension::k2dArray:
case ast::type::TextureDimension::kCubeArray:
is_array = true;
break;
default:
break;
}
switch (ident->intrinsic()) {
case ast::Intrinsic::kTextureLoad:
param.idx.texture = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
// TODO(dsinclair): Remove the LOD param from textureLoad on storage
// textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
if (expr->params().size() > param.count) {
param.idx.level = param.count++;
}
break;
case ast::Intrinsic::kTextureSample:
param.idx.texture = param.count++;
param.idx.sampler = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
if (expr->params().size() > param.count) {
param.idx.offset = param.count++;
}
break;
case ast::Intrinsic::kTextureSampleBias:
param.idx.texture = param.count++;
param.idx.sampler = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
param.idx.bias = param.count++;
if (expr->params().size() > param.count) {
param.idx.offset = param.count++;
}
break;
case ast::Intrinsic::kTextureSampleLevel:
param.idx.texture = param.count++;
param.idx.sampler = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
param.idx.level = param.count++;
if (expr->params().size() > param.count) {
param.idx.offset = param.count++;
}
break;
case ast::Intrinsic::kTextureSampleCompare:
param.idx.texture = param.count++;
param.idx.sampler = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
param.idx.depth_ref = param.count++;
if (expr->params().size() > param.count) {
param.idx.offset = param.count++;
}
break;
case ast::Intrinsic::kTextureSampleGrad:
param.idx.texture = param.count++;
param.idx.sampler = param.count++;
param.idx.coords = param.count++;
if (is_array) {
param.idx.array_index = param.count++;
}
param.idx.ddx = param.count++;
param.idx.ddy = param.count++;
if (expr->params().size() > param.count) {
param.idx.offset = param.count++;
}
break;
default:
set_error(expr->source(),
"Internal compiler error: Unreachable intrinsic " +
std::to_string(static_cast<int>(ident->intrinsic())));
return false;
}
if (expr->params().size() != param.count) {
set_error(expr->source(),
"incorrect number of parameters for " + ident->name() +
", got " + std::to_string(expr->params().size()) +
" and expected " + std::to_string(param.count));
return false;
}
ident->set_intrinsic_signature(
std::make_unique<ast::intrinsic::TextureSignature>(param));
if (texture->IsDepth()) {
expr->func()->set_result_type(
ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
return true;
}
if (!texture->IsStorage() && if (!texture->IsStorage() &&
!(texture->IsSampled() || texture->IsMultisampled())) { !(texture->IsSampled() || texture->IsMultisampled())) {
set_error(expr->source(), "invalid texture for " + ident->name()); set_error(expr->source(), "invalid texture for " + ident->name());
@ -925,6 +1015,8 @@ bool TypeDeterminer::SetIntrinsicIfNeeded(ast::IdentifierExpression* ident) {
ident->set_intrinsic(ast::Intrinsic::kTextureSampleBias); ident->set_intrinsic(ast::Intrinsic::kTextureSampleBias);
} else if (ident->name() == "textureSampleCompare") { } else if (ident->name() == "textureSampleCompare") {
ident->set_intrinsic(ast::Intrinsic::kTextureSampleCompare); ident->set_intrinsic(ast::Intrinsic::kTextureSampleCompare);
} else if (ident->name() == "textureSampleGrad") {
ident->set_intrinsic(ast::Intrinsic::kTextureSampleGrad);
} else if (ident->name() == "textureSampleLevel") { } else if (ident->name() == "textureSampleLevel") {
ident->set_intrinsic(ast::Intrinsic::kTextureSampleLevel); ident->set_intrinsic(ast::Intrinsic::kTextureSampleLevel);
} else if (ident->name() == "trunc") { } else if (ident->name() == "trunc") {

File diff suppressed because it is too large Load Diff

View File

@ -1749,9 +1749,15 @@ uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
return 0; return 0;
} }
auto intrinsic = ident->intrinsic();
if (ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
GenerateTextureIntrinsic(ident, call, Operand::Int(result_type_id), result);
return result_id;
}
OperandList params = {Operand::Int(result_type_id), result}; OperandList params = {Operand::Int(result_type_id), result};
auto intrinsic = ident->intrinsic();
if (ast::intrinsic::IsFineDerivative(intrinsic) || if (ast::intrinsic::IsFineDerivative(intrinsic) ||
ast::intrinsic::IsCoarseDerivative(intrinsic)) { ast::intrinsic::IsCoarseDerivative(intrinsic)) {
push_capability(SpvCapabilityDerivativeControl); push_capability(SpvCapabilityDerivativeControl);
@ -1825,7 +1831,7 @@ uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
op = spv::Op::OpBitReverse; op = spv::Op::OpBitReverse;
} else if (intrinsic == ast::Intrinsic::kSelect) { } else if (intrinsic == ast::Intrinsic::kSelect) {
op = spv::Op::OpSelect; op = spv::Op::OpSelect;
} else if (!ast::intrinsic::IsTextureIntrinsic(intrinsic)) { } else {
GenerateGLSLstd450Import(); GenerateGLSLstd450Import();
auto set_iter = import_name_to_id_.find(kGLSLstd450); auto set_iter = import_name_to_id_.find(kGLSLstd450);
@ -1846,7 +1852,8 @@ uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
op = spv::Op::OpExtInst; op = spv::Op::OpExtInst;
} }
if (!ast::intrinsic::IsTextureIntrinsic(intrinsic) && op == spv::Op::OpNop) {
if (op == spv::Op::OpNop) {
error_ = "unable to determine operator for: " + ident->name(); error_ = "unable to determine operator for: " + ident->name();
return 0; return 0;
} }
@ -1854,15 +1861,11 @@ uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
for (auto* p : call->params()) { for (auto* p : call->params()) {
auto val_id = GenerateExpression(p); auto val_id = GenerateExpression(p);
if (val_id == 0) { if (val_id == 0) {
return 0; return false;
} }
val_id = GenerateLoadIfNeeded(p->result_type(), val_id); val_id = GenerateLoadIfNeeded(p->result_type(), val_id);
params.push_back(Operand::Int(val_id)); params.emplace_back(Operand::Int(val_id));
}
if (ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
return GenerateTextureIntrinsic(ident, call, result_id, params);
} }
push_function_inst(op, params); push_function_inst(op, params);
@ -1870,68 +1873,171 @@ uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
return result_id; return result_id;
} }
uint32_t Builder::GenerateTextureIntrinsic(ast::IdentifierExpression* ident, void Builder::GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
ast::CallExpression* call, ast::CallExpression* call,
uint32_t result_id, spirv::Operand result_type,
OperandList wgsl_params) { spirv::Operand result_id) {
auto* texture_type = auto* texture_type =
call->params()[0]->result_type()->UnwrapAll()->AsTexture(); call->params()[0]->result_type()->UnwrapAll()->AsTexture();
// TODO(dsinclair): Remove the LOD param from textureLoad on storage textures auto* sig = static_cast<const ast::intrinsic::TextureSignature*>(
// when https://github.com/gpuweb/gpuweb/pull/1032 gets merged. ident->intrinsic_signature());
assert(sig != nullptr);
auto& pidx = sig->params.idx;
auto const kNotUsed = ast::intrinsic::TextureSignature::Parameters::kNotUsed;
assert(pidx.texture != kNotUsed);
auto op = spv::Op::OpNop;
auto gen_param = [&](size_t idx) {
auto* p = call->params()[idx];
auto val_id = GenerateExpression(p);
if (val_id == 0) {
return Operand::Int(0);
}
val_id = GenerateLoadIfNeeded(p->result_type(), val_id);
return Operand::Int(val_id);
};
// Populate the spirv_params with common parameters
OperandList spirv_params;
spirv_params.reserve(8); // Enough to fit most parameter lists
spirv_params.emplace_back(std::move(result_type)); // result type
spirv_params.emplace_back(std::move(result_id)); // result id
// Extra image operands, appended to spirv_params.
uint32_t spirv_operand_mask = 0;
OperandList spirv_operands;
spirv_operands.reserve(4); // Enough to fit most parameter lists
if (ident->intrinsic() == ast::Intrinsic::kTextureLoad) { if (ident->intrinsic() == ast::Intrinsic::kTextureLoad) {
std::vector<Operand> spirv_params = { op = texture_type->IsStorage() ? spv::Op::OpImageRead
std::move(wgsl_params[0]), std::move(wgsl_params[1]), : spv::Op::OpImageFetch;
std::move(wgsl_params[2]), std::move(wgsl_params[3])}; spirv_params.emplace_back(gen_param(pidx.texture));
if (texture_type->IsMultisampled()) { spirv_params.emplace_back(gen_param(pidx.coords));
spirv_params.push_back(Operand::Int(SpvImageOperandsSampleMask));
// TODO(dsinclair): Remove the LOD param from textureLoad on storage
// textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
if (pidx.level != kNotUsed) {
if (texture_type->IsMultisampled()) {
spirv_operand_mask |= SpvImageOperandsSampleMask;
} else {
spirv_operand_mask |= SpvImageOperandsLodMask;
}
spirv_operands.emplace_back(gen_param(pidx.level));
}
} else {
assert(pidx.sampler != kNotUsed);
auto sampler_param = gen_param(pidx.sampler);
auto texture_param = gen_param(pidx.texture);
auto sampled_image =
GenerateSampledImage(texture_type, texture_param, sampler_param);
// Populate the spirv_params with the common parameters
spirv_params.emplace_back(Operand::Int(sampled_image)); // sampled image
if (pidx.array_index != kNotUsed) {
// Array index needs to be appended to the coordinates.
auto* param_coords = call->params()[pidx.coords];
auto* param_array_index = call->params()[pidx.array_index];
uint32_t packed_coords_size;
ast::type::Type* packed_coords_el_ty; // Currenly must be f32.
if (param_coords->result_type()->IsVector()) {
auto* vec = param_coords->result_type()->AsVector();
packed_coords_size = vec->size() + 1;
packed_coords_el_ty = vec->type();
} else {
packed_coords_size = 2;
packed_coords_el_ty = param_coords->result_type();
}
// Cast param_array_index to the vector element type
ast::TypeConstructorExpression array_index_cast(packed_coords_el_ty,
{param_array_index});
array_index_cast.set_result_type(packed_coords_el_ty);
ast::type::VectorType packed_coords_ty(packed_coords_el_ty,
packed_coords_size);
ast::TypeConstructorExpression constructor{
&packed_coords_ty, {param_coords, &array_index_cast}};
auto packed_coords =
GenerateTypeConstructorExpression(&constructor, false);
spirv_params.emplace_back(Operand::Int(packed_coords)); // coordinates
} else { } else {
spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask)); spirv_params.emplace_back(gen_param(pidx.coords)); // coordinates
} }
spirv_params.push_back(std::move(wgsl_params[4]));
auto op = spv::Op::OpImageFetch; switch (ident->intrinsic()) {
if (texture_type->IsStorage()) { case ast::Intrinsic::kTextureSample: {
op = spv::Op::OpImageRead; op = spv::Op::OpImageSampleImplicitLod;
break;
}
case ast::Intrinsic::kTextureSampleBias: {
op = spv::Op::OpImageSampleImplicitLod;
assert(pidx.bias != kNotUsed);
spirv_operand_mask |= SpvImageOperandsBiasMask;
spirv_operands.emplace_back(gen_param(pidx.bias));
break;
}
case ast::Intrinsic::kTextureSampleLevel: {
op = spv::Op::OpImageSampleExplicitLod;
assert(pidx.level != kNotUsed);
spirv_operand_mask |= SpvImageOperandsLodMask;
spirv_operands.emplace_back(gen_param(pidx.level));
break;
}
case ast::Intrinsic::kTextureSampleGrad: {
op = spv::Op::OpImageSampleExplicitLod;
assert(pidx.ddx != kNotUsed);
assert(pidx.ddy != kNotUsed);
spirv_operand_mask |= SpvImageOperandsGradMask;
spirv_operands.emplace_back(gen_param(pidx.ddx));
spirv_operands.emplace_back(gen_param(pidx.ddy));
break;
}
case ast::Intrinsic::kTextureSampleCompare: {
op = spv::Op::OpImageSampleDrefExplicitLod;
assert(pidx.depth_ref != kNotUsed);
spirv_params.emplace_back(gen_param(pidx.depth_ref));
spirv_operand_mask |= SpvImageOperandsLodMask;
ast::type::F32Type f32;
ast::FloatLiteral float_0(&f32, 0.0);
spirv_operands.emplace_back(
Operand::Int(GenerateLiteralIfNeeded(nullptr, &float_0)));
break;
}
default:
break; // unreachable
} }
push_function_inst(op, spirv_params);
return result_id;
} }
spv::Op op = spv::Op::OpNop; if (pidx.offset != kNotUsed) {
OperandList spirv_params = { spirv_operand_mask |= SpvImageOperandsOffsetMask;
wgsl_params[0], std::move(wgsl_params[1]), spirv_operands.emplace_back(gen_param(pidx.offset));
Operand::Int(GenerateSampledImage(texture_type, std::move(wgsl_params[2]),
std::move(wgsl_params[3]))),
std::move(wgsl_params[4])};
if (ident->intrinsic() == ast::Intrinsic::kTextureSample) {
op = spv::Op::OpImageSampleImplicitLod;
} else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleLevel) {
op = spv::Op::OpImageSampleExplicitLod;
spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
spirv_params.push_back(std::move(wgsl_params[5]));
} else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleBias) {
op = spv::Op::OpImageSampleImplicitLod;
spirv_params.push_back(Operand::Int(SpvImageOperandsBiasMask));
spirv_params.push_back(std::move(wgsl_params[5]));
} else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
op = spv::Op::OpImageSampleDrefExplicitLod;
spirv_params.push_back(std::move(wgsl_params[5]));
spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
ast::type::F32Type f32;
ast::FloatLiteral float_0(&f32, 0.0);
spirv_params.push_back(
Operand::Int(GenerateLiteralIfNeeded(nullptr, &float_0)));
} }
if (spirv_operand_mask != 0) {
// Note: Order of operands is based on SpvImageXXXOperands value -
// smaller-numbered SpvImageXXXOperands bits appear first.
spirv_params.emplace_back(Operand::Int(spirv_operand_mask));
spirv_params.insert(std::end(spirv_params), std::begin(spirv_operands),
std::end(spirv_operands));
}
if (op == spv::Op::OpNop) { if (op == spv::Op::OpNop) {
error_ = "unable to determine operator for: " + ident->name(); error_ = "unable to determine operator for: " + ident->name();
return 0; return;
} }
push_function_inst(op, spirv_params);
return result_id; push_function_inst(op, spirv_params);
} }
uint32_t Builder::GenerateSampledImage(ast::type::Type* texture_type, uint32_t Builder::GenerateSampledImage(ast::type::Type* texture_type,

View File

@ -231,7 +231,7 @@ class Builder {
bool GenerateExecutionModes(ast::Function* func, uint32_t id); bool GenerateExecutionModes(ast::Function* func, uint32_t id);
/// Generates an expression /// Generates an expression
/// @param expr the expression to generate /// @param expr the expression to generate
/// @returns the resulting ID of the exp = {};ression or 0 on error /// @returns the resulting ID of the expression or 0 on error
uint32_t GenerateExpression(ast::Expression* expr); uint32_t GenerateExpression(ast::Expression* expr);
/// Generates the instructions for a function /// Generates the instructions for a function
/// @param func the function to generate /// @param func the function to generate
@ -335,14 +335,13 @@ class Builder {
/// Generates a texture intrinsic call /// Generates a texture intrinsic call
/// @param ident the texture intrinsic /// @param ident the texture intrinsic
/// @param call the call expression /// @param call the call expression
/// @param result_id result ID of the texture instruction /// @param result_type result type operand of the texture instruction
/// @param wgsl_params SPIR-V arguments for WGSL-specific intrinsic's call /// @param result_id result identifier operand of the texture instruction
/// parameters /// parameters
/// @returns the expression ID on success or 0 otherwise void GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
uint32_t GenerateTextureIntrinsic(ast::IdentifierExpression* ident, ast::CallExpression* call,
ast::CallExpression* call, spirv::Operand result_type,
uint32_t result_id, spirv::Operand result_id);
OperandList wgsl_params);
/// Generates a sampled image /// Generates a sampled image
/// @param texture_type the texture type /// @param texture_type the texture type
/// @param texture_operand the texture operand /// @param texture_operand the texture operand

File diff suppressed because it is too large Load Diff