//===-- metal_texture ------------------------------------------------------===//
// Copyright (c) 2014 Apple Inc. All rights reserved
//===----------------------------------------------------------------------===//

#ifndef __METAL_TEXTURE
#define __METAL_TEXTURE

typedef device struct _texture_1d_t *texture_1d_t;
typedef device struct _texture_1d_array_t *texture_1d_array_t;
typedef device struct _texture_2d_t *texture_2d_t;
typedef device struct _texture_2d_array_t *texture_2d_array_t;
typedef device struct _texture_3d_t *texture_3d_t;
typedef device struct _texture_cube_t *texture_cube_t;
typedef device struct _texture_2d_ms_t *texture_2d_ms_t;
typedef device struct _depth_2d_t *depth_2d_t;
typedef device struct _depth_2d_array_t *depth_2d_array_t;
typedef device struct _depth_cube_t *depth_cube_t;
typedef device struct _depth_2d_ms_t *depth_2d_ms_t;

typedef __constant struct _sampler_t *sampler_t;

#define _AIR_DEPTH_FLOAT32 1

namespace metal {
  // 2.5 Textures
  enum class access { sample, read, write };

  template<typename T, access a = access::sample> struct texture1d;
  template<typename T, access a = access::sample> struct texture1d_array;
  template<typename T, access a = access::sample> struct texture2d;
  template<typename T, access a = access::sample> struct texture2d_array;
  template<typename T, access a = access::sample> struct texture3d;
  template<typename T, access a = access::sample> struct texturecube;
  template<typename T, access a = access::read> struct texture2d_ms;

  enum class depth_format { depth_float = _AIR_DEPTH_FLOAT32 };
  template<typename T, access a = access::sample, depth_format d = depth_format::depth_float> struct depth2d;
  template<typename T, access a = access::sample, depth_format d = depth_format::depth_float> struct depth2d_array;
  template<typename T, access a = access::sample, depth_format d = depth_format::depth_float> struct depthcube;
  template<typename T, access a = access::read, depth_format d = depth_format::depth_float> struct depth2d_ms;

  // 2.6 Samplers
  enum class coord { normalized = 0, pixel = 1 };
  enum class filter { nearest = 0, linear = 1 };
  enum class s_address { clamp_to_zero = 0, clamp_to_edge = 1,
                         repeat = 2, mirrored_repeat = 3 };
  enum class t_address { clamp_to_zero = 0, clamp_to_edge = 1,
                         repeat = 2, mirrored_repeat = 3 };
  enum class r_address { clamp_to_zero = 0, clamp_to_edge = 1,
                         repeat = 2, mirrored_repeat = 3 };
  enum class address { clamp_to_zero = 0, clamp_to_edge = 1,
                       repeat = 2, mirrored_repeat = 3 };
  enum class mip_filter { none = 0, nearest = 1, linear = 2 };
  enum class compare_func { none = 0, less = 1, less_equal = 2, greater = 3,
                            greater_equal = 4, equal = 5, not_equal = 6 };
  enum class mag_filter   { nearest = 0, linear = 1 };
  enum class min_filter   { nearest = 0, linear = 1 };

  struct sampler {
    void operator=(thread  const sampler&) = delete;
    void operator&() = delete;
    void operator,(thread  const sampler&) = delete;

    METAL_FUNC constexpr sampler() : val((1ULL << marker_bit) |
                                        ((unsigned __metal_internal_int64_t) address::clamp_to_edge << s_address_base) |
                                        ((unsigned __metal_internal_int64_t) address::clamp_to_edge << t_address_base) |
                                        ((unsigned __metal_internal_int64_t) address::clamp_to_edge << r_address_base)) {}

