| // |
| // Copyright 2020 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| #include <cctype> |
| |
| #include "compiler/translator/InfoSink.h" |
| #include "compiler/translator/Symbol.h" |
| #include "compiler/translator/TranslatorMetalDirect/AstHelpers.h" |
| #include "compiler/translator/TranslatorMetalDirect/Name.h" |
| #include "compiler/translator/TranslatorMetalDirect/ProgramPrelude.h" |
| #include "compiler/translator/tree_util/IntermTraverse.h" |
| |
| using namespace sh; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| namespace |
| { |
| |
| class ProgramPrelude : public TIntermTraverser |
| { |
| using LineTag = unsigned; |
| using FuncEmitter = void (*)(ProgramPrelude &, const TFunction &); |
| using FuncToEmitter = std::map<Name, FuncEmitter>; |
| |
| public: |
| ProgramPrelude(TInfoSinkBase &out, const ProgramPreludeConfig &ppc) |
| : TIntermTraverser(true, false, false), mOut(out) |
| { |
| ALWAYS_INLINE(); |
| int_clamp(); |
| if (ppc.hasStructEq) |
| { |
| equalVector(); |
| equalMatrix(); |
| } |
| |
| switch (ppc.shaderType) |
| { |
| case MetalShaderType::None: |
| ASSERT(0 && "ppc.shaderType should not be ShaderTypeNone"); |
| break; |
| case MetalShaderType::Vertex: |
| transform_feedback_guard(); |
| break; |
| case MetalShaderType::Fragment: |
| writeSampleMask(); |
| break; |
| case MetalShaderType::Compute: |
| ASSERT(0 && "compute shaders not currently supported"); |
| break; |
| default: |
| break; |
| } |
| |
| #if 1 |
| mOut << "#define ANGLE_tensor metal::array\n"; |
| mOut << "#pragma clang diagnostic ignored \"-Wunused-value\"\n"; |
| #else |
| tensor(); |
| #endif |
| } |
| |
| private: |
| bool emitGuard(LineTag lineTag) |
| { |
| if (mEmitted.find(lineTag) != mEmitted.end()) |
| { |
| return false; |
| } |
| mEmitted.insert(lineTag); |
| return true; |
| } |
| |
| static FuncToEmitter BuildFuncToEmitter(); |
| |
| void visitOperator(TOperator op, |
| const TFunction *func, |
| const TType *argType0, |
| const TType *argType1 = nullptr); |
| void visitVariable(const Name &name, const TType &type); |
| void visitVariable(const TVariable &var); |
| void visitStructure(const TStructure &s); |
| |
| bool visitBinary(Visit, TIntermBinary *node) override; |
| bool visitUnary(Visit, TIntermUnary *node) override; |
| bool visitAggregate(Visit, TIntermAggregate *node) override; |
| bool visitDeclaration(Visit, TIntermDeclaration *node) override; |
| void visitSymbol(TIntermSymbol *node) override; |
| |
| private: |
| void ALWAYS_INLINE(); |
| |
| void include_metal_atomic(); |
| void include_metal_common(); |
| void include_metal_geometric(); |
| void include_metal_graphics(); |
| void include_metal_math(); |
| void include_metal_matrix(); |
| void include_metal_pack(); |
| void include_metal_relational(); |
| |
| void transform_feedback_guard(); |
| |
| void enable_if(); |
| void scalar_of(); |
| void is_scalar(); |
| void is_vector(); |
| void is_matrix(); |
| void addressof(); |
| void distance(); |
| void length(); |
| void dot(); |
| void normalize(); |
| void faceforward(); |
| void reflect(); |
| void refract(); |
| void degrees(); |
| void radians(); |
| void mod(); |
| void postIncrementMatrix(); |
| void preIncrementMatrix(); |
| void postDecrementMatrix(); |
| void preDecrementMatrix(); |
| void negateMatrix(); |
| void matmulAssign(); |
| void atan(); |
| void int_clamp(); |
| void addMatrixScalarAssign(); |
| void subMatrixScalarAssign(); |
| void addMatrixScalar(); |
| void subMatrixScalar(); |
| void divMatrixScalar(); |
| void divMatrixScalarFast(); |
| void divMatrixScalarAssign(); |
| void divMatrixScalarAssignFast(); |
| void tensor(); |
| void componentWiseDivide(); |
| void componentWiseDivideAssign(); |
| void componentWiseMultiply(); |
| void outerProduct(); |
| void inverse2(); |
| void inverse3(); |
| void inverse4(); |
| void equalScalar(); |
| void equalVector(); |
| void equalMatrix(); |
| void notEqualVector(); |
| void notEqualStruct(); |
| void notEqualStructArray(); |
| void notEqualMatrix(); |
| void equalArray(); |
| void equalStructArray(); |
| void notEqualArray(); |
| void sign(); |
| void pack_half_2x16(); |
| void unpack_half_2x16(); |
| void vectorElemRef(); |
| void swizzleRef(); |
| void out(); |
| void inout(); |
| void flattenArray(); |
| void castVector(); |
| void castMatrix(); |
| void functionConstants(); |
| void gradient(); |
| void writeSampleMask(); |
| void textureEnv(); |
| void texelFetch(); |
| void texelFetchOffset(); |
| void texture(); |
| void texture_generic_float2(); |
| void texture_generic_float2_float(); |
| void texture_generic_float3(); |
| void texture_generic_float3_float(); |
| void texture_depth2d_float3(); |
| void texture_depth2d_float3_float(); |
| void texture_depth2darray_float4(); |
| void texture_depth2darray_float4_float(); |
| void texture_depthcube_float4(); |
| void texture_depthcube_float4_float(); |
| void texture_texture2darray_float3(); |
| void texture_texture2darray_float3_float(); |
| void texture_texture2darray_float4(); |
| void texture_texture2darray_float4_float(); |
| void texture1DLod(); |
| void texture1DProj(); |
| void texture1DProjLod(); |
| void texture2D(); |
| void texture2DLod(); |
| void texture2DProj(); |
| void texture2DProjLod(); |
| void texture2DRect(); |
| void texture2DRectProj(); |
| void texture3DLod(); |
| void texture3DProj(); |
| void texture3DProjLod(); |
| void textureCube(); |
| void textureCubeLod(); |
| void textureCubeProj(); |
| void textureCubeProjLod(); |
| void textureGrad(); |
| void textureGrad_generic_floatN_floatN_floatN(); |
| void textureGrad_generic_float3_float2_float2(); |
| void textureGrad_generic_float4_float2_float2(); |
| void textureGrad_depth2d_float3_float2_float2(); |
| void textureGrad_depth2darray_float4_float2_float2(); |
| void textureGrad_depthcube_float4_float3_float3(); |
| void textureGrad_texturecube_float3_float3_float3(); |
| void textureGradOffset(); |
| void textureGradOffset_generic_floatN_floatN_floatN_intN(); |
| void textureGradOffset_generic_float3_float2_float2_int2(); |
| void textureGradOffset_generic_float4_float2_float2_int2(); |
| void textureGradOffset_depth2d_float3_float2_float2_int2(); |
| void textureGradOffset_depth2darray_float4_float2_float2_int2(); |
| void textureGradOffset_depthcube_float4_float3_float3_int3(); |
| void textureGradOffset_texturecube_float3_float3_float3_int3(); |
| void textureLod(); |
| void textureLod_generic_float2(); |
| void textureLod_generic_float3(); |
| void textureLod_depth2d_float3(); |
| void textureLod_texture2darray_float3(); |
| void textureLod_texture2darray_float4(); |
| void textureLodOffset(); |
| void textureOffset(); |
| void textureProj(); |
| void textureProjGrad(); |
| void textureProjGrad_generic_float3_float2_float2(); |
| void textureProjGrad_generic_float4_float2_float2(); |
| void textureProjGrad_depth2d_float4_float2_float2(); |
| void textureProjGrad_texture3d_float4_float3_float3(); |
| void textureProjGradOffset(); |
| void textureProjGradOffset_generic_float3_float2_float2_int2(); |
| void textureProjGradOffset_generic_float4_float2_float2_int2(); |
| void textureProjGradOffset_depth2d_float4_float2_float2_int2(); |
| void textureProjGradOffset_texture3d_float4_float3_float3_int3(); |
| void textureProjLod(); |
| void textureProjLod_generic_float3(); |
| void textureProjLod_generic_float4(); |
| void textureProjLod_depth2d_float4(); |
| void textureProjLod_texture3d_float4(); |
| void textureProjLodOffset(); |
| void textureProjOffset(); |
| void textureSize(); |
| |
| private: |
| TInfoSinkBase &mOut; |
| std::unordered_set<LineTag> mEmitted; |
| std::unordered_set<const TSymbol *> mHandled; |
| const FuncToEmitter mFuncToEmitter = BuildFuncToEmitter(); |
| }; |
| |
| } // anonymous namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #define PROGRAM_PRELUDE_INCLUDE(header) \ |
| void ProgramPrelude::include_##header() \ |
| { \ |
| if (emitGuard(__LINE__)) \ |
| { \ |
| mOut << ("#include <" #header ">\n\n"); \ |
| } \ |
| } |
| |
| #define PROGRAM_PRELUDE_DECLARE(name, code, ...) \ |
| void ProgramPrelude::name() \ |
| { \ |
| ASSERT(code[0] == '\n'); \ |
| if (emitGuard(__LINE__)) \ |
| { \ |
| __VA_ARGS__; /* dependencies */ \ |
| mOut << (static_cast<const char *>(code "\n") + 1); \ |
| } \ |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| PROGRAM_PRELUDE_INCLUDE(metal_atomic) |
| PROGRAM_PRELUDE_INCLUDE(metal_common) |
| PROGRAM_PRELUDE_INCLUDE(metal_geometric) |
| PROGRAM_PRELUDE_INCLUDE(metal_graphics) |
| PROGRAM_PRELUDE_INCLUDE(metal_math) |
| PROGRAM_PRELUDE_INCLUDE(metal_matrix) |
| PROGRAM_PRELUDE_INCLUDE(metal_pack) |
| PROGRAM_PRELUDE_INCLUDE(metal_relational) |
| |
| PROGRAM_PRELUDE_DECLARE(transform_feedback_guard, R"( |
| #if TRANSFORM_FEEDBACK_ENABLED |
| #define __VERTEX_OUT(args) void |
| #else |
| #define __VERTEX_OUT(args) args |
| #endif |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(ALWAYS_INLINE, R"( |
| #define ANGLE_ALWAYS_INLINE __attribute__((always_inline)) |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(enable_if, R"( |
| template <bool B, typename T = void> |
| struct ANGLE_enable_if {}; |
| template <typename T> |
| struct ANGLE_enable_if<true, T> |
| { |
| using type = T; |
| }; |
| template <bool B> |
| using ANGLE_enable_if_t = typename ANGLE_enable_if<B>::type; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(scalar_of, R"( |
| template <typename T> |
| struct ANGLE_scalar_of |
| { |
| using type = T; |
| }; |
| template <typename T> |
| using ANGLE_scalar_of_t = typename ANGLE_scalar_of<T>::type; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(is_scalar, R"( |
| template <typename T> |
| struct ANGLE_is_scalar {}; |
| #define ANGLE_DEFINE_SCALAR(scalar) \ |
| template <> struct ANGLE_is_scalar<scalar> { enum { value = true }; } |
| ANGLE_DEFINE_SCALAR(bool); |
| ANGLE_DEFINE_SCALAR(char); |
| ANGLE_DEFINE_SCALAR(short); |
| ANGLE_DEFINE_SCALAR(int); |
| ANGLE_DEFINE_SCALAR(long); |
| ANGLE_DEFINE_SCALAR(uchar); |
| ANGLE_DEFINE_SCALAR(ushort); |
| ANGLE_DEFINE_SCALAR(uint); |
| ANGLE_DEFINE_SCALAR(ulong); |
| ANGLE_DEFINE_SCALAR(half); |
| ANGLE_DEFINE_SCALAR(float); |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(is_vector, |
| R"( |
| template <typename T> |
| struct ANGLE_is_vector |
| { |
| enum { value = false }; |
| }; |
| #define ANGLE_DEFINE_VECTOR(scalar) \ |
| template <> struct ANGLE_is_vector<metal::scalar ## 2> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_vector<metal::scalar ## 3> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_vector<metal::scalar ## 4> { enum { value = true }; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 2> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 3> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 4> { using type = scalar; } |
| ANGLE_DEFINE_VECTOR(bool); |
| ANGLE_DEFINE_VECTOR(char); |
| ANGLE_DEFINE_VECTOR(short); |
| ANGLE_DEFINE_VECTOR(int); |
| ANGLE_DEFINE_VECTOR(long); |
| ANGLE_DEFINE_VECTOR(uchar); |
| ANGLE_DEFINE_VECTOR(ushort); |
| ANGLE_DEFINE_VECTOR(uint); |
| ANGLE_DEFINE_VECTOR(ulong); |
| ANGLE_DEFINE_VECTOR(half); |
| ANGLE_DEFINE_VECTOR(float); |
| )", |
| scalar_of()) |
| |
| PROGRAM_PRELUDE_DECLARE(is_matrix, |
| R"( |
| template <typename T> |
| struct ANGLE_is_matrix |
| { |
| enum { value = false }; |
| }; |
| #define ANGLE_DEFINE_MATRIX(scalar) \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 2x2> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 2x3> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 2x4> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 3x2> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 3x3> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 3x4> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 4x2> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 4x3> { enum { value = true }; }; \ |
| template <> struct ANGLE_is_matrix<metal::scalar ## 4x4> { enum { value = true }; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 2x2> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 2x3> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 2x4> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 3x2> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 3x3> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 3x4> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 4x2> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 4x3> { using type = scalar; }; \ |
| template <> struct ANGLE_scalar_of<metal::scalar ## 4x4> { using type = scalar; } |
| ANGLE_DEFINE_MATRIX(half); |
| ANGLE_DEFINE_MATRIX(float); |
| )", |
| scalar_of()) |
| |
| PROGRAM_PRELUDE_DECLARE(addressof, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE thread T * ANGLE_addressof(thread T &ref) |
| { |
| return &ref; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(distance, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_distance_impl |
| { |
| static ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> exec(T x, T y) |
| { |
| return metal::distance(x, y); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_distance_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x, T y) |
| { |
| return metal::abs(x - y); |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> ANGLE_distance(T x, T y) |
| { |
| return ANGLE_distance_impl<T>::exec(x, y); |
| }; |
| )", |
| include_metal_geometric(), |
| include_metal_math(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(length, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_length_impl |
| { |
| static ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> exec(T x) |
| { |
| return metal::length(x); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_length_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x) |
| { |
| return metal::abs(x); |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> ANGLE_length(T x) |
| { |
| return ANGLE_length_impl<T>::exec(x); |
| }; |
| )", |
| include_metal_geometric(), |
| include_metal_math(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(dot, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_dot_impl |
| { |
| static ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> exec(T x, T y) |
| { |
| return metal::dot(x, y); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_dot_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x, T y) |
| { |
| return x * y; |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE ANGLE_scalar_of_t<T> ANGLE_dot(T x, T y) |
| { |
| return ANGLE_dot_impl<T>::exec(x, y); |
| }; |
| )", |
| include_metal_geometric(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(normalize, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_normalize_impl |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x) |
| { |
| return metal::normalize(x); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_normalize_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x) |
| { |
| return ANGLE_sign(x); |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_normalize(T x) |
| { |
| return ANGLE_normalize_impl<T>::exec(x); |
| }; |
| )", |
| include_metal_common(), |
| include_metal_geometric(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix(), |
| sign()) |
| |
| PROGRAM_PRELUDE_DECLARE(faceforward, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_faceforward_impl |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T n, T i, T nref) |
| { |
| return metal::faceforward(n, i, nref); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_faceforward_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T n, T i, T nref) |
| { |
| return ANGLE_dot(nref, i) < T(0) ? n : -n; |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_faceforward(T n, T i, T nref) |
| { |
| return ANGLE_faceforward_impl<T>::exec(n, i, nref); |
| }; |
| )", |
| include_metal_geometric(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix(), |
| dot()) |
| |
| PROGRAM_PRELUDE_DECLARE(reflect, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_reflect_impl |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T i, T n) |
| { |
| return metal::reflect(i, n); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_reflect_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T i, T n) |
| { |
| return i - T(2) * ANGLE_dot(n, i) * n; |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_reflect(T i, T n) |
| { |
| return ANGLE_reflect_impl<T>::exec(i, n); |
| }; |
| )", |
| include_metal_geometric(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix(), |
| dot()) |
| |
| PROGRAM_PRELUDE_DECLARE(refract, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_refract_impl |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T i, T n, ANGLE_scalar_of_t<T> eta) |
| { |
| return metal::refract(i, n, eta); |
| } |
| }; |
| template <typename T> |
| struct ANGLE_refract_impl<T, ANGLE_enable_if_t<ANGLE_is_scalar<T>::value>> |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T i, T n, T eta) |
| { |
| auto dotNI = n * i; |
| auto k = T(1) - eta * eta * (T(1) - dotNI * dotNI); |
| if (k < T(0)) |
| { |
| return T(0); |
| } |
| else |
| { |
| return eta * i - (eta * dotNI + metal::sqrt(k)) * n; |
| } |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_refract(T i, T n, ANGLE_scalar_of_t<T> eta) |
| { |
| return ANGLE_refract_impl<T>::exec(i, n, eta); |
| }; |
| )", |
| include_metal_math(), |
| include_metal_geometric(), |
| enable_if(), |
| is_scalar(), |
| is_vector(), |
| is_matrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(sign, |
| R"( |
| template <typename T, typename Enable = void> |
| struct ANGLE_sign_impl |
| { |
| static ANGLE_ALWAYS_INLINE T exec(T x) |
| { |
| return metal::sign(x); |
| } |
| }; |
| template <> |
| struct ANGLE_sign_impl<int> |
| { |
| static ANGLE_ALWAYS_INLINE int exec(int x) |
| { |
| return (0 < x) - (x < 0); |
| } |
| }; |
| template <int N> |
| struct ANGLE_sign_impl<metal::vec<int, N>> |
| { |
| static ANGLE_ALWAYS_INLINE metal::vec<int, N> exec(metal::vec<int, N> x) |
| { |
| metal::vec<int, N> s; |
| for (int i = 0; i < N; ++i) |
| { |
| s[i] = ANGLE_sign_impl<int>::exec(x[i]); |
| } |
| return s; |
| } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_sign(T x) |
| { |
| return ANGLE_sign_impl<T>::exec(x); |
| }; |
| )", |
| include_metal_common()) |
| |
| PROGRAM_PRELUDE_DECLARE(int_clamp, |
| R"( |
| ANGLE_ALWAYS_INLINE int ANGLE_int_clamp(int value, int minValue, int maxValue) |
| { |
| return ((value < minValue) ? minValue : ((value > maxValue) ? maxValue : value)); |
| }; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(atan, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_atan(T yOverX) |
| { |
| return metal::atan(yOverX); |
| } |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_atan(T y, T x) |
| { |
| return metal::atan2(y, x); |
| } |
| )", |
| include_metal_math()) |
| |
| PROGRAM_PRELUDE_DECLARE(degrees, R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_degrees(T x) |
| { |
| return static_cast<T>(57.29577951308232) * x; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(radians, R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE T ANGLE_radians(T x) |
| { |
| return static_cast<T>(1.7453292519943295e-2) * x; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(mod, |
| R"( |
| template <typename X, typename Y> |
| ANGLE_ALWAYS_INLINE X ANGLE_mod(X x, Y y) |
| { |
| return x - y * metal::floor(x / y); |
| } |
| )", |
| include_metal_math()) |
| |
| PROGRAM_PRELUDE_DECLARE(pack_half_2x16, |
| R"( |
| ANGLE_ALWAYS_INLINE uint ANGLE_pack_half_2x16(float2 v) |
| { |
| return as_type<uint>(half2(v)); |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(unpack_half_2x16, |
| R"( |
| ANGLE_ALWAYS_INLINE float2 ANGLE_unpack_half_2x16(uint x) |
| { |
| return float2(as_type<half2>(x)); |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(matmulAssign, R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator*=(thread metal::matrix<T, Cols, Rows> &a, metal::matrix<T, Cols, Cols> b) |
| { |
| a = a * b; |
| return a; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(postIncrementMatrix, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator++(thread metal::matrix<T, Cols, Rows> &a, int) |
| { |
| auto b = a; |
| a += T(1); |
| return b; |
| } |
| )", |
| addMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(preIncrementMatrix, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator++(thread metal::matrix<T, Cols, Rows> &a) |
| { |
| a += T(1); |
| return a; |
| } |
| )", |
| addMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(postDecrementMatrix, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator--(thread metal::matrix<T, Cols, Rows> &a, int) |
| { |
| auto b = a; |
| a -= T(1); |
| return b; |
| } |
| )", |
| subMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(preDecrementMatrix, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator--(thread metal::matrix<T, Cols, Rows> &a) |
| { |
| a -= T(1); |
| return a; |
| } |
| )", |
| subMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(negateMatrix, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator-(metal::matrix<T, Cols, Rows> m) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| thread auto &mCol = m[col]; |
| mCol = -mCol; |
| } |
| return m; |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(addMatrixScalarAssign, R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator+=(thread metal::matrix<T, Cols, Rows> &m, T x) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| m[col] += x; |
| } |
| return m; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(addMatrixScalar, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator+(metal::matrix<T, Cols, Rows> m, T x) |
| { |
| m += x; |
| return m; |
| } |
| )", |
| addMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(subMatrixScalarAssign, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator-=(thread metal::matrix<T, Cols, Rows> &m, T x) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| m[col] -= x; |
| } |
| return m; |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(subMatrixScalar, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator-(metal::matrix<T, Cols, Rows> m, T x) |
| { |
| m -= x; |
| return m; |
| } |
| )", |
| subMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(divMatrixScalarAssignFast, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator/=(thread metal::matrix<T, Cols, Rows> &m, T x) |
| { |
| x = T(1) / x; |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| m[col] *= x; |
| } |
| return m; |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(divMatrixScalarAssign, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator/=(thread metal::matrix<T, Cols, Rows> &m, T x) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| m[col] /= x; |
| } |
| return m; |
| } |
| )", ) |
| |
| PROGRAM_PRELUDE_DECLARE(divMatrixScalarFast, |
| R"( |
| #if __METAL_VERSION__ <= 220 |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator/(metal::matrix<T, Cols, Rows> m, T x) |
| { |
| m /= x; |
| return m; |
| } |
| #endif |
| )", |
| divMatrixScalarAssignFast()) |
| |
| PROGRAM_PRELUDE_DECLARE(divMatrixScalar, |
| R"( |
| #if __METAL_VERSION__ <= 220 |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator/(metal::matrix<T, Cols, Rows> m, T x) |
| { |
| m /= x; |
| return m; |
| } |
| #endif |
| )", |
| divMatrixScalarAssign()) |
| |
| PROGRAM_PRELUDE_DECLARE(componentWiseDivide, R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> operator/(metal::matrix<T, Cols, Rows> a, metal::matrix<T, Cols, Rows> b) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| a[col] /= b[col]; |
| } |
| return a; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(componentWiseDivideAssign, |
| R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE thread metal::matrix<T, Cols, Rows> &operator/=(thread metal::matrix<T, Cols, Rows> &a, metal::matrix<T, Cols, Rows> b) |
| { |
| a = a / b; |
| return a; |
| } |
| )", |
| componentWiseDivide()) |
| |
| PROGRAM_PRELUDE_DECLARE(componentWiseMultiply, R"( |
| template <typename T, int Cols, int Rows> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, Cols, Rows> ANGLE_componentWiseMultiply(metal::matrix<T, Cols, Rows> a, metal::matrix<T, Cols, Rows> b) |
| { |
| for (size_t col = 0; col < Cols; ++col) |
| { |
| a[col] *= b[col]; |
| } |
| return a; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(outerProduct, R"( |
| template <typename T, int M, int N> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, N, M> ANGLE_outerProduct(metal::vec<T, M> u, metal::vec<T, N> v) |
| { |
| metal::matrix<T, N, M> o; |
| for (size_t n = 0; n < N; ++n) |
| { |
| o[n] = u * v[n]; |
| } |
| return o; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(inverse2, R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, 2, 2> ANGLE_inverse(metal::matrix<T, 2, 2> m) |
| { |
| metal::matrix<T, 2, 2> adj; |
| adj[0][0] = m[1][1]; |
| adj[0][1] = -m[0][1]; |
| adj[1][0] = -m[1][0]; |
| adj[1][1] = m[0][0]; |
| T det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); |
| return adj * (T(1) / det); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(inverse3, R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, 3, 3> ANGLE_inverse(metal::matrix<T, 3, 3> m) |
| { |
| T a = m[1][1] * m[2][2] - m[2][1] * m[1][2]; |
| T b = m[1][0] * m[2][2]; |
| T c = m[1][2] * m[2][0]; |
| T d = m[1][0] * m[2][1]; |
| T det = m[0][0] * (a) - |
| m[0][1] * (b - c) + |
| m[0][2] * (d - m[1][1] * m[2][0]); |
| det = T(1) / det; |
| metal::matrix<T, 3, 3> minv; |
| minv[0][0] = (a) * det; |
| minv[0][1] = (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * det; |
| minv[0][2] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * det; |
| minv[1][0] = (c - b) * det; |
| minv[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * det; |
| minv[1][2] = (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * det; |
| minv[2][0] = (d - m[2][0] * m[1][1]) * det; |
| minv[2][1] = (m[2][0] * m[0][1] - m[0][0] * m[2][1]) * det; |
| minv[2][2] = (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * det; |
| return minv; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(inverse4, R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, 4, 4> ANGLE_inverse(metal::matrix<T, 4, 4> m) |
| { |
| T A2323 = m[2][2] * m[3][3] - m[2][3] * m[3][2]; |
| T A1323 = m[2][1] * m[3][3] - m[2][3] * m[3][1]; |
| T A1223 = m[2][1] * m[3][2] - m[2][2] * m[3][1]; |
| T A0323 = m[2][0] * m[3][3] - m[2][3] * m[3][0]; |
| T A0223 = m[2][0] * m[3][2] - m[2][2] * m[3][0]; |
| T A0123 = m[2][0] * m[3][1] - m[2][1] * m[3][0]; |
| T A2313 = m[1][2] * m[3][3] - m[1][3] * m[3][2]; |
| T A1313 = m[1][1] * m[3][3] - m[1][3] * m[3][1]; |
| T A1213 = m[1][1] * m[3][2] - m[1][2] * m[3][1]; |
| T A2312 = m[1][2] * m[2][3] - m[1][3] * m[2][2]; |
| T A1312 = m[1][1] * m[2][3] - m[1][3] * m[2][1]; |
| T A1212 = m[1][1] * m[2][2] - m[1][2] * m[2][1]; |
| T A0313 = m[1][0] * m[3][3] - m[1][3] * m[3][0]; |
| T A0213 = m[1][0] * m[3][2] - m[1][2] * m[3][0]; |
| T A0312 = m[1][0] * m[2][3] - m[1][3] * m[2][0]; |
| T A0212 = m[1][0] * m[2][2] - m[1][2] * m[2][0]; |
| T A0113 = m[1][0] * m[3][1] - m[1][1] * m[3][0]; |
| T A0112 = m[1][0] * m[2][1] - m[1][1] * m[2][0]; |
| T a = m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223; |
| T b = m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223; |
| T c = m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123; |
| T d = m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123; |
| T det = m[0][0] * ( a ) |
| - m[0][1] * ( b ) |
| + m[0][2] * ( c ) |
| - m[0][3] * ( d ); |
| det = T(1) / det; |
| metal::matrix<T, 4, 4> im; |
| im[0][0] = det * ( a ); |
| im[0][1] = det * - ( m[0][1] * A2323 - m[0][2] * A1323 + m[0][3] * A1223 ); |
| im[0][2] = det * ( m[0][1] * A2313 - m[0][2] * A1313 + m[0][3] * A1213 ); |
| im[0][3] = det * - ( m[0][1] * A2312 - m[0][2] * A1312 + m[0][3] * A1212 ); |
| im[1][0] = det * - ( b ); |
| im[1][1] = det * ( m[0][0] * A2323 - m[0][2] * A0323 + m[0][3] * A0223 ); |
| im[1][2] = det * - ( m[0][0] * A2313 - m[0][2] * A0313 + m[0][3] * A0213 ); |
| im[1][3] = det * ( m[0][0] * A2312 - m[0][2] * A0312 + m[0][3] * A0212 ); |
| im[2][0] = det * ( c ); |
| im[2][1] = det * - ( m[0][0] * A1323 - m[0][1] * A0323 + m[0][3] * A0123 ); |
| im[2][2] = det * ( m[0][0] * A1313 - m[0][1] * A0313 + m[0][3] * A0113 ); |
| im[2][3] = det * - ( m[0][0] * A1312 - m[0][1] * A0312 + m[0][3] * A0112 ); |
| im[3][0] = det * - ( d ); |
| im[3][1] = det * ( m[0][0] * A1223 - m[0][1] * A0223 + m[0][2] * A0123 ); |
| im[3][2] = det * - ( m[0][0] * A1213 - m[0][1] * A0213 + m[0][2] * A0113 ); |
| im[3][3] = det * ( m[0][0] * A1212 - m[0][1] * A0212 + m[0][2] * A0112 ); |
| return im; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(equalArray, |
| R"( |
| template <typename T, size_t N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_equal(metal::array<T, N> u, metal::array<T, N> v) |
| { |
| for(size_t i = 0; i < N; i++) |
| if (!ANGLE_equal(u[i], v[i])) return false; |
| return true; |
| } |
| )", |
| equalScalar(), |
| equalVector(), |
| equalMatrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(equalStructArray, |
| R"( |
| template <typename T, size_t N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_equalStructArray(metal::array<T, N> u, metal::array<T, N> v) |
| { |
| for(size_t i = 0; i < N; i++) |
| { |
| if (ANGLE_equal(u[i], v[i]) == false) |
| return false; |
| } |
| return true; |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(notEqualArray, |
| R"( |
| template <typename T, size_t N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_notEqual(metal::array<T, N> u, metal::array<T, N> v) |
| { |
| return !ANGLE_equal(u,v); |
| } |
| )", |
| equalArray()) |
| |
| PROGRAM_PRELUDE_DECLARE(equalScalar, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE bool ANGLE_equal(T u, T v) |
| { |
| return u == v; |
| } |
| )", |
| include_metal_math()) |
| |
| PROGRAM_PRELUDE_DECLARE(equalVector, |
| R"( |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_equal(metal::vec<T, N> u, metal::vec<T, N> v) |
| { |
| return metal::all(u == v); |
| } |
| )", |
| include_metal_math()) |
| |
| PROGRAM_PRELUDE_DECLARE(equalMatrix, |
| R"( |
| template <typename T, int C, int R> |
| ANGLE_ALWAYS_INLINE bool ANGLE_equal(metal::matrix<T, C, R> a, metal::matrix<T, C, R> b) |
| { |
| for (int c = 0; c < C; ++c) |
| { |
| if (!ANGLE_equal(a[c], b[c])) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| )", |
| equalVector()) |
| |
| PROGRAM_PRELUDE_DECLARE(notEqualMatrix, |
| R"( |
| template <typename T, int C, int R> |
| ANGLE_ALWAYS_INLINE bool ANGLE_notEqual(metal::matrix<T, C, R> u, metal::matrix<T, C, R> v) |
| { |
| return !ANGLE_equal(u, v); |
| } |
| )", |
| equalMatrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(notEqualVector, |
| R"( |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_notEqual(metal::vec<T, N> u, metal::vec<T, N> v) |
| { |
| return !ANGLE_equal(u, v); |
| } |
| )", |
| equalVector()) |
| |
| PROGRAM_PRELUDE_DECLARE(notEqualStruct, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE bool ANGLE_notEqualStruct(thread const T &a, thread const T &b) |
| { |
| return !ANGLE_equal(a, b); |
| } |
| )", |
| equalVector(), |
| equalMatrix()) |
| |
| PROGRAM_PRELUDE_DECLARE(notEqualStructArray, |
| R"( |
| template <typename T, size_t N> |
| ANGLE_ALWAYS_INLINE bool ANGLE_notEqualStructArray(metal::array<T, N> u, metal::array<T, N> v) |
| { |
| for(size_t i = 0; i < N; i++) |
| { |
| if (ANGLE_notEqualStruct(u[i], v[i])) |
| return true; |
| } |
| return false; |
| } |
| )", |
| notEqualStruct()) |
| |
| PROGRAM_PRELUDE_DECLARE(vectorElemRef, R"( |
| template <typename T, int N> |
| struct ANGLE_VectorElemRef |
| { |
| thread metal::vec<T, N> &mVec; |
| T mRef; |
| const int mIndex; |
| ~ANGLE_VectorElemRef() { mVec[mIndex] = mRef; } |
| ANGLE_VectorElemRef(thread metal::vec<T, N> &vec, int index) |
| : mVec(vec), mRef(vec[index]), mIndex(index) |
| {} |
| operator thread T &() { return mRef; } |
| }; |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE ANGLE_VectorElemRef<T, N> ANGLE_elem_ref(thread metal::vec<T, N> &vec, int index) |
| { |
| return ANGLE_VectorElemRef<T, N>(vec, index); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(swizzleRef, |
| R"( |
| template <typename T, int VN, int SN> |
| struct ANGLE_SwizzleRef |
| { |
| thread metal::vec<T, VN> &mVec; |
| metal::vec<T, SN> mRef; |
| int mIndices[SN]; |
| ~ANGLE_SwizzleRef() |
| { |
| for (int i = 0; i < SN; ++i) |
| { |
| const int j = mIndices[i]; |
| mVec[j] = mRef[i]; |
| } |
| } |
| ANGLE_SwizzleRef(thread metal::vec<T, VN> &vec, thread const int *indices) |
| : mVec(vec) |
| { |
| for (int i = 0; i < SN; ++i) |
| { |
| const int j = indices[i]; |
| mIndices[i] = j; |
| mRef[i] = mVec[j]; |
| } |
| } |
| operator thread metal::vec<T, SN> &() { return mRef; } |
| }; |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE ANGLE_VectorElemRef<T, N> ANGLE_swizzle_ref(thread metal::vec<T, N> &vec, int i0) |
| { |
| const int is[] = { i0 }; |
| return ANGLE_VectorElemRef<T, N>(vec, is); |
| } |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE ANGLE_SwizzleRef<T, N, 2> ANGLE_swizzle_ref(thread metal::vec<T, N> &vec, int i0, int i1) |
| { |
| const int is[] = { i0, i1 }; |
| return ANGLE_SwizzleRef<T, N, 2>(vec, is); |
| } |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE ANGLE_SwizzleRef<T, N, 3> ANGLE_swizzle_ref(thread metal::vec<T, N> &vec, int i0, int i1, int i2) |
| { |
| const int is[] = { i0, i1, i2 }; |
| return ANGLE_SwizzleRef<T, N, 3>(vec, is); |
| } |
| template <typename T, int N> |
| ANGLE_ALWAYS_INLINE ANGLE_SwizzleRef<T, N, 4> ANGLE_swizzle_ref(thread metal::vec<T, N> &vec, int i0, int i1, int i2, int i3) |
| { |
| const int is[] = { i0, i1, i2, i3 }; |
| return ANGLE_SwizzleRef<T, N, 4>(vec, is); |
| } |
| )", |
| vectorElemRef()) |
| |
| PROGRAM_PRELUDE_DECLARE(out, R"( |
| template <typename T> |
| struct ANGLE_Out |
| { |
| T mTemp; |
| thread T &mDest; |
| ~ANGLE_Out() { mDest = mTemp; } |
| ANGLE_Out(thread T &dest) |
| : mTemp(dest), mDest(dest) |
| {} |
| operator thread T &() { return mTemp; } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE ANGLE_Out<T> ANGLE_out(thread T &dest) |
| { |
| return ANGLE_Out<T>(dest); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(inout, R"( |
| template <typename T> |
| struct ANGLE_InOut |
| { |
| T mTemp; |
| thread T &mDest; |
| ~ANGLE_InOut() { mDest = mTemp; } |
| ANGLE_InOut(thread T &dest) |
| : mTemp(dest), mDest(dest) |
| {} |
| operator thread T &() { return mTemp; } |
| }; |
| template <typename T> |
| ANGLE_ALWAYS_INLINE ANGLE_InOut<T> ANGLE_inout(thread T &dest) |
| { |
| return ANGLE_InOut<T>(dest); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(flattenArray, R"( |
| template <typename T> |
| struct ANGLE_flatten_impl |
| { |
| static ANGLE_ALWAYS_INLINE thread T *exec(thread T &x) |
| { |
| return &x; |
| } |
| }; |
| template <typename T, size_t N> |
| struct ANGLE_flatten_impl<metal::array<T, N>> |
| { |
| static ANGLE_ALWAYS_INLINE auto exec(thread metal::array<T, N> &arr) -> T |
| { |
| return ANGLE_flatten_impl<T>::exec(arr[0]); |
| } |
| }; |
| template <typename T, size_t N> |
| ANGLE_ALWAYS_INLINE auto ANGLE_flatten(thread metal::array<T, N> &arr) -> T |
| { |
| return ANGLE_flatten_impl<T>::exec(arr[0]); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(castVector, R"( |
| template <typename T, int N1, int N2> |
| struct ANGLE_castVector {}; |
| template <typename T, int N> |
| struct ANGLE_castVector<T, N, N> |
| { |
| static ANGLE_ALWAYS_INLINE metal::vec<T, N> exec(thread metal::vec<T, N> const &v) |
| { |
| return v; |
| } |
| }; |
| template <typename T> |
| struct ANGLE_castVector<T, 2, 3> |
| { |
| static ANGLE_ALWAYS_INLINE metal::vec<T, 2> exec(thread metal::vec<T, 3> const &v) |
| { |
| return v.xy; |
| } |
| }; |
| template <typename T> |
| struct ANGLE_castVector<T, 2, 4> |
| { |
| static ANGLE_ALWAYS_INLINE metal::vec<T, 2> exec(thread metal::vec<T, 4> const &v) |
| { |
| return v.xy; |
| } |
| }; |
| template <typename T> |
| struct ANGLE_castVector<T, 3, 4> |
| { |
| static ANGLE_ALWAYS_INLINE metal::vec<T, 3> exec(thread metal::vec<T, 4> const &v) |
| { |
| return as_type<metal::vec<T, 3>>(v); |
| } |
| }; |
| template <int N1, int N2, typename T> |
| ANGLE_ALWAYS_INLINE metal::vec<T, N1> ANGLE_cast(thread metal::vec<T, N2> const &v) |
| { |
| return ANGLE_castVector<T, N1, N2>::exec(v); |
| } |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(castMatrix, |
| R"( |
| template <typename T, int C1, int R1, int C2, int R2, typename Enable = void> |
| struct ANGLE_castMatrix |
| { |
| static ANGLE_ALWAYS_INLINE metal::matrix<T, C1, R1> exec(thread metal::matrix<T, C2, R2> const &m2) |
| { |
| metal::matrix<T, C1, R1> m1; |
| const int MinC = C1 <= C2 ? C1 : C2; |
| const int MinR = R1 <= R2 ? R1 : R2; |
| for (int c = 0; c < MinC; ++c) |
| { |
| for (int r = 0; r < MinR; ++r) |
| { |
| m1[c][r] = m2[c][r]; |
| } |
| for (int r = R2; r < R1; ++r) |
| { |
| m1[c][r] = c == r ? T(1) : T(0); |
| } |
| } |
| for (int c = C2; c < C1; ++c) |
| { |
| for (int r = 0; r < R1; ++r) |
| { |
| m1[c][r] = c == r ? T(1) : T(0); |
| } |
| } |
| return m1; |
| } |
| }; |
| template <typename T, int C1, int R1, int C2, int R2> |
| struct ANGLE_castMatrix<T, C1, R1, C2, R2, ANGLE_enable_if_t<(C1 <= C2 && R1 <= R2)>> |
| { |
| static ANGLE_ALWAYS_INLINE metal::matrix<T, C1, R1> exec(thread metal::matrix<T, C2, R2> const &m2) |
| { |
| metal::matrix<T, C1, R1> m1; |
| for (size_t c = 0; c < C1; ++c) |
| { |
| m1[c] = ANGLE_cast<R1>(m2[c]); |
| } |
| return m1; |
| } |
| }; |
| template <int C1, int R1, int C2, int R2, typename T> |
| ANGLE_ALWAYS_INLINE metal::matrix<T, C1, R1> ANGLE_cast(thread metal::matrix<T, C2, R2> const &m) |
| { |
| return ANGLE_castMatrix<T, C1, R1, C2, R2>::exec(m); |
| }; |
| )", |
| enable_if(), |
| castVector()) |
| |
| PROGRAM_PRELUDE_DECLARE(tensor, R"( |
| template <typename T, size_t... DS> |
| struct ANGLE_tensor_traits; |
| template <typename T, size_t D> |
| struct ANGLE_tensor_traits<T, D> |
| { |
| enum : size_t { outer_dim = D }; |
| using inner_type = T; |
| using outer_type = inner_type[D]; |
| }; |
| template <typename T, size_t D, size_t... DS> |
| struct ANGLE_tensor_traits<T, D, DS...> |
| { |
| enum : size_t { outer_dim = D }; |
| using inner_type = typename ANGLE_tensor_traits<T, DS...>::outer_type; |
| using outer_type = inner_type[D]; |
| }; |
| template <size_t D, typename value_type_, typename inner_type_> |
| struct ANGLE_tensor_impl |
| { |
| enum : size_t { outer_dim = D }; |
| using value_type = value_type_; |
| using inner_type = inner_type_; |
| using outer_type = inner_type[D]; |
| outer_type _data; |
| ANGLE_ALWAYS_INLINE size_t size() const { return outer_dim; } |
| ANGLE_ALWAYS_INLINE inner_type &operator[](size_t i) { return _data[i]; } |
| ANGLE_ALWAYS_INLINE const inner_type &operator[](size_t i) const { return _data[i]; } |
| }; |
| template <typename T, size_t... DS> |
| using ANGLE_tensor = ANGLE_tensor_impl< |
| ANGLE_tensor_traits<T, DS...>::outer_dim, |
| T, |
| typename ANGLE_tensor_traits<T, DS...>::inner_type>; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(gradient, |
| R"( |
| template <int N> |
| struct ANGLE_gradient_traits; |
| template <> |
| struct ANGLE_gradient_traits<2> { using type = metal::gradient2d; }; |
| template <> |
| struct ANGLE_gradient_traits<3> { using type = metal::gradient3d; }; |
| |
| template <int N> |
| using ANGLE_gradient = typename ANGLE_gradient_traits<N>::type; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(writeSampleMask, |
| R"( |
| ANGLE_ALWAYS_INLINE void ANGLE_writeSampleMask(const uint mask, |
| thread uint& gl_SampleMask) |
| { |
| if (ANGLECoverageMaskEnabled) |
| { |
| gl_SampleMask = as_type<int>(mask); |
| } |
| } |
| )", |
| functionConstants()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureEnv, |
| R"( |
| template <typename T> |
| struct ANGLE_TextureEnv |
| { |
| thread T *texture; |
| thread metal::sampler *sampler; |
| }; |
| )") |
| |
| // Note: for the time being, names must match those in TranslatorMetal. |
| PROGRAM_PRELUDE_DECLARE(functionConstants, |
| R"( |
| #define ANGLE_SAMPLE_COMPARE_GRADIENT_INDEX 0 |
| #define ANGLE_SAMPLE_COMPARE_LOD_INDEX 1 |
| #define ANGLE_RASTERIZATION_DISCARD_INDEX 2 |
| #define ANGLE_COVERAGE_MASK_ENABLED_INDEX 3 |
| |
| constant bool ANGLEUseSampleCompareGradient [[function_constant(ANGLE_SAMPLE_COMPARE_GRADIENT_INDEX)]]; |
| constant bool ANGLEUseSampleCompareLod [[function_constant(ANGLE_SAMPLE_COMPARE_LOD_INDEX)]]; |
| constant bool ANGLERasterizerDisabled [[function_constant(ANGLE_RASTERIZATION_DISCARD_INDEX)]]; |
| constant bool ANGLECoverageMaskEnabled [[function_constant(ANGLE_COVERAGE_MASK_ENABLED_INDEX)]]; |
| )") |
| |
| PROGRAM_PRELUDE_DECLARE(texelFetch, |
| R"( |
| #define ANGLE_texelFetch(env, ...) ANGLE_texelFetch_impl(*env.texture, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetch_impl( |
| thread Texture &texture, |
| metal::int2 const coord, |
| uint level) |
| { |
| return texture.read(uint2(coord), level); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetch_impl( |
| thread Texture &texture, |
| metal::int3 const coord, |
| uint level) |
| { |
| return texture.read(uint3(coord), level); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetch_impl( |
| thread metal::texture2d_array<T> &texture, |
| metal::int3 const coord, |
| uint level) |
| { |
| return texture.read(uint2(coord.xy), uint(coord.z), level); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texelFetchOffset, |
| R"( |
| #define ANGLE_texelFetchOffset(env, ...) ANGLE_texelFetchOffset_impl(*env.texture, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetchOffset_impl( |
| thread Texture &texture, |
| metal::int2 const coord, |
| uint level, |
| metal::int2 const offset) |
| { |
| return texture.read(uint2(coord + offset), level); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetchOffset_impl( |
| thread Texture &texture, |
| metal::int3 const coord, |
| uint level, |
| metal::int3 const offset) |
| { |
| return texture.read(uint3(coord + offset), level); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texelFetchOffset_impl( |
| thread metal::texture2d_array<T> &texture, |
| metal::int3 const coord, |
| uint level, |
| metal::int2 const offset) |
| { |
| return texture.read(uint2(coord.xy + offset), uint(coord.z), level); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture, |
| R"( |
| #define ANGLE_texture(env, ...) ANGLE_texture_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_generic_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_generic_float2_float, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_generic_float3, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_generic_float3_float, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depth2d_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample_compare(sampler, coord.xy, coord.z); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depth2d_float3_float, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias) |
| { |
| return texture.sample_compare(sampler, coord.xy, coord.z, metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depth2darray_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depth2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord) |
| { |
| return texture.sample_compare(sampler, coord.xy, uint(metal::round(coord.z)), coord.w); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depth2darray_float4_float, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depth2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float compare) |
| { |
| return texture.sample_compare(sampler, coord.xyz, uint(metal::round(coord.w)), compare); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depthcube_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depthcube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord) |
| { |
| return texture.sample_compare(sampler, coord.xyz, coord.w); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_depthcube_float4_float, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::depthcube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias) |
| { |
| return texture.sample_compare(sampler, coord.xyz, coord.w, metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_texture2darray_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z))); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_texture2darray_float3_float, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_texture2darray_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord) |
| { |
| return texture.sample(sampler, coord.xyz, uint(metal::round(coord.w))); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture_texture2darray_float4_float, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord.xyz, uint(metal::round(coord.w)), metal::bias(bias)); |
| } |
| )", |
| texture()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture1DLod, |
| R"( |
| #define ANGLE_texture1DLod(env, ...) ANGLE_texture1DLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture1DLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| float const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture1DProj, |
| R"( |
| #define ANGLE_texture1DProj(env, ...) ANGLE_texture1DProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture1DProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.x/coord.y, metal::bias(bias)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture1DProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.x/coord.w, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture1DProjLod, |
| R"( |
| #define ANGLE_texture1DProjLod(env, ...) ANGLE_texture1DProjLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture1DProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.x/coord.y, metal::level(level)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture1DProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.x/coord.w, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2D, |
| R"( |
| #define ANGLE_texture2D(env, ...) ANGLE_texture2D_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2D_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2D_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2D_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2D_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2DRect, |
| R"( |
| #define ANGLE_texture2DRect(env, ...) ANGLE_texture2DRect_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DRect_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2DLod, |
| R"( |
| #define ANGLE_texture2DLod(env, ...) ANGLE_texture2DLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2DProj, |
| R"( |
| #define ANGLE_texture2DProj(env, ...) ANGLE_texture2DProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::bias(bias)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2DRectProj, |
| R"( |
| #define ANGLE_texture2DRectProj(env, ...) ANGLE_texture2DRectProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DRectProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample(sampler, coord.xy/coord.z); |
| } |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DRectProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord) |
| { |
| return texture.sample(sampler, coord.xy/coord.w); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture2DProjLod, |
| R"( |
| #define ANGLE_texture2DProjLod(env, ...) ANGLE_texture2DProjLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::level(level)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture2DProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture3DLod, |
| R"( |
| #define ANGLE_texture3DLod(env, ...) ANGLE_texture3DLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture3DLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture3DProj, |
| R"( |
| #define ANGLE_texture3DProj(env, ...) ANGLE_texture3DProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture3DProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(texture3DProjLod, |
| R"( |
| #define ANGLE_texture3DProjLod(env, ...) ANGLE_texture3DProjLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_texture3DProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureCube, |
| R"( |
| #define ANGLE_textureCube(env, ...) ANGLE_textureCube_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureCube_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord) |
| { |
| return texture.sample(sampler, coord); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureCube_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureCubeLod, |
| R"( |
| #define ANGLE_textureCubeLod(env, ...) ANGLE_textureCubeLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureCubeLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureCubeProj, |
| R"( |
| #define ANGLE_textureCubeProj(env, ...) ANGLE_textureCubeProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureCubeProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureCubeProjLod, |
| R"( |
| #define ANGLE_textureCubeProjLod(env, ...) ANGLE_textureCubeProjLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureCubeProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::level(level)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad, |
| R"( |
| #define ANGLE_textureGrad(env, ...) ANGLE_textureGrad_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_generic_floatN_floatN_floatN, |
| R"( |
| template <typename Texture, int N> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::vec<float, N> const coord, |
| metal::vec<float, N> const dPdx, |
| metal::vec<float, N> const dPdy) |
| { |
| return texture.sample(sampler, coord, ANGLE_gradient<N>(dPdx, dPdy)); |
| } |
| )", |
| gradient(), |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_generic_float3_float2_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy)); |
| } |
| )", |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_generic_float4_float2_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy)); |
| } |
| )", |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_depth2d_float3_float2_float2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, coord.z, metal::gradient2d(dPdx, dPdy))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, metal::gradient2d(dPdx, dPdy)) > coord.z); |
| } |
| } |
| )", |
| functionConstants(), |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_depth2darray_float4_float2_float2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread metal::depth2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, uint(metal::round(coord.z)), coord.w, metal::gradient2d(dPdx, dPdy))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy)) > coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_depthcube_float4_float3_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread metal::depthcube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xyz, coord.w, metal::gradientcube(dPdx, dPdy))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xyz, metal::gradientcube(dPdx, dPdy)) > coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGrad_texturecube_float3_float3_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGrad_impl( |
| thread metal::texturecube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy) |
| { |
| return texture.sample(sampler, coord, metal::gradientcube(dPdx, dPdy)); |
| } |
| )", |
| textureGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset, |
| R"( |
| #define ANGLE_textureGradOffset(env, ...) ANGLE_textureGradOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_generic_floatN_floatN_floatN_intN, |
| R"( |
| template <typename Texture, int N> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::vec<float, N> const coord, |
| metal::vec<float, N> const dPdx, |
| metal::vec<float, N> const dPdy, |
| metal::vec<int, N> const offset) |
| { |
| return texture.sample(sampler, coord, ANGLE_gradient<N>(dPdx, dPdy), offset); |
| } |
| )", |
| gradient(), |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_generic_float3_float2_float2_int2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy), offset); |
| } |
| )", |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_generic_float4_float2_float2_int2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy), offset); |
| } |
| )", |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_depth2d_float3_float2_float2_int2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| metal::int2 const offset) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, coord.z, metal::gradient2d(dPdx, dPdy), offset)); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, metal::gradient2d(dPdx, dPdy), offset) > coord.z); |
| } |
| } |
| )", |
| functionConstants(), |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_depth2darray_float4_float2_float2_int2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread metal::depth2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| metal::int2 const offset) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, uint(metal::round(coord.z)), coord.w, metal::gradient2d(dPdx, dPdy), offset)); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::gradient2d(dPdx, dPdy), offset) > coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_depthcube_float4_float3_float3_int3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread metal::depthcube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy, |
| metal::int3 const offset) |
| { |
| return texture.sample_compare(sampler, coord.xyz, coord.w, metal::gradientcube(dPdx, dPdy), offset); |
| } |
| )", |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureGradOffset_texturecube_float3_float3_float3_int3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureGradOffset_impl( |
| thread metal::texturecube<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy, |
| metal::int3 const offset) |
| { |
| return texture.sample(sampler, coord, metal::gradientcube(dPdx, dPdy), offset); |
| } |
| )", |
| textureGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod, |
| R"( |
| #define ANGLE_textureLod(env, ...) ANGLE_textureLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod_generic_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod_generic_float3, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord, metal::level(level)); |
| } |
| )", |
| textureLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod_depth2d_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLod_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| if (ANGLEUseSampleCompareLod) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, coord.z, metal::level(level))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, metal::level(level)) > coord.z); |
| } |
| } |
| )", |
| functionConstants(), |
| textureLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod_texture2darray_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLod_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::level(level)); |
| } |
| )", |
| textureLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLod_texture2darray_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLod_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xyz, uint(metal::round(coord.w)), metal::level(level)); |
| } |
| )", |
| textureLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureLodOffset, |
| R"( |
| #define ANGLE_textureLodOffset(env, ...) ANGLE_textureLodOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLodOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| float level, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord, metal::level(level), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLodOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level, |
| metal::int3 const offset) |
| { |
| return texture.sample(sampler, coord, metal::level(level), offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLodOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level, |
| int2 const offset) |
| { |
| if (ANGLEUseSampleCompareLod) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy, coord.z, metal::level(level), offset)); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy, metal::level(level), offset) > coord.z); |
| } |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLodOffset_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::level(level), offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureLodOffset_impl( |
| thread metal::texture2d_array<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level, |
| metal::int3 const offset) |
| { |
| return texture.sample(sampler, coord.xyz, uint(metal::round(coord.w)), metal::level(level), offset); |
| } |
| )", |
| functionConstants(), |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureOffset, |
| R"( |
| #define ANGLE_textureOffset(env, ...) ANGLE_textureOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord, offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float2 const coord, |
| metal::int2 const offset, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int2 const offset, |
| float bias) |
| { |
| return texture.sample(sampler, coord.xy, uint(metal::round(coord.z)), metal::bias(bias), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int3 const offset) |
| { |
| return texture.sample(sampler, coord, offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int3 const offset, |
| float bias) |
| { |
| return texture.sample(sampler, coord, metal::bias(bias), offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int2 const offset) |
| { |
| return texture.sample_compare(sampler, coord.xy, coord.z, offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::int2 const offset, |
| float bias) |
| { |
| return texture.sample_compare(sampler, coord.xy, coord.z, metal::bias(bias), offset); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProj, |
| R"( |
| #define ANGLE_textureProj(env, ...) ANGLE_textureProj_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::bias(bias)); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProj_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::bias(bias)); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProj_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::bias(bias)); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGrad, |
| R"( |
| #define ANGLE_textureProjGrad(env, ...) ANGLE_textureProjGrad_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGrad_generic_float3_float2_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGrad_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::gradient2d(dPdx, dPdy)); |
| } |
| )", |
| textureProjGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGrad_generic_float4_float2_float2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGrad_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::gradient2d(dPdx, dPdy)); |
| } |
| )", |
| textureProjGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGrad_depth2d_float4_float2_float2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGrad_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy/coord.w, coord.z/coord.w, metal::gradient2d(dPdx, dPdy))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy/coord.w, metal::gradient2d(dPdx, dPdy)) > coord.z/coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureProjGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGrad_texture3d_float4_float3_float3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGrad_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::gradient3d(dPdx, dPdy)); |
| } |
| )", |
| textureProjGrad()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGradOffset, |
| R"( |
| #define ANGLE_textureProjGradOffset(env, ...) ANGLE_textureProjGradOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGradOffset_generic_float3_float2_float2_int2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGradOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::gradient2d(dPdx, dPdy), offset); |
| } |
| )", |
| textureProjGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGradOffset_generic_float4_float2_float2_int2, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGradOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::gradient2d(dPdx, dPdy), offset); |
| } |
| )", |
| textureProjGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGradOffset_depth2d_float4_float2_float2_int2, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGradOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float2 const dPdx, |
| metal::float2 const dPdy, |
| int2 const offset) |
| { |
| if (ANGLEUseSampleCompareGradient) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy/coord.w, coord.z/coord.w, metal::gradient2d(dPdx, dPdy), offset)); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy/coord.w, metal::gradient2d(dPdx, dPdy), offset) > coord.z/coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureProjGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjGradOffset_texture3d_float4_float3_float3_int3, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjGradOffset_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| metal::float3 const dPdx, |
| metal::float3 const dPdy, |
| int3 const offset) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::gradient3d(dPdx, dPdy), offset); |
| } |
| )", |
| textureProjGradOffset()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLod, |
| R"( |
| #define ANGLE_textureProjLod(env, ...) ANGLE_textureProjLod_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLod_generic_float3, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::level(level)); |
| } |
| )", |
| textureProjLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLod_generic_float4, |
| R"( |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLod_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::level(level)); |
| } |
| )", |
| textureProjLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLod_depth2d_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLod_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| if (ANGLEUseSampleCompareLod) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy/coord.w, coord.z/coord.w, metal::level(level))); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy/coord.w, metal::level(level)) > coord.z/coord.w); |
| } |
| } |
| )", |
| functionConstants(), |
| textureProjLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLod_texture3d_float4, |
| R"( |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLod_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::level(level)); |
| } |
| )", |
| textureProjLod()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjLodOffset, |
| R"( |
| #define ANGLE_textureProjLodOffset(env, ...) ANGLE_textureProjLodOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLodOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| float level, |
| int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::level(level), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLodOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level, |
| int2 const offset) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::level(level), offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLodOffset_impl( |
| thread metal::depth2d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level, |
| int2 const offset) |
| { |
| if (ANGLEUseSampleCompareLod) |
| { |
| return static_cast<T>(texture.sample_compare(sampler, coord.xy/coord.w, coord.z/coord.w, metal::level(level), offset)); |
| } |
| else |
| { |
| return static_cast<T>(texture.sample(sampler, coord.xy/coord.w, metal::level(level), offset) > coord.z/coord.w); |
| } |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjLodOffset_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| float level, |
| int3 const offset) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::level(level), offset); |
| } |
| )", |
| functionConstants(), |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureProjOffset, |
| R"( |
| #define ANGLE_textureProjOffset(env, ...) ANGLE_textureProjOffset_impl(*env.texture, *env.sampler, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float3 const coord, |
| int2 const offset, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.z, metal::bias(bias), offset); |
| } |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjOffset_impl( |
| thread Texture &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| int2 const offset, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xy/coord.w, metal::bias(bias), offset); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureProjOffset_impl( |
| thread metal::texture3d<T> &texture, |
| thread metal::sampler const &sampler, |
| metal::float4 const coord, |
| int3 const offset, |
| float bias = 0) |
| { |
| return texture.sample(sampler, coord.xyz/coord.w, metal::bias(bias), offset); |
| } |
| )", |
| textureEnv()) |
| |
| PROGRAM_PRELUDE_DECLARE(textureSize, |
| R"( |
| #define ANGLE_textureSize(env, ...) ANGLE_textureSize_impl(*env.texture, __VA_ARGS__) |
| |
| template <typename Texture> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureSize_impl( |
| thread Texture &texture, |
| int level) |
| { |
| return int2(texture.get_width(uint(level)), texture.get_height(uint(level))); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureSize_impl( |
| thread metal::texture3d<T> &texture, |
| int level) |
| { |
| return int3(texture.get_width(uint(level)), texture.get_height(uint(level)), texture.get_depth(uint(level))); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureSize_impl( |
| thread metal::depth2d_array<T> &texture, |
| int level) |
| { |
| return int3(texture.get_width(uint(level)), texture.get_height(uint(level)), texture.get_array_size()); |
| } |
| |
| template <typename T> |
| ANGLE_ALWAYS_INLINE auto ANGLE_textureSize_impl( |
| thread metal::texture2d_array<T> &texture, |
| int level) |
| { |
| return int3(texture.get_width(uint(level)), texture.get_height(uint(level)), texture.get_array_size()); |
| } |
| )", |
| textureEnv()) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // Returned Name is valid for as long as `buffer` is still alive. |
| // Returns false if no template args exist. |
| // Returns false if buffer is not large enough. |
| // |
| // Example: |
| // "foo<1,2>" --> "foo<>" |
| static std::pair<Name, bool> MaskTemplateArgs(const Name &name, size_t bufferSize, char *buffer) |
| { |
| const char *begin = name.rawName().data(); |
| const char *end = strchr(begin, '<'); |
| if (!end) |
| { |
| return {{}, false}; |
| } |
| size_t n = end - begin; |
| if (n + 3 > bufferSize) |
| { |
| return {{}, false}; |
| } |
| for (size_t i = 0; i < n; ++i) |
| { |
| buffer[i] = begin[i]; |
| } |
| buffer[n + 0] = '<'; |
| buffer[n + 1] = '>'; |
| buffer[n + 2] = '\0'; |
| return {Name(buffer, name.symbolType()), true}; |
| } |
| |
| ProgramPrelude::FuncToEmitter ProgramPrelude::BuildFuncToEmitter() |
| { |
| #define EMIT_METHOD(method) \ |
| [](ProgramPrelude &pp, const TFunction &) -> void { return pp.method(); } |
| FuncToEmitter map; |
| |
| auto put = [&](Name name, FuncEmitter emitter) { |
| FuncEmitter &dest = map[name]; |
| ASSERT(!dest); |
| dest = emitter; |
| }; |
| |
| auto putAngle = [&](const char *nameStr, FuncEmitter emitter) { |
| Name name(nameStr, SymbolType::AngleInternal); |
| put(name, emitter); |
| }; |
| |
| auto putBuiltIn = [&](const char *nameStr, FuncEmitter emitter) { |
| Name name(nameStr, SymbolType::BuiltIn); |
| put(name, emitter); |
| }; |
| |
| putAngle("addressof", EMIT_METHOD(addressof)); |
| putAngle("cast<>", EMIT_METHOD(castMatrix)); |
| putAngle("elem_ref", EMIT_METHOD(vectorElemRef)); |
| putAngle("flatten", EMIT_METHOD(flattenArray)); |
| putAngle("inout", EMIT_METHOD(inout)); |
| putAngle("out", EMIT_METHOD(out)); |
| putAngle("swizzle_ref", EMIT_METHOD(swizzleRef)); |
| |
| putBuiltIn("texelFetch", EMIT_METHOD(texelFetch)); |
| putBuiltIn("texelFetchOffset", EMIT_METHOD(texelFetchOffset)); |
| putBuiltIn("texture", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| const bool bias = func.getParamCount() >= 3; |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| if (bias) |
| { |
| return pp.texture_depth2d_float3_float(); |
| } |
| return pp.texture_depth2d_float3(); |
| } |
| } |
| if (textureName.beginsWith("metal::depthcube<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| if (bias) |
| { |
| return pp.texture_depthcube_float4_float(); |
| } |
| return pp.texture_depthcube_float4(); |
| } |
| } |
| if (textureName.beginsWith("metal::depth2d_array<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| if (bias) |
| { |
| return pp.texture_depth2darray_float4_float(); |
| } |
| return pp.texture_depth2darray_float4(); |
| } |
| } |
| if (textureName.beginsWith("metal::texture2d_array<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| if (bias) |
| { |
| return pp.texture_texture2darray_float3_float(); |
| } |
| return pp.texture_texture2darray_float3(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| if (bias) |
| { |
| return pp.texture_texture2darray_float4_float(); |
| } |
| return pp.texture_texture2darray_float4(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 2) |
| { |
| if (bias) |
| { |
| return pp.texture_generic_float2_float(); |
| } |
| return pp.texture_generic_float2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| if (bias) |
| { |
| return pp.texture_generic_float3_float(); |
| } |
| return pp.texture_generic_float3(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("texture1DLod", EMIT_METHOD(texture1DLod)); |
| putBuiltIn("texture1DProj", EMIT_METHOD(texture1DProj)); |
| putBuiltIn("texture1DProjLod", EMIT_METHOD(texture1DProjLod)); |
| putBuiltIn("texture2D", EMIT_METHOD(texture2D)); |
| putBuiltIn("texture2DLod", EMIT_METHOD(texture2DLod)); |
| putBuiltIn("texture2DProj", EMIT_METHOD(texture2DProj)); |
| putBuiltIn("texture2DRect", EMIT_METHOD(texture2DRect)); |
| putBuiltIn("texture2DRectProj", EMIT_METHOD(texture2DRectProj)); |
| putBuiltIn("texture3DLod", EMIT_METHOD(texture3DLod)); |
| putBuiltIn("texture3DProj", EMIT_METHOD(texture3DProj)); |
| putBuiltIn("texture3DProjLod", EMIT_METHOD(texture3DProjLod)); |
| putBuiltIn("textureCube", EMIT_METHOD(textureCube)); |
| putBuiltIn("textureCubeLod", EMIT_METHOD(textureCubeLod)); |
| putBuiltIn("textureCubeProj", EMIT_METHOD(textureCubeProj)); |
| putBuiltIn("textureCubeProjLod", EMIT_METHOD(textureCubeProjLod)); |
| putBuiltIn("texture2DProjLod", EMIT_METHOD(texture2DProjLod)); |
| putBuiltIn("textureGrad", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| const TType &dPdx = func.getParam(2)->getType(); |
| const int dPdxN = dPdx.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureGrad_depth2d_float3_float2_float2(); |
| } |
| } |
| if (textureName.beginsWith("metal::depth2d_array<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureGrad_depth2darray_float4_float2_float2(); |
| } |
| } |
| if (textureName.beginsWith("metal::depthcube<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 3) |
| { |
| return pp.textureGrad_depthcube_float4_float3_float3(); |
| } |
| } |
| if (textureName.beginsWith("metal::texturecube<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 3) |
| { |
| return pp.textureGrad_texturecube_float3_float3_float3(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureGrad_generic_float3_float2_float2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureGrad_generic_float4_float2_float2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == dPdxN) |
| { |
| return pp.textureGrad_generic_floatN_floatN_floatN(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureGradOffset", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| const TType &dPdx = func.getParam(2)->getType(); |
| const int dPdxN = dPdx.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureGradOffset_depth2d_float3_float2_float2_int2(); |
| } |
| } |
| if (textureName.beginsWith("metal::depth2d_array<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureGradOffset_depth2darray_float4_float2_float2_int2(); |
| } |
| } |
| if (textureName.beginsWith("metal::depthcube<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 3) |
| { |
| return pp.textureGradOffset_depthcube_float4_float3_float3_int3(); |
| } |
| } |
| if (textureName.beginsWith("metal::texturecube<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 3) |
| { |
| return pp.textureGradOffset_texturecube_float3_float3_float3_int3(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureGradOffset_generic_float3_float2_float2_int2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureGradOffset_generic_float4_float2_float2_int2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == dPdxN) |
| { |
| return pp.textureGradOffset_generic_floatN_floatN_floatN_intN(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureLod", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| return pp.textureLod_depth2d_float3(); |
| } |
| } |
| if (textureName.beginsWith("metal::texture2d_array<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| return pp.textureLod_texture2darray_float3(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| return pp.textureLod_texture2darray_float4(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 2) |
| { |
| return pp.textureLod_generic_float2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| return pp.textureLod_generic_float3(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureLodOffset", EMIT_METHOD(textureLodOffset)); |
| putBuiltIn("textureOffset", EMIT_METHOD(textureOffset)); |
| putBuiltIn("textureProj", EMIT_METHOD(textureProj)); |
| putBuiltIn("textureProjGrad", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| const TType &dPdx = func.getParam(2)->getType(); |
| const int dPdxN = dPdx.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureProjGrad_depth2d_float4_float2_float2(); |
| } |
| } |
| if (textureName.beginsWith("metal::texture3d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 3) |
| { |
| return pp.textureProjGrad_texture3d_float4_float3_float3(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureProjGrad_generic_float3_float2_float2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureProjGrad_generic_float4_float2_float2(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureProjGradOffset", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| const TType &dPdx = func.getParam(2)->getType(); |
| const int dPdxN = dPdx.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureProjGradOffset_depth2d_float4_float2_float2_int2(); |
| } |
| } |
| if (textureName.beginsWith("metal::texture3d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 3) |
| { |
| return pp.textureProjGradOffset_texture3d_float4_float3_float3_int3(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3 && dPdxN == 2) |
| { |
| return pp.textureProjGradOffset_generic_float3_float2_float2_int2(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4 && dPdxN == 2) |
| { |
| return pp.textureProjGradOffset_generic_float4_float2_float2_int2(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureProjLod", [](ProgramPrelude &pp, const TFunction &func) { |
| const ImmutableString textureName = |
| GetTextureTypeName(func.getParam(0)->getType().getBasicType()).rawName(); |
| const TType &coord = func.getParam(1)->getType(); |
| const TBasicType coordBasic = coord.getBasicType(); |
| const int coordN = coord.getNominalSize(); |
| if (textureName.beginsWith("metal::depth2d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| return pp.textureProjLod_depth2d_float4(); |
| } |
| } |
| if (textureName.beginsWith("metal::texture3d<")) |
| { |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| return pp.textureProjLod_texture3d_float4(); |
| } |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 3) |
| { |
| return pp.textureProjLod_generic_float3(); |
| } |
| if (coordBasic == TBasicType::EbtFloat && coordN == 4) |
| { |
| return pp.textureProjLod_generic_float4(); |
| } |
| UNIMPLEMENTED(); |
| }); |
| putBuiltIn("textureProjLodOffset", EMIT_METHOD(textureProjLodOffset)); |
| putBuiltIn("textureProjOffset", EMIT_METHOD(textureProjOffset)); |
| putBuiltIn("textureSize", EMIT_METHOD(textureSize)); |
| |
| return map; |
| |
| #undef EMIT_METHOD |
| } |
| |
| void ProgramPrelude::visitOperator(TOperator op, |
| const TFunction *func, |
| const TType *argType0, |
| const TType *argType1) |
| { |
| switch (op) |
| { |
| case TOperator::EOpRadians: |
| radians(); |
| break; |
| case TOperator::EOpDegrees: |
| degrees(); |
| break; |
| case TOperator::EOpAtan: |
| atan(); |
| break; |
| case TOperator::EOpMod: |
| mod(); |
| break; |
| case TOperator::EOpRefract: |
| refract(); |
| break; |
| case TOperator::EOpDistance: |
| distance(); |
| break; |
| case TOperator::EOpLength: |
| length(); |
| break; |
| case TOperator::EOpDot: |
| dot(); |
| break; |
| case TOperator::EOpNormalize: |
| normalize(); |
| break; |
| case TOperator::EOpFaceforward: |
| faceforward(); |
| break; |
| case TOperator::EOpReflect: |
| reflect(); |
| break; |
| |
| case TOperator::EOpSin: |
| case TOperator::EOpCos: |
| case TOperator::EOpTan: |
| case TOperator::EOpAsin: |
| case TOperator::EOpAcos: |
| case TOperator::EOpSinh: |
| case TOperator::EOpCosh: |
| case TOperator::EOpTanh: |
| case TOperator::EOpAsinh: |
| case TOperator::EOpAcosh: |
| case TOperator::EOpAtanh: |
| case TOperator::EOpAbs: |
| case TOperator::EOpFma: |
| case TOperator::EOpPow: |
| case TOperator::EOpExp: |
| case TOperator::EOpExp2: |
| case TOperator::EOpLog: |
| case TOperator::EOpLog2: |
| case TOperator::EOpSqrt: |
| case TOperator::EOpFloor: |
| case TOperator::EOpTrunc: |
| case TOperator::EOpCeil: |
| case TOperator::EOpFract: |
| case TOperator::EOpRound: |
| case TOperator::EOpRoundEven: |
| case TOperator::EOpModf: |
| case TOperator::EOpLdexp: |
| case TOperator::EOpFrexp: |
| case TOperator::EOpInversesqrt: |
| include_metal_math(); |
| break; |
| |
| case TOperator::EOpEqual: |
| if (argType0->isVector() && argType1->isVector()) |
| { |
| equalVector(); |
| } |
| // Even if Arg0 is a vector or matrix, it could also be an array. |
| if (argType0->isArray() && argType1->isArray()) |
| { |
| equalArray(); |
| } |
| if (argType0->getStruct() && argType1->getStruct() && argType0->isArray() && |
| argType1->isArray()) |
| { |
| equalStructArray(); |
| } |
| if (argType0->isMatrix() && argType1->isMatrix()) |
| { |
| equalMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpNotEqual: |
| if (argType0->isVector() && argType1->isVector()) |
| { |
| notEqualVector(); |
| } |
| else if (argType0->getStruct() && argType1->getStruct()) |
| { |
| notEqualStruct(); |
| } |
| // Same as above. |
| if (argType0->isArray() && argType1->isArray()) |
| { |
| notEqualArray(); |
| } |
| if (argType0->getStruct() && argType1->getStruct() && argType0->isArray() && |
| argType1->isArray()) |
| { |
| notEqualStructArray(); |
| } |
| if (argType0->isMatrix() && argType1->isMatrix()) |
| { |
| notEqualMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpCross: |
| include_metal_geometric(); |
| break; |
| |
| case TOperator::EOpSign: |
| sign(); |
| break; |
| |
| case TOperator::EOpClamp: |
| case TOperator::EOpMin: |
| case TOperator::EOpMax: |
| case TOperator::EOpMix: |
| case TOperator::EOpStep: |
| case TOperator::EOpSmoothstep: |
| include_metal_common(); |
| break; |
| |
| case TOperator::EOpAll: |
| case TOperator::EOpAny: |
| case TOperator::EOpIsnan: |
| case TOperator::EOpIsinf: |
| include_metal_relational(); |
| break; |
| |
| case TOperator::EOpDFdx: |
| case TOperator::EOpDFdy: |
| case TOperator::EOpFwidth: |
| include_metal_graphics(); |
| break; |
| |
| case TOperator::EOpTranspose: |
| case TOperator::EOpDeterminant: |
| include_metal_matrix(); |
| break; |
| |
| case TOperator::EOpAdd: |
| if (argType0->isMatrix() && argType1->isScalar()) |
| { |
| addMatrixScalar(); |
| } |
| break; |
| |
| case TOperator::EOpAddAssign: |
| if (argType0->isMatrix() && argType1->isScalar()) |
| { |
| addMatrixScalarAssign(); |
| } |
| break; |
| |
| case TOperator::EOpSub: |
| if (argType0->isMatrix() && argType1->isScalar()) |
| { |
| subMatrixScalar(); |
| } |
| break; |
| |
| case TOperator::EOpSubAssign: |
| if (argType0->isMatrix() && argType1->isScalar()) |
| { |
| subMatrixScalarAssign(); |
| } |
| break; |
| |
| case TOperator::EOpDiv: |
| if (argType0->isMatrix()) |
| { |
| if (argType1->isMatrix()) |
| { |
| componentWiseDivide(); |
| } |
| else if (argType1->isScalar()) |
| { |
| divMatrixScalar(); |
| } |
| } |
| break; |
| |
| case TOperator::EOpDivAssign: |
| if (argType0->isMatrix() && argType1->isMatrix()) |
| { |
| componentWiseDivideAssign(); |
| } |
| break; |
| |
| case TOperator::EOpMatrixCompMult: |
| if (argType0->isMatrix() && argType1->isMatrix()) |
| { |
| componentWiseMultiply(); |
| } |
| break; |
| |
| case TOperator::EOpOuterProduct: |
| outerProduct(); |
| break; |
| |
| case TOperator::EOpInverse: |
| switch (argType0->getCols()) |
| { |
| case 2: |
| inverse2(); |
| break; |
| case 3: |
| inverse3(); |
| break; |
| case 4: |
| inverse4(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| |
| case TOperator::EOpMatrixTimesMatrixAssign: |
| matmulAssign(); |
| break; |
| |
| case TOperator::EOpPreIncrement: |
| if (argType0->isMatrix()) |
| { |
| preIncrementMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpPostIncrement: |
| if (argType0->isMatrix()) |
| { |
| postIncrementMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpPreDecrement: |
| if (argType0->isMatrix()) |
| { |
| preDecrementMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpPostDecrement: |
| if (argType0->isMatrix()) |
| { |
| postDecrementMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpNegative: |
| if (argType0->isMatrix()) |
| { |
| negateMatrix(); |
| } |
| break; |
| |
| case TOperator::EOpComma: |
| case TOperator::EOpAssign: |
| case TOperator::EOpInitialize: |
| case TOperator::EOpMulAssign: |
| case TOperator::EOpIModAssign: |
| case TOperator::EOpBitShiftLeftAssign: |
| case TOperator::EOpBitShiftRightAssign: |
| case TOperator::EOpBitwiseAndAssign: |
| case TOperator::EOpBitwiseXorAssign: |
| case TOperator::EOpBitwiseOrAssign: |
| case TOperator::EOpMul: |
| case TOperator::EOpIMod: |
| case TOperator::EOpBitShiftLeft: |
| case TOperator::EOpBitShiftRight: |
| case TOperator::EOpBitwiseAnd: |
| case TOperator::EOpBitwiseXor: |
| case TOperator::EOpBitwiseOr: |
| case TOperator::EOpLessThan: |
| case TOperator::EOpGreaterThan: |
| case TOperator::EOpLessThanEqual: |
| case TOperator::EOpGreaterThanEqual: |
| case TOperator::EOpLessThanComponentWise: |
| case TOperator::EOpLessThanEqualComponentWise: |
| case TOperator::EOpGreaterThanEqualComponentWise: |
| case TOperator::EOpGreaterThanComponentWise: |
| case TOperator::EOpLogicalOr: |
| case TOperator::EOpLogicalXor: |
| case TOperator::EOpLogicalAnd: |
| case TOperator::EOpPositive: |
| case TOperator::EOpLogicalNot: |
| case TOperator::EOpNotComponentWise: |
| case TOperator::EOpBitwiseNot: |
| case TOperator::EOpVectorTimesScalarAssign: |
| case TOperator::EOpVectorTimesMatrixAssign: |
| case TOperator::EOpMatrixTimesScalarAssign: |
| case TOperator::EOpVectorTimesScalar: |
| case TOperator::EOpVectorTimesMatrix: |
| case TOperator::EOpMatrixTimesVector: |
| case TOperator::EOpMatrixTimesScalar: |
| case TOperator::EOpMatrixTimesMatrix: |
| case TOperator::EOpReturn: |
| case TOperator::EOpBreak: |
| case TOperator::EOpContinue: |
| case TOperator::EOpEqualComponentWise: |
| case TOperator::EOpNotEqualComponentWise: |
| case TOperator::EOpIndexDirect: |
| case TOperator::EOpIndexIndirect: |
| case TOperator::EOpIndexDirectStruct: |
| case TOperator::EOpIndexDirectInterfaceBlock: |
| case TOperator::EOpFloatBitsToInt: |
| case TOperator::EOpIntBitsToFloat: |
| case TOperator::EOpUintBitsToFloat: |
| case TOperator::EOpFloatBitsToUint: |
| case TOperator::EOpNull: |
| // do nothing |
| break; |
| |
| case TOperator::EOpKill: |
| include_metal_graphics(); |
| break; |
| |
| case TOperator::EOpPackUnorm2x16: |
| case TOperator::EOpPackSnorm2x16: |
| case TOperator::EOpPackUnorm4x8: |
| case TOperator::EOpPackSnorm4x8: |
| case TOperator::EOpUnpackSnorm2x16: |
| case TOperator::EOpUnpackUnorm2x16: |
| case TOperator::EOpUnpackUnorm4x8: |
| case TOperator::EOpUnpackSnorm4x8: |
| include_metal_pack(); |
| break; |
| |
| case TOperator::EOpPackHalf2x16: |
| pack_half_2x16(); |
| break; |
| case TOperator::EOpUnpackHalf2x16: |
| unpack_half_2x16(); |
| break; |
| |
| case TOperator::EOpBitfieldExtract: |
| case TOperator::EOpBitfieldInsert: |
| case TOperator::EOpBitfieldReverse: |
| case TOperator::EOpBitCount: |
| case TOperator::EOpFindLSB: |
| case TOperator::EOpFindMSB: |
| case TOperator::EOpUaddCarry: |
| case TOperator::EOpUsubBorrow: |
| case TOperator::EOpUmulExtended: |
| case TOperator::EOpImulExtended: |
| case TOperator::EOpBarrier: |
| case TOperator::EOpMemoryBarrier: |
| case TOperator::EOpMemoryBarrierAtomicCounter: |
| case TOperator::EOpMemoryBarrierBuffer: |
| case TOperator::EOpMemoryBarrierImage: |
| case TOperator::EOpMemoryBarrierShared: |
| case TOperator::EOpGroupMemoryBarrier: |
| case TOperator::EOpAtomicAdd: |
| case TOperator::EOpAtomicMin: |
| case TOperator::EOpAtomicMax: |
| case TOperator::EOpAtomicAnd: |
| case TOperator::EOpAtomicOr: |
| case TOperator::EOpAtomicXor: |
| case TOperator::EOpAtomicExchange: |
| case TOperator::EOpAtomicCompSwap: |
| case TOperator::EOpEmitVertex: |
| case TOperator::EOpEndPrimitive: |
| case TOperator::EOpFtransform: |
| case TOperator::EOpPackDouble2x32: |
| case TOperator::EOpUnpackDouble2x32: |
| case TOperator::EOpArrayLength: |
| UNIMPLEMENTED(); |
| break; |
| |
| case TOperator::EOpConstruct: |
| ASSERT(!func); |
| break; |
| |
| case TOperator::EOpCallFunctionInAST: |
| case TOperator::EOpCallInternalRawFunction: |
| default: |
| ASSERT(func); |
| if (mHandled.insert(func).second) |
| { |
| const Name name(*func); |
| const auto end = mFuncToEmitter.end(); |
| auto iter = mFuncToEmitter.find(name); |
| if (iter == end) |
| { |
| char buffer[32]; |
| auto mask = MaskTemplateArgs(name, sizeof(buffer), buffer); |
| if (mask.second) |
| { |
| iter = mFuncToEmitter.find(mask.first); |
| } |
| } |
| if (iter != end) |
| { |
| const auto &emitter = iter->second; |
| emitter(*this, *func); |
| } |
| } |
| break; |
| } |
| } |
| |
| void ProgramPrelude::visitVariable(const Name &name, const TType &type) |
| { |
| if (const TStructure *s = type.getStruct()) |
| { |
| const Name typeName(*s); |
| if (typeName.beginsWith(Name("TextureEnv<"))) |
| { |
| textureEnv(); |
| } |
| } |
| else |
| { |
| if (name.rawName() == sh::mtl::kRasterizerDiscardEnabledConstName) |
| { |
| functionConstants(); |
| } |
| } |
| } |
| |
| void ProgramPrelude::visitVariable(const TVariable &var) |
| { |
| if (mHandled.insert(&var).second) |
| { |
| visitVariable(Name(var), var.getType()); |
| } |
| } |
| |
| void ProgramPrelude::visitStructure(const TStructure &s) |
| { |
| if (mHandled.insert(&s).second) |
| { |
| for (const TField *field : s.fields()) |
| { |
| const TType &type = *field->type(); |
| visitVariable(Name(*field), type); |
| } |
| } |
| } |
| |
| bool ProgramPrelude::visitBinary(Visit visit, TIntermBinary *node) |
| { |
| const TType &leftType = node->getLeft()->getType(); |
| const TType &rightType = node->getRight()->getType(); |
| visitOperator(node->getOp(), nullptr, &leftType, &rightType); |
| return true; |
| } |
| |
| bool ProgramPrelude::visitUnary(Visit visit, TIntermUnary *node) |
| { |
| const TType &argType = node->getOperand()->getType(); |
| visitOperator(node->getOp(), nullptr, &argType); |
| return true; |
| } |
| |
| bool ProgramPrelude::visitAggregate(Visit visit, TIntermAggregate *node) |
| { |
| const size_t argCount = node->getChildCount(); |
| |
| auto getArgType = [node, argCount](size_t index) -> const TType & { |
| ASSERT(index < argCount); |
| TIntermTyped *arg = node->getChildNode(index)->getAsTyped(); |
| ASSERT(arg); |
| return arg->getType(); |
| }; |
| |
| const TFunction *func = node->getFunction(); |
| |
| switch (node->getChildCount()) |
| { |
| case 0: |
| { |
| visitOperator(node->getOp(), func, nullptr); |
| } |
| break; |
| |
| case 1: |
| { |
| const TType &argType0 = getArgType(0); |
| visitOperator(node->getOp(), func, &argType0); |
| } |
| break; |
| |
| case 2: |
| { |
| const TType &argType0 = getArgType(0); |
| const TType &argType1 = getArgType(1); |
| visitOperator(node->getOp(), func, &argType0, &argType1); |
| } |
| break; |
| |
| default: |
| { |
| const TType &argType0 = getArgType(0); |
| const TType &argType1 = getArgType(1); |
| visitOperator(node->getOp(), func, &argType0, &argType1); |
| } |
| break; |
| } |
| |
| return true; |
| } |
| |
| bool ProgramPrelude::visitDeclaration(Visit, TIntermDeclaration *node) |
| { |
| Declaration decl = ViewDeclaration(*node); |
| const TType &type = decl.symbol.getType(); |
| if (type.isStructSpecifier()) |
| { |
| const TStructure *s = type.getStruct(); |
| ASSERT(s); |
| visitStructure(*s); |
| } |
| return true; |
| } |
| |
| void ProgramPrelude::visitSymbol(TIntermSymbol *node) |
| { |
| visitVariable(node->variable()); |
| } |
| |
| bool sh::EmitProgramPrelude(TIntermBlock &root, TInfoSinkBase &out, const ProgramPreludeConfig &ppc) |
| { |
| ProgramPrelude programPrelude(out, ppc); |
| root.traverse(&programPrelude); |
| return true; |
| } |