   template<typename... Ts>
    METAL_FUNC constexpr sampler(coord mode, Ts... t) 
     : val(((unsigned __metal_internal_int64_t) mode << normalized_base) |
           ((~normalized_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(s_address mode, Ts... t)
      : val(((unsigned __metal_internal_int64_t) mode << s_address_base) |
            ((~s_address_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(t_address mode, Ts... t)
      : val(((unsigned __metal_internal_int64_t) mode << t_address_base) |
            ((~t_address_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(r_address mode, Ts... t)
      : val(((unsigned __metal_internal_int64_t) mode << r_address_base) |
            ((~r_address_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(address mode, Ts... t)
      : val(((unsigned __metal_internal_int64_t) mode << s_address_base) |
            ((unsigned __metal_internal_int64_t) mode << t_address_base) |
            ((unsigned __metal_internal_int64_t) mode << r_address_base) |
            ((~(s_address_mask | t_address_mask | r_address_mask)) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(mag_filter mode, Ts... t)
     : val(((unsigned __metal_internal_int64_t) mode << mag_filter_base) |
           ((~mag_filter_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(min_filter mode, Ts... t)
     : val(((unsigned __metal_internal_int64_t) mode << min_filter_base) |
           ((~min_filter_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(filter mode, Ts... t)
      : val(((unsigned __metal_internal_int64_t) mode << mag_filter_base) |
            ((unsigned __metal_internal_int64_t) mode << min_filter_base) |
            ((~(mag_filter_mask | min_filter_mask)) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(mip_filter mode, Ts... t)
     : val(((unsigned __metal_internal_int64_t) mode << mip_filter_base) |
           ((~mip_filter_mask) & sampler(t...))) {}

    template<typename... Ts>
    METAL_FUNC constexpr sampler(compare_func mode, Ts... t)
     : val(((unsigned __metal_internal_int64_t) mode << compare_base) |
           ((~compare_mask) & sampler(t...))) {}

    private :

    unsigned __metal_internal_int64_t val;

    // Values to help calculate offsets.
    constexpr const static constant unsigned s_address_bits  =  3; // bits[2:0]   s_address mode
    constexpr const static constant unsigned t_address_bits  =  3; // bits[5:3]   t_address mode
    constexpr const static constant unsigned r_address_bits  =  3; // bits[8:6]   r_address mode
    constexpr const static constant unsigned mag_filter_bits =  2; // bits[10:9]  mag filter mode
    constexpr const static constant unsigned min_filter_bits =  2; // bits[12:11] min filter mode
    constexpr const static constant unsigned mip_filter_bits =  2; // bits[14:13] mip filter mode
    constexpr const static constant unsigned normalized_bits =  1; // bits[15:15] normalized mode
    constexpr const static constant unsigned compare_bits    =  4; // bits[19:16] compare func
                                                                   // bits[62:20] reserved
    constexpr const static constant unsigned marker_bit      = 63; // bits[63:63] constant sampler indicator.

    constexpr const static constant unsigned s_address_base  = 0;
    constexpr const static constant unsigned t_address_base  = s_address_base  + s_address_bits;
    constexpr const static constant unsigned r_address_base  = t_address_base  + t_address_bits;
    constexpr const static constant unsigned mag_filter_base = r_address_base  + r_address_bits;
    constexpr const static constant unsigned min_filter_base = mag_filter_base + mag_filter_bits;
    constexpr const static constant unsigned mip_filter_base = min_filter_base + min_filter_bits;
    constexpr const static constant unsigned normalized_base = mip_filter_base + mip_filter_bits;
    constexpr const static constant unsigned compare_base    = normalized_base + normalized_bits;
    constexpr const static constant unsigned end_base        = compare_base    + compare_bits;

    constexpr const static constant unsigned __metal_internal_int64_t s_address_mask  = ((1 << s_address_bits ) - 1) << s_address_base;
    constexpr const static constant unsigned __metal_internal_int64_t t_address_mask  = ((1 << t_address_bits ) - 1) << t_address_base;
    constexpr const static constant unsigned __metal_internal_int64_t r_address_mask  = ((1 << r_address_bits ) - 1) << r_address_base;
    constexpr const static constant unsigned __metal_internal_int64_t mag_filter_mask = ((1 << mag_filter_bits) - 1) << mag_filter_base;
    constexpr const static constant unsigned __metal_internal_int64_t min_filter_mask = ((1 << min_filter_bits) - 1) << min_filter_base;
    constexpr const static constant unsigned __metal_internal_int64_t mip_filter_mask = ((1 << mip_filter_bits) - 1) << mip_filter_base;
    constexpr const static constant unsigned __metal_internal_int64_t normalized_mask = ((1 << normalized_bits) - 1) << normalized_base;
    constexpr const static constant unsigned __metal_internal_int64_t compare_mask    = ((1 << compare_bits   ) - 1) << compare_base;

    constexpr operator unsigned __metal_internal_int64_t() const {
      return val;
    }
  };

  // 5.10 Texture Functions

  // 5.10.1 1D Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod);
  template<typename T> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<T,4> color, uint lod);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.v4f32");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<float,4> color, uint lod) __asm("air.write_texture_1d.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.s.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<int,4> color, uint lod) __asm("air.write_texture_1d.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.u.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<uint,4> color, uint lod) __asm("air.write_texture_1d.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.v4f16");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<half,4> color, uint lod) __asm("air.write_texture_1d.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.s.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<short,4> color, uint lod) __asm("air.write_texture_1d.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_1d(texture_1d_t tex, sampler smp, float coord, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_1d(texture_1d_t tex, uint coord, uint lod) __asm("air.read_texture_1d.u.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_1d(texture_1d_t tex, uint coord, vec<ushort,4> color, uint lod) __asm("air.write_texture_1d.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_1d(texture_1d_t tex, uint lod) __asm("air.get_width_texture_1d");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_1d(texture_1d_t tex) __asm("air.get_num_mip_levels_texture_1d");

  template<typename T, access a>
  struct texture1d {
    static_assert(_is_valid_type_texture_1d<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texture1d<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture1d<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float coord) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_1d<T>(t, s, coord, false, 0, false, 0);
    }
    METAL_FUNC vec<T,4> read(uint coord, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid texture access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_texture_1d<T>(t, coord, lod);
    }
    METAL_FUNC void write(vec<T,4> color, uint coord, uint lod = 0) {
      static_assert(a == access::write, "Invalid texture access qualifier.  Must use access::write as the access qualifier");
      return _air_write_texture_1d<T>(t, coord, color, lod);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_1d(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_1d(t);
    }
    private:
    texture_1d_t t;
  };

  // 5.10.2 1D Texture Array
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod);
  template<typename T> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<T,4> color, uint lod);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.v4f32");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<float,4> color, uint lod) __asm("air.write_texture_1d_array.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.s.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<int,4> color, uint lod) __asm("air.write_texture_1d_array.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.u.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<uint,4> color, uint lod) __asm("air.write_texture_1d_array.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.v4f16");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<half,4> color, uint lod) __asm("air.write_texture_1d_array.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.s.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<short,4> color, uint lod) __asm("air.write_texture_1d_array.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_1d_array<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_1d_array(texture_1d_array_t tex, sampler smp, float coord, uint array, bool offset_en, int offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_1d_array.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, uint lod) __asm("air.read_texture_1d_array.u.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_1d_array(texture_1d_array_t tex, uint coord, uint array, vec<ushort,4> color, uint lod) __asm("air.write_texture_1d_array.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_1d_array(texture_1d_array_t tex, uint lod) __asm("air.get_width_texture_1d_array");
  METAL_INTERNAL int _air_get_array_size_texture_1d_array(texture_1d_array_t tex) __asm("air.get_array_size_texture_1d_array");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_1d_array(texture_1d_array_t tex) __asm("air.get_num_mip_levels_texture_1d_array");

  template<typename T, access a>
  struct texture1d_array {
    static_assert(_is_valid_type_texture_1d_array<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texture1d_array<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture1d_array<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float coord, uint array) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_1d_array<T>(t, s, coord, array, false, 0, false, 0);
    }
    METAL_FUNC vec<T,4> read(uint coord, uint array, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid texture access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_texture_1d_array<T>(t, coord, array, lod);
    }
    METAL_FUNC void write(vec<T,4> color, uint coord, uint array, uint lod = 0) {
      static_assert(a == access::write, "Invalid texture access qualifier.  Must use access::write as the access qualifier");
      return _air_write_texture_1d_array<T>(t, coord, array, color, lod);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_1d_array(t, lod);
    }
    METAL_FUNC uint get_array_size() const {
      return _air_get_array_size_texture_1d_array(t);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_1d_array(t);
    }
    private:
    texture_1d_array_t t;
  };

  // 5.10.3 2D Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod);
  template<typename T> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<T,4> color, uint lod);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_2d<float>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.v4f32");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<float,4> color, uint lod) __asm("air.write_texture_2d.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_2d<int>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.s.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<int,4> color, uint lod) __asm("air.write_texture_2d.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_2d<uint>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.u.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<uint,4> color, uint lod) __asm("air.write_texture_2d.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_2d<half>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.v4f16");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<half,4> color, uint lod) __asm("air.write_texture_2d.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_2d<short>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.s.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<short,4> color, uint lod) __asm("air.write_texture_2d.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_2d<ushort>(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_2d_grad(texture_2d_t tex, sampler smp, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_grad.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_2d(texture_2d_t tex, uint2 coord, uint lod) __asm("air.read_texture_2d.u.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_2d(texture_2d_t tex, uint2 coord, vec<ushort,4> color, uint lod) __asm("air.write_texture_2d.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_gather_texture_2d(texture_2d_t tex, sampler smp, float2 coord, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_2d(texture_2d_t tex, uint lod) __asm("air.get_width_texture_2d");
  METAL_INTERNAL int _air_get_height_texture_2d(texture_2d_t tex, uint lod) __asm("air.get_height_texture_2d");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_2d(texture_2d_t tex) __asm("air.get_num_mip_levels_texture_2d");

  struct bias {
    METAL_FUNC constexpr bias(float value) : value(value) {}
    float value;
  };
  struct level {
    METAL_FUNC constexpr level(float lod) : lod(lod) {}
    float lod;
  };
  struct gradient2d {
    METAL_FUNC gradient2d(float2 dPdx, float2 dPdy) : dPdx(dPdx), dPdy(dPdy) {}
    float2 dPdx;
    float2 dPdy;
  };
  enum class component { x, y, z, w };

  template<typename T, access a>
  struct texture2d {
    static_assert(_is_valid_type_texture_2d<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texture2d<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture2d<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d<T>(t, s, coord, true, offset, false, 0);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d<T>(t, s, coord, true, offset, false, options.value);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d<T>(t, s, coord, true, offset, true, options.lod);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d_grad<T>(t, s, coord, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC vec<T,4> read(uint2 coord, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid texture access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_texture_2d<T>(t, coord, lod);
    }
    METAL_FUNC void write(vec<T,4> color, uint2 coord, uint lod = 0) {
      static_assert(a == access::write, "Invalid texture access qualifier.  Must use access::write as the access qualifier");
      return _air_write_texture_2d<T>(t, coord, color, lod);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float2 coord, int2 offset = int2(0), component c = component::x) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_texture_2d<T>(t, s, coord, true, offset, int(c));
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_2d(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_texture_2d(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_2d(t);
    }
    private :
    texture_2d_t t;
  };

  // 5.10.4 2D Texture Array
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod);
  template<typename T> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<T,4> color, uint lod);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.v4f32");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<float,4> color, uint lod) __asm("air.write_texture_2d_array.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.s.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<int,4> color, uint lod) __asm("air.write_texture_2d_array.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.u.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<uint,4> color, uint lod) __asm("air.write_texture_2d_array.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.v4f16");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<half,4> color, uint lod) __asm("air.write_texture_2d_array.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.s.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<short,4> color, uint lod) __asm("air.write_texture_2d_array.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_array<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_2d_array.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_2d_array_grad(texture_2d_array_t tex, sampler smp, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_texture_2d_array_grad.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, uint lod) __asm("air.read_texture_2d_array.u.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_2d_array(texture_2d_array_t tex, uint2 coord, uint array, vec<ushort,4> color, uint lod) __asm("air.write_texture_2d_array.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_gather_texture_2d_array(texture_2d_array_t tex, sampler smp, float2 coord, uint array, bool offset_en, int2 offset, int component) __asm("air.gather_texture_2d_array.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_2d_array(texture_2d_array_t tex, uint lod) __asm("air.get_width_texture_2d_array");
  METAL_INTERNAL int _air_get_height_texture_2d_array(texture_2d_array_t tex, uint lod) __asm("air.get_height_texture_2d_array");
  METAL_INTERNAL int _air_get_array_size_texture_2d_array(texture_2d_array_t tex) __asm("air.get_array_size_texture_2d_array");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_2d_array(texture_2d_array_t tex) __asm("air.get_num_mip_levels_texture_2d_array");

  template<typename T, access a>
  struct texture2d_array {
    static_assert(_is_valid_type_texture_2d_array<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texture2d_array<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture2d_array<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, uint array, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d_array<T>(t, s, coord, array, true, offset, false, 0);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, uint array, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d_array<T>(t, s, coord, array, true, offset, false, options.value);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, uint array, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d_array<T>(t, s, coord, array, true, offset, true, options.lod);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float2 coord, uint array, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_2d_array_grad<T>(t, s, coord, array, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC vec<T,4> read(uint2 coord, uint array, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid texture access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_texture_2d_array<T>(t, coord, array, lod);
    }
    METAL_FUNC void write(vec<T,4> color, uint2 coord, uint array, uint lod = 0) {
      static_assert(a == access::write, "Invalid texture access qualifier.  Must use access::write as the access qualifier");
      return _air_write_texture_2d_array<T>(t, coord, array, color, lod);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float2 coord, uint array, int2 offset = int2(0), component c = component::x) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_texture_2d_array<T>(t, s, coord, array, true, offset, int(c));
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_2d_array(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_texture_2d_array(t, lod);
    }
    METAL_FUNC uint get_array_size() const {
      return _air_get_array_size_texture_2d_array(t);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_2d_array(t);
    }
    private:
    texture_2d_array_t t;
  };

  // 5.10.5 3D Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset);
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod);
  template<typename T> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<T,4> color, uint lod);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.v4f32");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<float,4> color, uint lod) __asm("air.write_texture_3d.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.s.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<int,4> color, uint lod) __asm("air.write_texture_3d.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.u.v4i32");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<uint,4> color, uint lod) __asm("air.write_texture_3d.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.v4f16");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<half,4> color, uint lod) __asm("air.write_texture_3d.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.s.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<short,4> color, uint lod) __asm("air.write_texture_3d.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_3d<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_3d(texture_3d_t tex, sampler smp, float3 coord, bool offset_en, int3 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_3d.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_3d_grad(texture_3d_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy, bool offset_en, int3 offset) __asm("air.sample_texture_3d_grad.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_3d(texture_3d_t tex, uint3 coord, uint lod) __asm("air.read_texture_3d.u.v4i16");
  template<> METAL_INTERNAL void _air_write_texture_3d(texture_3d_t tex, uint3 coord, vec<ushort,4> color, uint lod) __asm("air.write_texture_3d.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_3d(texture_3d_t tex, uint lod) __asm("air.get_width_texture_3d");
  METAL_INTERNAL int _air_get_height_texture_3d(texture_3d_t tex, uint lod) __asm("air.get_height_texture_3d");
  METAL_INTERNAL int _air_get_depth_texture_3d(texture_3d_t tex, uint lod) __asm("air.get_depth_texture_3d");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_3d(texture_3d_t tex) __asm("air.get_num_mip_levels_texture_3d");

  struct gradient3d {
    METAL_FUNC gradient3d(float3 dPdx, float3 dPdy) : dPdx(dPdx), dPdy(dPdy) {}
    float3 dPdx;
    float3 dPdy;
  };

  template<typename T, access a>
  struct texture3d {
    static_assert(_is_valid_type_texture_3d<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texture3d<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture3d<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, int3 offset = int3(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_3d<T>(t, s, coord, true, offset, false, 0);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, bias options, int3 offset = int3(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_3d<T>(t, s, coord, true, offset, false, options.value);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, level options, int3 offset = int3(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_3d<T>(t, s, coord, true, offset, true, options.lod);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, gradient3d options, int3 offset = int3(0)) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_3d_grad<T>(t, s, coord, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC vec<T,4> read(uint3 coord, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid texture access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_texture_3d<T>(t, coord, lod);
    }
    METAL_FUNC void write(vec<T,4> color, uint3 coord, uint lod = 0) {
      static_assert(a == access::write, "Invalid texture access qualifier.  Must use access::write as the access qualifier");
      return _air_write_texture_3d<T>(t, coord, color, lod);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_3d(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_texture_3d(t, lod);
    }
    METAL_FUNC uint get_depth(uint lod = 0) const {
      return _air_get_depth_texture_3d(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_3d(t);
    }
    private:
    texture_3d_t t;
  };

  // 5.10.6 Cube-Map Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL vec<T,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.s.v4i32");
  template<> METAL_INTERNAL vec<int,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.u.v4i32");
  template<> METAL_INTERNAL vec<uint,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.v4f16");
  template<> METAL_INTERNAL vec<half,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.s.v4i16");
  template<> METAL_INTERNAL vec<short,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_cube<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_cube(texture_cube_t tex, sampler smp, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_texture_cube.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_sample_texture_cube_grad(texture_cube_t tex, sampler smp, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_texture_cube_grad.u.v4i16");
  template<> METAL_INTERNAL vec<ushort,4> _air_gather_texture_cube(texture_cube_t tex, sampler smp, float3 coord, int component) __asm("air.gather_texture_cube.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_cube(texture_cube_t tex, uint lod) __asm("air.get_width_texture_cube");
  METAL_INTERNAL int _air_get_height_texture_cube(texture_cube_t tex, uint lod) __asm("air.get_height_texture_cube");
  METAL_INTERNAL int _air_get_num_mip_levels_texture_cube(texture_cube_t tex) __asm("air.get_num_mip_levels_texture_cube");

  struct gradientcube {
    METAL_FUNC gradientcube(float3 dPdx, float3 dPdy) : dPdx(dPdx), dPdy(dPdy) {}
    float3 dPdx;
    float3 dPdy;
  };

  template<typename T, access a>
  struct texturecube {
    static_assert(_is_valid_type_texture_cube<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    void operator=(thread  const texturecube<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texturecube<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_cube<T>(t, s, coord, false, 0);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, bias options) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_cube<T>(t, s, coord, false, options.value);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, level options) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_cube<T>(t, s, coord, true, options.lod);
    }
    METAL_FUNC vec<T,4> sample(sampler s, float3 coord, gradientcube options) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_texture_cube_grad<T>(t, s, coord, options.dPdx, options.dPdy);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float3 coord, component c = component::x) const {
      static_assert(a == access::sample, "Invalid texture access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_texture_cube<T>(t, s, coord, int(c));
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_texture_cube(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_texture_cube(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_texture_cube(t);
    }
    private:
    texture_cube_t t;
  };


  // 5.10.8 2D Multi-sampled Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms() { return false; }
  template<typename T> METAL_INTERNAL vec<T,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<float>() { return true; }
  template<> METAL_INTERNAL vec<float,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.v4f32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<int>() { return true; }
  template<> METAL_INTERNAL vec<int,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.s.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<uint>() { return true; }
  template<> METAL_INTERNAL vec<uint,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.u.v4i32");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<half>() { return true; }
  template<> METAL_INTERNAL vec<half,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.v4f16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<short>() { return true; }
  template<> METAL_INTERNAL vec<short,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.s.v4i16");
  template<> METAL_INTERNAL constexpr bool _is_valid_type_texture_2d_ms<ushort>() { return true; }
  template<> METAL_INTERNAL vec<ushort,4> _air_read_texture_2d_ms(texture_2d_ms_t tex, uint2 coord, uint sample) __asm("air.read_texture_2d_ms.u.v4i16");
  METAL_INTERNAL int _air_get_width_texture_2d_ms(texture_2d_ms_t tex) __asm("air.get_width_texture_2d_ms");
  METAL_INTERNAL int _air_get_height_texture_2d_ms(texture_2d_ms_t tex) __asm("air.get_height_texture_2d_ms");
  METAL_INTERNAL int _air_get_num_samples_texture_2d_ms(texture_2d_ms_t tex) __asm("air.get_num_samples_texture_2d_ms");

  template<typename T, access a>
  struct texture2d_ms {
    static_assert(_is_valid_type_texture_2d_ms<T>(), "Invalid type, valid types are float, int, uint, half, short, ushort");
    static_assert(a == access::read, "Invalid ms texture access qualifier.  Must use access::read as the access qualifier");
    void operator=(thread  const texture2d_ms<T, a>&) = delete;
    void operator&() = delete;
    void operator,(thread  const texture2d_ms<T, a>&) = delete;
    
    METAL_FUNC vec<T,4> read(uint2 coord, uint sample) const {
      static_assert(a == access::read, "Invalid ms texture access qualifier.  Must use access::read as the access qualifier");
      return _air_read_texture_2d_ms<T>(t, coord, sample);
    }
    METAL_FUNC uint get_width() const {
      return _air_get_width_texture_2d_ms(t);
    }
    METAL_FUNC uint get_height() const {
      return _air_get_height_texture_2d_ms(t);
    }
    METAL_FUNC uint get_num_samples() const {
      return _air_get_num_samples_texture_2d_ms(t);
    }
    private:
    texture_2d_ms_t t;
  };

  // 5.10.9 2D Depth Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d() { return false; }
  template<typename T> METAL_INTERNAL T _air_sample_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_depth_2d_grad(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_2d_grad(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL T _air_read_depth_2d(depth_2d_t tex, uint depth_format, uint2 coord, uint lod);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_compare_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, bool offset_en, int2 offset);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d<float>() { return true; }
  template<> METAL_INTERNAL float _air_sample_depth_2d<float>(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_depth_2d.f32");
  template<> METAL_INTERNAL float _air_sample_depth_2d_grad(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_depth_2d_grad.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_compare_depth_2d.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_2d_grad(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_compare_depth_2d_grad.f32");
  template<> METAL_INTERNAL float _air_read_depth_2d(depth_2d_t tex, uint depth_format, uint2 coord, uint lod) __asm("air.read_depth_2d.f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, bool offset_en, int2 offset) __asm("air.gather_depth_2d.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_compare_depth_2d(depth_2d_t tex, sampler smp, uint depth_format, float2 coord, float compare_value, bool offset_en, int2 offset) __asm("air.gather_compare_depth_2d.f32");
  METAL_INTERNAL int _air_get_width_depth_2d(depth_2d_t tex, uint lod) __asm("air.get_width_depth_2d");
  METAL_INTERNAL int _air_get_height_depth_2d(depth_2d_t tex, uint lod) __asm("air.get_height_depth_2d");
  METAL_INTERNAL int _air_get_num_mip_levels_depth_2d(depth_2d_t tex) __asm("air.get_num_mip_levels_depth_2d");

  template<typename T, access a, depth_format d>
  struct depth2d {
    static_assert(_is_valid_type_depth_2d<T>(), "Invalid type, valid types are float");
    static_assert(a == access::sample || a == access::read, "Invalid depth access qualifier.  Must use access::sample or access::read as the access qualifier");
    void operator=(thread  const depth2d<T, a, d>&) = delete;
    void operator&() = delete;
    void operator,(thread  const depth2d<T, a, d>&) = delete;
    
    METAL_FUNC T sample(sampler s, float2 coord, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d<T>(t, s, uint(d), coord, true, offset, false, 0);
    }
    METAL_FUNC T sample(sampler s, float2 coord, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d<T>(t, s, uint(d), coord, true, offset, false, options.value);
    }
    METAL_FUNC T sample(sampler s, float2 coord, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d<T>(t, s, uint(d), coord, true, offset, true, options.lod);
    }
    METAL_FUNC T sample(sampler s, float2 coord, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d_grad<T>(t, s, uint(d), coord, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, float compare_value, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d<T>(t, s, uint(d), coord, compare_value, true, offset, false, 0);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, float compare_value, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d<T>(t, s, uint(d), coord, compare_value, true, offset, false, options.value);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, float compare_value, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d<T>(t, s, uint(d), coord, compare_value, true, offset, true, options.lod);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, float compare_value, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d_grad<T>(t, s, uint(d), coord, compare_value, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC T read(uint2 coord, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid depth access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_depth_2d<T>(t, uint(d), coord, lod);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float2 coord, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_depth_2d<T>(t, s, uint(d), coord, true, offset);
    }
    METAL_FUNC vec<T,4> gather_compare(sampler s, float2 coord, float compare_value, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_compare_depth_2d<T>(t, s, uint(d), coord, compare_value, true, offset);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_depth_2d(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_depth_2d(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_depth_2d(t);
    }
    private:
    depth_2d_t t;
  };

  // 5.10.10 2D Depth Texture Array
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d_array() { return false; }
  template<typename T> METAL_INTERNAL T _air_sample_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_depth_2d_array_grad(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_2d_array_grad(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, float2 dPdx, float2 dPdy, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL T _air_read_depth_2d_array(depth_2d_array_t tex, uint depth_format, uint2 coord, uint array, uint lod);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, bool offset_en, int2 offset);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_compare_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, bool offset_en, int2 offset);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d_array<float>() { return true; }
  template<> METAL_INTERNAL float _air_sample_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_depth_2d_array.f32");
  template<> METAL_INTERNAL float _air_sample_depth_2d_array_grad(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_depth_2d_array_grad.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, bool offset_en, int2 offset, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_compare_depth_2d_array.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_2d_array_grad(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, float2 dPdx, float2 dPdy, bool offset_en, int2 offset) __asm("air.sample_compare_depth_2d_array_grad.f32");
  template<> METAL_INTERNAL float _air_read_depth_2d_array(depth_2d_array_t tex, uint depth_format, uint2 coord, uint array, uint lod) __asm("air.read_depth_2d_array.f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, bool offset_en, int2 offset) __asm("air.gather_depth_2d_array.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_compare_depth_2d_array(depth_2d_array_t tex, sampler smp, uint depth_format, float2 coord, uint array, float compare_value, bool offset_en, int2 offset) __asm("air.gather_compare_depth_2d_array.f32");
  METAL_INTERNAL int _air_get_width_depth_2d_array(depth_2d_array_t tex, uint lod) __asm("air.get_width_depth_2d_array");
  METAL_INTERNAL int _air_get_height_depth_2d_array(depth_2d_array_t tex, uint lod) __asm("air.get_height_depth_2d_array");
  METAL_INTERNAL int _air_get_array_size_depth_2d_array(depth_2d_array_t tex) __asm("air.get_array_size_depth_2d_array");
  METAL_INTERNAL int _air_get_num_mip_levels_depth_2d_array(depth_2d_array_t tex) __asm("air.get_num_mip_levels_depth_2d_array");

  template<typename T, access a, depth_format d>
  struct depth2d_array {
    static_assert(_is_valid_type_depth_2d_array<T>(), "Invalid type, valid types are float");
    static_assert(a == access::sample || a == access::read, "Invalid depth access qualifier.  Must use access::sample or access::read as the access qualifier");
    void operator=(thread  const depth2d_array<T, a, d>&) = delete;
    void operator&() = delete;
    void operator,(thread  const depth2d_array<T, a, d>&) = delete;
    
    METAL_FUNC T sample(sampler s, float2 coord, uint array, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d_array<T>(t, s, uint(d), coord, array, true, offset, false, 0);
    }
    METAL_FUNC T sample(sampler s, float2 coord, uint array, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d_array<T>(t, s, uint(d), coord, array, true, offset, false, options.value);
    }
    METAL_FUNC T sample(sampler s, float2 coord, uint array, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d_array<T>(t, s, uint(d), coord, array, true, offset, true, options.lod);
    }
    METAL_FUNC T sample(sampler s, float2 coord, uint array, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_2d_array_grad<T>(t, s, uint(d), coord, array, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, uint array, float compare_value, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d_array<T>(t, s, uint(d), coord, array, compare_value, true, offset, false, 0);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, uint array, float compare_value, bias options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d_array<T>(t, s, uint(d), coord, array, compare_value, true, offset, false, options.value);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, uint array, float compare_value, level options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d_array<T>(t, s, uint(d), coord, array, compare_value, true, offset, false, options.lod);
    }
    METAL_FUNC T sample_compare(sampler s, float2 coord, uint array, float compare_value, gradient2d options, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_2d_array_grad<T>(t, s, uint(d), coord, array, compare_value, options.dPdx, options.dPdy, true, offset);
    }
    METAL_FUNC T read(uint2 coord, uint array, uint lod = 0) const {
      static_assert(a == access::sample || a == access::read, "Invalid depth access qualifier.  Must use access::sample or access::read as the access qualifier");
      return _air_read_depth_2d_array<T>(t, uint(d), coord, array, lod);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float2 coord, uint array, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_depth_2d_array<T>(t, s, uint(d), coord, array, true, offset);
    }
    METAL_FUNC vec<T,4> gather_compare(sampler s, float2 coord, uint array, float compare_value, int2 offset = int2(0)) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_compare_depth_2d_array<T>(t, s, uint(d), coord, array, compare_value, true, offset);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_depth_2d_array(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_depth_2d_array(t, lod);
    }
    METAL_FUNC uint get_array_size() const {
      return _air_get_array_size_depth_2d_array(t);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_depth_2d_array(t);
    }
    private:
    depth_2d_array_t t;
  };

  // 5.10.11 Cube-Map Depth Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_depth_cube() { return false; }
  template<typename T> METAL_INTERNAL T _air_sample_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_depth_cube_grad(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float3 dPdx, float3 dPdy);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value, bool lod_or_bias, float lod_or_bias_value);
  template<typename T> METAL_INTERNAL T _air_sample_compare_depth_cube_grad(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value, float3 dPdx, float3 dPdy);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord);
  template<typename T> METAL_INTERNAL vec<T,4> _air_gather_compare_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_depth_cube<float>() { return true; }
  template<> METAL_INTERNAL float _air_sample_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_depth_cube.f32");
  template<> METAL_INTERNAL float _air_sample_depth_cube_grad(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float3 dPdx, float3 dPdy) __asm("air.sample_depth_cube_grad.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value, bool lod_or_bias, float lod_or_bias_value) __asm("air.sample_compare_depth_cube.f32");
  template<> METAL_INTERNAL float _air_sample_compare_depth_cube_grad(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value, float3 dPdx, float3 dPdy) __asm("air.sample_compare_depth_cube_grad.f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord) __asm("air.gather_depth_cube.v4f32");
  template<> METAL_INTERNAL vec<float,4> _air_gather_compare_depth_cube(depth_cube_t tex, sampler smp, uint depth_format, float3 coord, float compare_value) __asm("air.gather_compare_depth_cube.f32");
  METAL_INTERNAL int _air_get_width_depth_cube(depth_cube_t tex, uint lod) __asm("air.get_width_depth_cube");
  METAL_INTERNAL int _air_get_height_depth_cube(depth_cube_t tex, uint lod) __asm("air.get_height_depth_cube");
  METAL_INTERNAL int _air_get_num_mip_levels_depth_cube(depth_cube_t tex) __asm("air.get_num_mip_levels_depth_cube");

  template<typename T, access a, depth_format d>
  struct depthcube {
    static_assert(_is_valid_type_depth_cube<T>(), "Invalid type, valid types are float");
    static_assert(a == access::sample || a == access::read, "Invalid depth access qualifier.  Must use access::sample or access::read as the access qualifier");
    void operator=(thread  const depthcube<T, a, d>&) = delete;
    void operator&() = delete;
    void operator,(thread  const depthcube<T, a, d>&) = delete;
    
    METAL_FUNC T sample(sampler s, float3 coord) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_cube<T>(t, s, uint(d), coord, false, 0);
    }
    METAL_FUNC T sample(sampler s, float3 coord, bias options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_cube<T>(t, s, uint(d), coord, false, options.value);
    }
    METAL_FUNC T sample(sampler s, float3 coord, level options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_cube<T>(t, s, uint(d), coord, true, options.lod);
    }
    METAL_FUNC T sample(sampler s, float3 coord, gradientcube options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_depth_cube_grad<T>(t, s, uint(d), coord, options.dPdx, options.dPdy);
    }
    METAL_FUNC T sample_compare(sampler s, float3 coord, float compare_value) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_cube<T>(t, s, uint(d), coord, compare_value, false, 0);
    }
    METAL_FUNC T sample_compare(sampler s, float3 coord, float compare_value, bias options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_cube<T>(t, s, uint(d), coord, compare_value, false, options.value);
    }
    METAL_FUNC T sample_compare(sampler s, float3 coord, float compare_value, level options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_cube<T>(t, s, uint(d), coord, compare_value, true, options.lod);
    }
    METAL_FUNC T sample_compare(sampler s, float3 coord, float compare_value, gradientcube options) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_sample_compare_depth_cube_grad<T>(t, s, uint(d), coord, compare_value, options.dPdx, options.dPdy);
    }
    METAL_FUNC vec<T,4> gather(sampler s, float3 coord) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_depth_cube<T>(t, s, uint(d), coord);
    }
    METAL_FUNC vec<T,4> gather_compare(sampler s, float3 coord, float compare_value) const {
      static_assert(a == access::sample, "Invalid depth access qualifier.  Must use access::sample as the access qualifier");
      return _air_gather_compare_depth_cube<T>(t, s, uint(d), coord, compare_value);
    }
    METAL_FUNC uint get_width(uint lod = 0) const {
      return _air_get_width_depth_cube(t, lod);
    }
    METAL_FUNC uint get_height(uint lod = 0) const {
      return _air_get_height_depth_cube(t, lod);
    }
    METAL_FUNC uint get_num_mip_levels() const {
      return _air_get_num_mip_levels_depth_cube(t);
    }
    private:
    depth_cube_t t;
  };


  // 5.10.13 2D Multi-sampled Depth Texture
  template<typename T> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d_ms() { return false; }
  template<typename T> METAL_INTERNAL T _air_read_depth_2d_ms(depth_2d_ms_t tex, uint depth_format, uint2 coord, uint sample);

  template<> METAL_INTERNAL constexpr bool _is_valid_type_depth_2d_ms<float>() { return true; }
  template<> METAL_INTERNAL float _air_read_depth_2d_ms(depth_2d_ms_t tex, uint depth_format, uint2 coord, uint sample) __asm("air.read_depth_2d_ms.f32");
  METAL_INTERNAL int _air_get_width_depth_2d_ms(depth_2d_ms_t tex) __asm("air.get_width_depth_2d_ms");
  METAL_INTERNAL int _air_get_height_depth_2d_ms(depth_2d_ms_t tex) __asm("air.get_height_depth_2d_ms");
  METAL_INTERNAL int _air_get_num_samples_depth_2d_ms(depth_2d_ms_t tex) __asm("air.get_num_samples_depth_2d_ms");

  template<typename T, access a, depth_format d>
  struct depth2d_ms {
    static_assert(_is_valid_type_depth_2d_ms<T>(), "Invalid type, valid types are float");
    static_assert(a == access::read, "Invalid ms depth access qualifier.  Must use access::read as the access qualifier");
    void operator=(thread  const depth2d_ms<T, a, d>&) = delete;
    void operator&() = delete;
    void operator,(thread  const depth2d_ms<T, a, d>&) = delete;

    METAL_FUNC T read(uint2 coord, uint sample) const {
      static_assert(a == access::read, "Invalid ms depth access qualifier.  Must use access::read as the access qualifier");
      return _air_read_depth_2d_ms<T>(t, uint(d), coord, sample);
    }
    METAL_FUNC uint get_width() const {
      return _air_get_width_depth_2d_ms(t);
    }
    METAL_FUNC uint get_height() const {
      return _air_get_height_depth_2d_ms(t);
    }
    METAL_FUNC uint get_num_samples() const {
      return _air_get_num_samples_depth_2d_ms(t);
    }
    private:
    depth_2d_ms_t t;
  };
} // namespace metal

#endif // __METAL_TEXTURE
