/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2022 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#ifndef XENIA_UI_SHADERS_XESL_XESLI_
#define XENIA_UI_SHADERS_XESL_XESLI_

// XESL_LANGUAGE_GLSL / HLSL / MSL = 1 are expected to be defined via compiler
// arguments.

// Required GLSL extensions:
// - GL_EXT_control_flow_attributes
// - GL_EXT_samplerless_texture_functions

// For functions, it's preferable to take the identifiers here from an existing
// target language, such as GLSL or HLSL, prefixing them with xesl_, only
// modifying the names when altering (generalizing or specializing usually)
// their functionality compared to that of the original function. The preferred
// name choice from all the shading languages is the name that reflects the
// functionality the closest, especially if some languages have a narrower input
// domain (for instance, HLSL has asuint that can accept both float and int,
// while GLSL has floatBitsToUint that accepts only float - there are two
// options here, a xesl_floatBitsToUint alias, or xesl_asuint overloads, but the
// former is more precisely descriptive, so it's preferred; xesl_lerp is
// preferred over xesl_mix because the former describes how exactly the mixing
// will be performed), and / or that is the most visually consistent
// (xesl_float4 over xesl_vec4 considering it's a float vector).

#if XESL_LANGUAGE_MSL
  #include <metal_stdlib>
  using namespace metal;
#endif

// Vectors.

// Scalars for definition consistency.
#define xesl_bool bool
#define xesl_int int
#define xesl_uint uint
#define xesl_float float
#if XESL_LANGUAGE_GLSL
  #define xesl_bool2 bvec2
  #define xesl_bool3 bvec3
  #define xesl_bool4 bvec4
  #define xesl_int2 ivec2
  #define xesl_int3 ivec3
  #define xesl_int4 ivec4
  #define xesl_uint2 uvec2
  #define xesl_uint3 uvec3
  #define xesl_uint4 uvec4
  #define xesl_float2 vec2
  #define xesl_float3 vec3
  #define xesl_float4 vec4
#else
  #define xesl_bool2 bool2
  #define xesl_bool3 bool3
  #define xesl_bool4 bool4
  #define xesl_int2 int2
  #define xesl_int3 int3
  #define xesl_int4 int4
  #define xesl_uint2 uint2
  #define xesl_uint3 uint3
  #define xesl_uint4 uint4
  #define xesl_float2 float2
  #define xesl_float3 float3
  #define xesl_float4 float4
#endif  // XESL_LANGUAGE_GLSL

xesl_bool2 xesl_bool_x2(bool xesl_var_value) {
  return xesl_bool2(xesl_var_value, xesl_var_value);
}
xesl_bool3 xesl_bool_x3(bool xesl_var_value) {
  return xesl_bool3(xesl_var_value, xesl_var_value, xesl_var_value);
}
xesl_bool4 xesl_bool_x4(bool xesl_var_value) {
  return xesl_bool4(xesl_var_value, xesl_var_value, xesl_var_value,
                    xesl_var_value);
}
xesl_int2 xesl_int_x2(int xesl_var_value) {
  return xesl_int2(xesl_var_value, xesl_var_value);
}
xesl_int3 xesl_int_x3(int xesl_var_value) {
  return xesl_int3(xesl_var_value, xesl_var_value, xesl_var_value);
}
xesl_int4 xesl_int_x4(int xesl_var_value) {
  return xesl_int4(xesl_var_value, xesl_var_value, xesl_var_value,
                   xesl_var_value);
}
xesl_uint2 xesl_uint_x2(uint xesl_var_value) {
  return xesl_uint2(xesl_var_value, xesl_var_value);
}
xesl_uint3 xesl_uint_x3(uint xesl_var_value) {
  return xesl_uint3(xesl_var_value, xesl_var_value, xesl_var_value);
}
xesl_uint4 xesl_uint_x4(uint xesl_var_value) {
  return xesl_uint4(xesl_var_value, xesl_var_value, xesl_var_value,
                    xesl_var_value);
}
xesl_float2 xesl_float_x2(float xesl_var_value) {
  return xesl_float2(xesl_var_value, xesl_var_value);
}
xesl_float3 xesl_float_x3(float xesl_var_value) {
  return xesl_float3(xesl_var_value, xesl_var_value, xesl_var_value);
}
xesl_float4 xesl_float_x4(float xesl_var_value) {
  return xesl_float4(xesl_var_value, xesl_var_value, xesl_var_value,
                     xesl_var_value);
}

// Declarations.
//
// Resource binding is very different between shading languages, so any
// customizations are fine in it. All binding slots for all APIs, however,
// should be explicitly specified by the shader for ease of manual lookup and
// tweaking. They should be alphabetically ordered by the name of the target
// shading language in the argument lists (GLSL before HLSL). For readability,
// the `set=` and `binding=` specifiers, and register types and the `space`
// prefix in HLSL, are exposed to the shader, even though they're redundant.
//
// The `xesl_id_` prefix (with context-specific sub-prefixes) can be used to
// create internal derivative identifiers (such as buffer block names from
// instance names, or separate texture and sampler from a combined texture /
// sampler for languages not supporting the latter).

// Non-compute shader entry point must be declared as:
// xesl_entry_outputs_begin
//   - Linked stage outputs.
//   - Linked system stage outputs (like vertex position).
//   - System stage outputs.
// xesl_entry_outputs_end_stageInputs_begin
//   - Linked stage inputs (vertex attributes, interpolants).
// xesl_entry_stageInputs_end_bindings_begin_[stage] (vertex, pixel)
//   Everything here must be separated with xesl_entry_binding_next, with no
//   leading or trailing separators.
//   - Buffer, texture, sampler bindings.
// xesl_entry_bindings_end_inputs_begin
// (or xesl_entry_bindings_empty_end_inputs_begin if there are no bindings).
//   Everything here must be separated with xesl_entry_input_next, with no
//   leading or trailing separators.
//   - xesl_entry_input_stageInputs if any linked stage inputs are used.
//   - Linked system inputs (like pixel position).
//   - System inputs.
// xesl_entry_inputs_end_code_begin
//   - Main function code.
// xesl_entry_code_end
//
// Compute shader entry point must be declared as:
// #define xesl_localSize_x, y, z
// xesl_entry_bindings_begin_compute
//   Everything here must be separated with xesl_entry_binding_next, with no
//   leading or trailing separators.
//   - Buffer, texture, sampler bindings.
// xesl_entry_bindings_end_inputs_begin_compute
//   Everything here must be separated with xesl_entry_input_next, with no
//   leading or trailing separators.
//   - System inputs.
// xesl_entry_inputs_end_code_begin_compute
//   - Main function code.
// xesl_entry_code_end_compute
//
// Bindings are in the entry point because they are passed this way in MSL. For
// this reason, constant and storage buffer declarations are also split into the
// declaration itself and the binding (because blocks can't be passed as
// function arguments in GLSL, for instance, so they must be fully declared
// before functions referencing them in headers, for example - but in MSL, their
// structure has to be forward-declared for this purpose, and the reference to
// the binding should be passed to the function).
//
// Note that for the stage inputs / outputs, the order must be the same as in
// HLSL linkage. For this reason, the position and the fragment coordinate also
// must be after the stage inputs structure in the input list.
//
// Both input / output and binding names may be placed in the global scope in
// the target language, make sure they don't collide with anything there.
//
// In compute shaders, the total group size must not exceed 128 threads (unless
// the shader is used with the appropriate conditionals), as that's the minimum
// maxComputeWorkGroupInvocations requirement on Vulkan. 128 threads exactly is
// the recommended group size overall, especially for shaders not using the
// group functionality, as it's the maximum wave size supported by DXIL and
// SPIR-V wave operations, and there are PowerVR GPUs with 128-lane waves, so
// it provides balance between wave utilization and excess thread (and, on GPUs
// with smaller waves, wave) count if the size of the actual work domain is not
// aligned to the group size.
//
// System outputs and inputs (declared via the respective xesl_entry_output_*
// and xesl_entry_input_*):
// - Vertex shaders:
//   - out xesl_float4 xesl_Position
//   - in uint xesl_VertexID
// - Pixel shaders:
//   - in xesl_float4 xesl_FragCoord
//     xesl_FragCoord.w is 1/W if XESL_FRAG_COORD_W_IS_INVERSE, W otherwise.
// - Compute shaders:
//   - in xesl_uint3 xesl_WorkGroupID
//   - in xesl_uint3 xesl_LocalInvocationID
//   - in xesl_uint3 xesl_GlobalInvocationID
//   - in uint xesl_LocalInvocationIndex
#if XESL_LANGUAGE_GLSL
  #define xesl_entry_output(type, name, index, semantic) \
      layout(location=index) out type name;
  #define xesl_entry_output_target(type, name, index) \
      layout(location=index) out type name;
  #define xesl_Output(name) name
  #define xesl_Position gl_Position
  #define xesl_entry_stageInput(type, name, index, semantic) \
      layout(location=index) in type name;
  #define xesl_entry_bindings_end_inputs_begin_compute                         \
      layout(local_size_x=(xesl_localSize_x), local_size_y=(xesl_localSize_y), \
             local_size_z=(xesl_localSize_z)) in;
  #define xesl_StageInput(name) name
  #define xesl_VertexID (uint(gl_VertexIndex))
  #define XESL_FRAG_COORD_W_IS_INVERSE 1
  #define xesl_FragCoord gl_FragCoord
  #define xesl_WorkGroupID gl_WorkGroupID
  #define xesl_LocalInvocationID gl_LocalInvocationID
  #define xesl_GlobalInvocationID gl_GlobalInvocationID
  #define xesl_LocalInvocationIndex gl_LocalInvocationIndex
  #define xesl_entry_inputs_end_code_begin void main() {
  #define xesl_entry_inputs_end_code_begin_compute void main() {
  #define xesl_entry_return return;
#elif XESL_LANGUAGE_HLSL
  #define xesl_entry_outputs_begin struct xesl_entry_outputs_struct {
  #define xesl_entry_output(type, name, index, semantic) type name : semantic;
  #define xesl_entry_output_target(type, name, index) \
      type name : SV_Target##index;
  #define xesl_Output(name) xesl_entry_outputs.name
  #define xesl_entry_output_position \
      float4 xesl_id_output_position : SV_Position;
  #define xesl_Position xesl_entry_outputs.xesl_id_output_position
  #define xesl_entry_outputs_end_stageInputs_begin \
      };                                           \
      struct xesl_entry_stageInputs_struct {
  #define xesl_entry_stageInput(type, name, index, semantic) \
      type name : semantic;
  #define xesl_entry_stageInputs_end_bindings_begin_vertex };
  #define xesl_entry_stageInputs_end_bindings_begin_pixel };
  #define xesl_entry_bindings_end_inputs_begin xesl_entry_outputs_struct main(
  #define xesl_entry_bindings_empty_end_inputs_begin \
      xesl_entry_outputs_struct main(
  #define xesl_entry_bindings_end_inputs_begin_compute                   \
      [numthreads(xesl_localSize_x, xesl_localSize_y, xesl_localSize_z)] \
      void main(
  #define xesl_entry_input_next ,
  #define xesl_entry_input_stageInputs \
      xesl_entry_stageInputs_struct xesl_entry_stageInputs
  #define xesl_StageInput(name) xesl_entry_stageInputs.name
  #define xesl_entry_input_vertexID uint xesl_VertexID : SV_VertexID
  #define xesl_entry_input_fragCoord xesl_float4 xesl_FragCoord : SV_Position
  #define xesl_entry_input_workGroupID xesl_uint3 xesl_WorkGroupID : SV_GroupID
  #define xesl_entry_input_localInvocationID \
      xesl_uint3 xesl_LocalInvocationID : SV_GroupThreadID
  #define xesl_entry_input_globalInvocationID \
      xesl_uint3 xesl_GlobalInvocationID : SV_DispatchThreadID
  #define xesl_entry_input_localInvocationIndex \
      uint xesl_LocalInvocationIndex : SV_GroupIndex
  #define xesl_entry_inputs_end_code_begin \
      ) {                                  \
          xesl_entry_outputs_struct xesl_entry_outputs;
  #define xesl_entry_inputs_end_code_begin_compute ) {
  #define xesl_entry_return return xesl_entry_outputs;
#elif XESL_LANGUAGE_MSL
  #define xesl_entry_outputs_begin struct xesl_entry_outputs_struct {
  #define xesl_entry_output(type, name, index, semantic) \
      type name [[user(semantic)]];
  #define xesl_entry_output_target(type, name, index) \
      type name [[color(index)]];
  #define xesl_Output(name) xesl_entry_outputs.name
  #define xesl_entry_output_position \
      xesl_float4 xesl_id_output_position [[position]];
  #define xesl_Position xesl_entry_outputs.xesl_id_output_position
  #define xesl_entry_outputs_end_stageInputs_begin \
      };                                           \
      struct xesl_entry_stageInputs_struct {
  #define xesl_entry_stageInput(type, name, index, semantic) \
      type name [[user(semantic)]];
  #define xesl_entry_stageInput_vertex(type, name, index, semantic) \
      type name [[attribute(index)]];
  #define xesl_entry_stageInputs_end_bindings_begin_vertex \
      };                                                   \
      vertex xesl_entry_outputs_struct xesl_entry(
  #define xesl_entry_stageInputs_end_bindings_begin_pixel \
      };                                                  \
      fragment xesl_entry_outputs_struct xesl_entry(
  #define xesl_entry_bindings_begin_compute kernel void xesl_entry(
  #define xesl_entry_binding_next ,
  #define xesl_entry_bindings_end_inputs_begin ,
  #define xesl_entry_bindings_end_inputs_begin_compute ,
  #define xesl_entry_input_next ,
  #define xesl_entry_input_stageInputs \
      xesl_entry_stageInputs_struct xesl_entry_stageInputs [[stage_in]]
  #define xesl_StageInput(name) xesl_entry_stageInputs.name
  #define xesl_entry_input_vertexID uint xesl_VertexID [[vertex_id]]
  #define XESL_FRAG_COORD_W_IS_INVERSE 1
  #define xesl_entry_input_fragCoord xesl_float4 xesl_FragCoord [[position]]
  #define xesl_entry_input_workGroupID \
      xesl_uint3 xesl_WorkGroupID [[threadgroup_position_in_grid]]
  #define xesl_entry_input_localInvocationID \
      xesl_uint3 xesl_LocalInvocationID [[thread_position_in_threadgroup]]
  #define xesl_entry_input_globalInvocationID \
      xesl_uint3 xesl_GlobalInvocationID [[thread_position_in_grid]]
  #define xesl_entry_input_localInvocationIndex \
      uint xesl_LocalInvocationIndex [[thread_index_in_threadgroup]]
  #define xesl_entry_inputs_end_code_begin \
      ) {                                  \
          xesl_entry_outputs_struct xesl_entry_outputs;
  #define xesl_entry_inputs_end_code_begin_compute ) {
  #define xesl_entry_return return xesl_entry_outputs;
#else
  #error xesl_entry not defined for the target language.
#endif  // XE_LANGUAGE
#ifndef xesl_entry_outputs_begin
  #define xesl_entry_outputs_begin
#endif  // !xesl_entry_outputs_begin
#ifndef xesl_entry_output_position
  #define xesl_entry_output_position
#endif  // !xesl_entry_output_position
#ifndef xesl_entry_outputs_end_stageInputs_begin
  #define xesl_entry_outputs_end_stageInputs_begin
#endif  // !xesl_entry_outputs_end_stageInputs_begin
#ifndef xesl_entry_stageInput_vertex
  #define xesl_entry_stageInput_vertex(type, name, index, semantic) \
      xesl_entry_stageInput(type, name, index, semantic)
#endif  // !xesl_entry_stageInput_vertex
#ifndef xesl_entry_stageInputs_end_bindings_begin_vertex
  #define xesl_entry_stageInputs_end_bindings_begin_vertex
#endif  // !xesl_entry_stageInputs_end_bindings_begin_vertex
#ifndef xesl_entry_stageInputs_end_bindings_begin_pixel
  #define xesl_entry_stageInputs_end_bindings_begin_pixel
#endif  // !xesl_entry_stageInputs_end_bindings_begin_pixel
#ifndef xesl_entry_bindings_begin_compute
  #define xesl_entry_bindings_begin_compute
#endif  // !xesl_entry_bindings_begin_compute
#ifndef xesl_entry_binding_next
  #define xesl_entry_binding_next
#endif  // !xesl_entry_binding_next
#ifndef xesl_entry_bindings_end_inputs_begin
  #define xesl_entry_bindings_end_inputs_begin
#endif  // !xesl_entry_bindings_end_inputs_begin
#ifndef xesl_entry_bindings_empty_end_inputs_begin
  #define xesl_entry_bindings_empty_end_inputs_begin
#endif  // !xesl_entry_bindings_empty_end_inputs_begin
#ifndef xesl_entry_bindings_end_inputs_begin_compute
  #define xesl_entry_bindings_end_inputs_begin_compute
#endif  // !xesl_entry_bindings_end_inputs_begin_compute
#ifndef xesl_entry_input_next
  #define xesl_entry_input_next
#endif  // !xesl_entry_input_next
#ifndef xesl_entry_input_stageInputs
  #define xesl_entry_input_stageInputs
#endif  // !xesl_entry_input_stageInputs
#ifndef xesl_entry_input_vertexID
  #define xesl_entry_input_vertexID
#endif  // !xesl_entry_input_vertexID
#ifndef xesl_entry_input_fragCoord
  #define xesl_entry_input_fragCoord
#endif  // !xesl_entry_input_fragCoord
#ifndef xesl_entry_input_workGroupID
  #define xesl_entry_input_workGroupID
#endif  // !xesl_entry_input_workGroupID
#ifndef xesl_entry_input_localInvocationID
  #define xesl_entry_input_localInvocationID
#endif  // !xesl_entry_input_localInvocationID
#ifndef xesl_entry_input_globalInvocationID
  #define xesl_entry_input_globalInvocationID
#endif  // !xesl_entry_input_globalInvocationID
#ifndef xesl_entry_input_localInvocationIndex
  #define xesl_entry_input_localInvocationIndex
#endif  // !xesl_entry_input_localInvocationIndex
#ifndef xesl_entry_code_end
  #define xesl_entry_code_end \
        xesl_entry_return     \
      }
#endif  // !xesl_entry_code_end
#ifndef xesl_entry_code_end_compute
  #define xesl_entry_code_end_compute }
#endif  // !xesl_entry_code_end_compute

// XESL_Y_SCREEN_DIRECTION is 1.0 if with a positive viewport height,
// +xesl_Position.y is towards +xesl_FragCoord.y, -1.0 if +xesl_Position.y is
// towards -xesl_FragCoord.y.
#if XESL_LANGUAGE_GLSL
  #define XESL_Y_SCREEN_DIRECTION 1.0
#else
  #define XESL_Y_SCREEN_DIRECTION -1.0
#endif  // XESL_LANGUAGE_GLSL

#if XESL_LANGUAGE_GLSL
  // GLSL requires just const for declaring a constant in the global scope.
  #define xesl_staticConst const
#elif XESL_LANGUAGE_HLSL
  // HLSL requires static const for declaring a constant in the global scope so
  // it doesn't go to $Globals instead.
  #define xesl_staticConst static const
#elif XESL_LANGUAGE_MSL
  #define xesl_staticConst constexpr constant
#else
  #error xesl_staticConst not defined for the target language.
#endif  // XESL_LANGUAGE

#if XESL_LANGUAGE_GLSL
  #define xesl_block_offset_member(glsl_offset_bytes, hlsl_packoffset, type, \
                                   name_element_count) \
      layout(offset=glsl_offset_bytes) type name_element_count;
#elif XESL_LANGUAGE_HLSL
  #define xesl_block_offset_member(glsl_offset_bytes, hlsl_packoffset, type, \
                                   name_element_count) \
      type name_element_count : packoffset(hlsl_packoffset);
#elif XESL_LANGUAGE_MSL
  // Explicit offset is not supported by MSL.
  #define xesl_block_offset_member(glsl_offset_bytes, hlsl_packoffset, type, \
                                   name_element_count) \
      type name_element_count;
#else
  #error xesl_block_offset_member not defined for the target language.
#endif  // XESL_LANGUAGE

// Structures of constant and structured buffer bindings must be declared before
// the entry point declaration.

// Constant buffers and push constants must be manually packed as std140 (which
// is stricter than HLSL packing) due to the GLSL requirement. This means that
// 32x4 and 32x3 vectors must start at 16-byte alignment, 32x2 at 8-byte, and a
// single 32-bit value can be placed immediately after a 32x3 vector (the Vulkan
// definition of this behavior). Specifically, all alignment padding must be
// inserted explicitly (or xesl_block_offset_member must be used), as by default
// HLSL doesn't have the alignment requirement, only the rule that elements
// (array elements, or single non-array members) must not cross 32x4 vector
// boundaries, so something like float|float3 or float|float2|float will be
// packed differently in GLSL (float|pad3|float3 or float|pad|float2|float) and
// HLSL (float|float3 or float|float2|float).

// Constant buffer and push constant member names will be in the global scope in
// some target languages - they must not collide with anything else there. To
// access a constant, use xesl_constant or xesl_pushConstant.

// Push constants, even though may be spread across multiple constant buffers in
// the Direct3D 12 API, must be declared in a single structure in XeSL - the
// reason is that layout qualifiers in GLSL can't be used in regular structures,
// only in blocks, and sub-blocks can't be declared in a block, so there's no
// way to create separate identifiers for push constant ranges in GLSL. Though
// both GLSL and HLSL support anonymous push constants / cbuffers, MSL requires
// a name for the buffer binding.
// In GLSL, the offsets in the push constants are global across shader stages.
// In HLSL, they're local to the specific root constant buffer.
#if XESL_LANGUAGE_GLSL
  #define xesl_constantBuffer_begin(name, glsl_set, glsl_binding, hlsl_b, \
                                    hlsl_b_space)                         \
      layout(std140, glsl_set, glsl_binding)                              \
      uniform xesl_id_constantBuffer_##name {
  #define xesl_constantBuffer_end(name) \
      } name;
  #define xesl_constant(cbuffer_name, constant_name) cbuffer_name.constant_name
  #define xesl_pushConstants_begin(hlsl_b, hlsl_b_space) \
      layout(push_constant) uniform xesl_pushConstants_block {
  #define xesl_pushConstants_end \
      } xesl_pushConstants;
  #define xesl_pushConstant(name) xesl_pushConstants.name
#elif XESL_LANGUAGE_HLSL
  #define xesl_constantBuffer_begin(name, glsl_set, glsl_binding, hlsl_b, \
                                    hlsl_b_space)                         \
      cbuffer name : register(hlsl_b, hlsl_b_space) {
  #define xesl_constantBuffer_end(name) \
      };
  #define xesl_constant(cbuffer_name, constant_name) constant_name
  #define xesl_pushConstants_begin(hlsl_b, hlsl_b_space) \
      cbuffer xesl_pushConstants : register(hlsl_b, hlsl_b_space) {
  #define xesl_pushConstants_end \
      };
  #define xesl_pushConstant(name) name
#elif XESL_LANGUAGE_MSL
  #define xesl_constantBuffer_begin(name, glsl_set, glsl_binding, hlsl_b, \
                                    hlsl_b_space)                         \
      struct xesl_id_constantBuffer_##name {
  #define xesl_constantBuffer_end(name) \
      };
  #define xesl_constantBuffer_binding(name, msl_buffer) \
      constant xesl_id_constantBuffer_##name& name [[msl_buffer]]
  #define xesl_constant(cbuffer_name, constant_name) cbuffer_name.constant_name
  #define xesl_pushConstants_begin(hlsl_b, hlsl_b_space) \
      struct xesl_pushConstants_struct {
  #define xesl_pushConstants_end \
      };
  #define xesl_pushConstants_binding(msl_buffer) \
      constant xesl_pushConstants_struct& xesl_pushConstants [[msl_buffer]]
  #define xesl_pushConstant(name) xesl_pushConstants.name
#else
  #error Constant buffers not defined for the target language.
#endif  // XESL_LANGUAGE
#ifndef xesl_constantBuffer_binding
  #define xesl_constantBuffer_binding(name, msl_buffer)
#endif  // !xesl_constantBuffer_binding
#ifndef xesl_pushConstants_binding
  #define xesl_pushConstants_binding(msl_buffer)
#endif  // !xesl_pushConstants_binding

// Declarations of typed storage buffers and dword buffers (_declare) must be
// outside the entry point, but their bindings (_binding) must also be specified
// in the entry point binding declarations.
//
// xesl_typedStorageBuffer is a buffer limited to 1/2/4-component vectors of
// 32-bit integers and floats, a typed buffer on Direct3D, but a storage buffer
// (as opposed to a texel buffer, which has a very small minimum requirement for
// the maximum size) on Vulkan.
//
// xesl_uintVectorBuffer is a buffer containing 32-bit values, but loading or
// storing may be done for 2, 3 or 4 consecutive values that are still
// 32-bit-aligned, and depending on the language and hardware support, access
// of multiple elements may or may not be compiled into a single hardware
// instruction instead of separate accesses of individual elements. Each index
// value corresponds to a 32-bit element. Implementations for languages without
// native support must use functions, not macros, for adding the component
// offset to the index to avoid evaluating the address multiple times.
#if XESL_LANGUAGE_GLSL
  // Binding declarations.
  #define xesl_typedStorageBuffer_declare(value_type, name, glsl_set,         \
                                          glsl_binding, hlsl_t, hlsl_t_space) \
      layout(std430, glsl_set, glsl_binding)                                  \
      readonly buffer xesl_id_buffer_##name {                                 \
        value_type xesl_id_data[];                                            \
      } name;
  #define xesl_writeTypedStorageBuffer_declare(value_type, name, glsl_set, \
                                               glsl_binding, hlsl_u,       \
                                               hlsl_u_space)               \
      layout(std430, glsl_set, glsl_binding)                               \
      writeonly buffer xesl_id_buffer_##name {                             \
        value_type xesl_id_data[];                                         \
      } name;
  #define xesl_uintVectorBuffer_declare(name, glsl_set, glsl_binding, hlsl_t, \
                                        hlsl_t_space)                         \
      layout(std430, glsl_set, glsl_binding)                                  \
      readonly buffer xesl_id_buffer_##name {                                 \
        uint xesl_id_data[];                                                  \
      } name;                                                                 \
      xesl_uint2 xesl_id_uintVectorBuffer_load2_##name(                       \
          uint xesl_var_position) {                                           \
        return xesl_uint2(name.xesl_id_data[xesl_var_position],               \
                          name.xesl_id_data[xesl_var_position + 1u]);         \
      }                                                                       \
      xesl_uint3 xesl_id_uintVectorBuffer_load3_##name(                       \
          uint xesl_var_position) {                                           \
        return xesl_uint3(name.xesl_id_data[xesl_var_position],               \
                          name.xesl_id_data[xesl_var_position + 1u],          \
                          name.xesl_id_data[xesl_var_position + 2u]);         \
      }                                                                       \
      xesl_uint4 xesl_id_uintVectorBuffer_load4_##name(                       \
          uint xesl_var_position) {                                           \
        return xesl_uint4(name.xesl_id_data[xesl_var_position],               \
                          name.xesl_id_data[xesl_var_position + 1u],          \
                          name.xesl_id_data[xesl_var_position + 2u],          \
                          name.xesl_id_data[xesl_var_position + 3u]);         \
      }
  // Loading and storing.
  #define xesl_typedStorageBufferLoad(name, position) \
      ((name).xesl_id_data[uint(position)])
  #define xesl_typedStorageBufferStore(name, position, value) \
      ((name).xesl_id_data[uint(position)] = (value))
  #define xesl_uintVectorBufferLoad1(name, position) \
      ((name).xesl_id_data[uint(position)])
  #define xesl_uintVectorBufferLoad2(name, position) \
      xesl_id_uintVectorBuffer_load2_##name(uint(position))
  #define xesl_uintVectorBufferLoad3(name, position) \
      xesl_id_uintVectorBuffer_load3_##name(uint(position))
  #define xesl_uintVectorBufferLoad4(name, position) \
      xesl_id_uintVectorBuffer_load4_##name(uint(position))
#elif XESL_LANGUAGE_HLSL
  // Binding declarations.
  #define xesl_typedStorageBuffer_declare(value_type, name, glsl_set,         \
                                          glsl_binding, hlsl_t, hlsl_t_space) \
      Buffer<value_type> name : register(hlsl_t, hlsl_t_space);
  #define xesl_writeTypedStorageBuffer_declare(value_type, name, glsl_set, \
                                               glsl_binding, hlsl_u,       \
                                               hlsl_u_space)               \
      RWBuffer<value_type> name : register(hlsl_u, hlsl_u_space);
  #define xesl_uintVectorBuffer_declare(name, glsl_set, glsl_binding, hlsl_t, \
                                        hlsl_t_space)                         \
      ByteAddressBuffer name : register(hlsl_t, hlsl_t_space);
  // Loading and storing.
  #define xesl_typedStorageBufferLoad(name, position) ((name)[uint(position)])
  #define xesl_typedStorageBufferStore(name, position, value) \
      ((name)[uint(position)] = (value))
  #define xesl_uintVectorBufferLoad1(name, position) \
      ((name).Load(int(position) << 2))
  #define xesl_uintVectorBufferLoad2(name, position) \
      ((name).Load2(int(position) << 2))
  #define xesl_uintVectorBufferLoad3(name, position) \
      ((name).Load3(int(position) << 2))
  #define xesl_uintVectorBufferLoad4(name, position) \
      ((name).Load4(int(position) << 2))
#elif XESL_LANGUAGE_MSL
  // Binding declarations.
  #define xesl_typedStorageBuffer_binding(value_type, name, msl_buffer) \
      const device value_type* name [[msl_buffer]]
  #define xesl_writeTypedStorageBuffer_binding(value_type, name, msl_buffer) \
      device value_type* name [[msl_buffer]]
  #define xesl_uintVectorBuffer_binding(name, msl_buffer) \
      const device uint* name [[msl_buffer]]
  // Loading and storing.
  #define xesl_typedStorageBufferLoad(name, position) ((name)[size_t(position)])
  #define xesl_typedStorageBufferStore(name, position, value) \
      ((name)[size_t(position)] = (value))
  #define xesl_uintVectorBufferLoad1(name, position) ((name)[size_t(position)])
  #define xesl_uintVectorBufferLoad2(name, position)            \
      xesl_uint2(*reinterpret_cast<const device packed_uint2*>( \
                     &((name)[size_t(position)])))
  #define xesl_uintVectorBufferLoad3(name, position)            \
      xesl_uint3(*reinterpret_cast<const device packed_uint3*>( \
                     &((name)[size_t(position)])))
  #define xesl_uintVectorBufferLoad4(name, position)            \
      xesl_uint4(*reinterpret_cast<const device packed_uint4*>( \
                     &((name)[size_t(position)])))
#else
  #error Storage buffers not defined for the target language.
#endif  // XESL_LANGUAGE
#ifndef xesl_typedStorageBuffer_declare
  #define xesl_typedStorageBuffer_declare(value_type, name, glsl_set,         \
                                          glsl_binding, hlsl_t, hlsl_t_space)
#endif  // !xesl_typedStorageBuffer_declare
#ifndef xesl_writeTypedStorageBuffer_declare
  #define xesl_writeTypedStorageBuffer_declare(value_type, name, glsl_set, \
                                               glsl_binding, hlsl_u,       \
                                               hlsl_u_space)
#endif  // !xesl_writeTypedStorageBuffer_declare
#ifndef xesl_typedStorageBuffer_binding
  #define xesl_typedStorageBuffer_binding(value_type, name, msl_buffer)
#endif  // !xesl_typedStorageBuffer_binding
#ifndef xesl_writeTypedStorageBuffer_binding
  #define xesl_writeTypedStorageBuffer_binding(value_type, name, msl_buffer)
#endif  // !xesl_writeTypedStorageBuffer_binding
#ifndef xesl_uintVectorBuffer_declare
  #define xesl_uintVectorBuffer_declare(name, glsl_set, glsl_binding, hlsl_t, \
                                        hlsl_t_space)
#endif  // !xesl_uintVectorBuffer_declare
#ifndef xesl_uintVectorBuffer_binding
  #define xesl_uintVectorBuffer_binding(name, msl_buffer)
#endif  // !xesl_uintVectorBuffer_binding

// Buffer, texture, sampler and image bindings must be in the entry point
// bindings declaration.
// - xesl_texture is a separate texture.
// - xesl_samplerState is a separate sampler.
// - xesl_sampler is a combined texture / sampler where available, internally
//   separate where not.
#if XESL_LANGUAGE_GLSL
  #define XESL_COMBINED_TEXTURE_SAMPLER 1
  // Types.
  #define xesl_textureBuffer textureBuffer
  #define xesl_utextureBuffer utextureBuffer
  #define xesl_texture2D texture2D
  #define xesl_texture2DMS texture2DMS
  #define xesl_samplerBuffer samplerBuffer
  #define xesl_usamplerBuffer usamplerBuffer
  #define xesl_sampler2D sampler2D
  #define xesl_image2D image2D
  #define xesl_imageFormat_rgb10_a2 rgb10_a2
  #define xesl_imageFormat_rgba16f rgba16f
  // Binding declarations.
  #define xesl_texture(texture_type, name, glsl_set, glsl_binding, hlsl_t, \
                       hlsl_t_space, msl_texture)                          \
      layout(glsl_set, glsl_binding) uniform texture_type name;
  #define xesl_samplerState(name, glsl_set, glsl_binding, hlsl_s, \
                            hlsl_s_space, msl_sampler)            \
      layout(glsl_set, glsl_binding) uniform sampler name;
  #define xesl_sampler(sampler_type, name, glsl_set, glsl_binding, hlsl_t, \
                       hlsl_t_space, hlsl_s, hlsl_s_space, msl_texture,    \
                       msl_sampler)                                        \
      layout(glsl_set, glsl_binding) uniform sampler_type name;
  #define xesl_writeImage(type, format, name, glsl_set, glsl_binding, hlsl_u, \
                          hlsl_u_space, msl_texture)                          \
      layout(format, glsl_set, glsl_binding) uniform writeonly type name;
  // Fetching and storing.
  #define xesl_texelFetchBuffer(texture_name, position) \
      texelFetch(texture_name, int(position))
  #define xesl_texelFetch2D(texture_name, position, lod) \
      texelFetch(texture_name, xesl_int2(position), int(lod))
  #define xesl_texelFetch2DMS(texture_name, position, sample_index) \
      texelFetch(texture_name, xesl_int2(position), int(sample_index))
  #define xesl_textureSampleLod2D_sep(texture_name, sampler_name, position, \
                                      lod)                                  \
      textureLod(sampler2D(texture_name, sampler_name), position, lod)
  #define xesl_textureSampleLod2D_comb(texture_sampler_name, position, lod) \
      textureLod(texture_sampler_name, position, lod)
  #define xesl_textureGatherRed2D_sep(texture_name, sampler_name, position) \
      textureGather(sampler2D(texture_name, sampler_name), position, 0)
  #define xesl_textureGatherGreen2D_sep(texture_name, sampler_name, position) \
      textureGather(sampler2D(texture_name, sampler_name), position, 1)
  #define xesl_textureGatherBlue2D_sep(texture_name, sampler_name, position) \
      textureGather(sampler2D(texture_name, sampler_name), position, 2)
  #define xesl_textureGatherAlpha2D_sep(texture_name, sampler_name, position) \
      textureGather(sampler2D(texture_name, sampler_name), position, 3)
  #define xesl_textureGatherRed2D_comb(texture_sampler_name, position) \
      textureGather(texture_sampler_name, position, 0)
  #define xesl_textureGatherGreen2D_comb(texture_sampler_name, position) \
      textureGather(texture_sampler_name, position, 1)
  #define xesl_textureGatherBlue2D_comb(texture_sampler_name, position) \
      textureGather(texture_sampler_name, position, 2)
  #define xesl_textureGatherAlpha2D_comb(texture_sampler_name, position) \
      textureGather(texture_sampler_name, position, 3)
  #define xesl_imageStore2DRGBA(name, position, data) \
      imageStore(name, xesl_int2(position), data)
#elif XESL_LANGUAGE_HLSL
  // Types.
  #define xesl_textureBuffer Buffer<xesl_float4>
  #define xesl_utextureBuffer Buffer<xesl_uint4>
  #define xesl_texture2D Texture2D<xesl_float4>
  #define xesl_texture2DMS Texture2DMS<xesl_float4>
  #define xesl_image2D RWTexture2D
  #define xesl_imageFormat_rgb10_a2 unorm float4
  #define xesl_imageFormat_rgba16f float4
  // Binding declarations.
  #define xesl_texture(texture_type, name, glsl_set, glsl_binding, hlsl_t, \
                       hlsl_t_space, msl_texture)                          \
      texture_type name : register(hlsl_t, hlsl_t_space);
  #define xesl_samplerState(name, glsl_set, glsl_binding, hlsl_s, \
                            hlsl_s_space, msl_sampler)            \
      SamplerState name : register(hlsl_s, hlsl_s_space);
  #define xesl_writeImage(type, format, name, glsl_set, glsl_binding, hlsl_u, \
                          hlsl_u_space, msl_texture)                          \
      type<format> name : register(hlsl_u, hlsl_u_space);
  // Fetching and storing.
  #define xesl_texelFetchBuffer(texture_name, position) \
      ((texture_name).Load(int(position)))
  #define xesl_texelFetch2D(texture_name, position, lod) \
      ((texture_name).Load(xesl_int3(position, lod)))
  #define xesl_texelFetch2DMS(texture_name, position, sample_index) \
      ((texture_name).Load(xesl_int2(position), int(sample_index)))
  #define xesl_textureSampleLod2D_sep(texture_name, sampler_name, position, \
                                      lod)                                  \
      ((texture_name).SampleLevel(sampler_name, position, lod))
  #define xesl_textureGatherRed2D_sep(texture_name, sampler_name, position) \
      ((texture_name).GatherRed(sampler_name, position))
  #define xesl_textureGatherGreen2D_sep(texture_name, sampler_name, position) \
      ((texture_name).GatherGreen(sampler_name, position))
  #define xesl_textureGatherBlue2D_sep(texture_name, sampler_name, position) \
      ((texture_name).GatherBlue(sampler_name, position))
  #define xesl_textureGatherAlpha2D_sep(texture_name, sampler_name, position) \
      ((texture_name).GatherAlpha(sampler_name, position))
  #define xesl_imageStore2DRGBA(name, position, data) \
      ((name)[xesl_int2(position)] = (data))
#elif XESL_LANGUAGE_MSL
  // Types.
  #define xesl_textureBuffer texture_buffer<float>
  #define xesl_utextureBuffer texture_buffer<uint>
  #define xesl_texture2D texture2d<float>
  #define xesl_texture2DMS texture2d_ms<float>
  #define xesl_image2D texture2d
  #define xesl_imageFormat_rgb10_a2 float
  #define xesl_imageFormat_rgba16f float
  // Binding declarations.
  #define xesl_texture(texture_type, name, glsl_set, glsl_binding, hlsl_t, \
                       hlsl_t_space, msl_texture)                          \
      texture_type name [[msl_texture]]
  #define xesl_samplerState(name, glsl_set, glsl_binding, hlsl_s, \
                            hlsl_s_space, msl_sampler)            \
      sampler name [[msl_sampler]]
  #define xesl_writeImage(type, format, name, glsl_set, glsl_binding, hlsl_u, \
                          hlsl_u_space, msl_texture)                          \
      type<format, access::write> name [[msl_texture]]
  // Fetching and storing.
  #define xesl_texelFetchBuffer(texture_name, position) \
      ((texture_name).read(uint(position)))
  #define xesl_texelFetch2D(texture_name, position, lod) \
      ((texture_name).read(xesl_uint2(position), uint(lod)))
  #define xesl_texelFetch2DMS(texture_name, position, sample_index) \
      ((texture_name).read(xesl_uint2(position), uint(sample_index)))
  #define xesl_textureSampleLod2D_sep(texture_name, sampler_name, position, \
                                      lod)                                  \
      ((texture_name).sample(sampler_name, position, level(lod)))
  #define xesl_textureGatherRed2D_sep(texture_name, sampler_name, position) \
      ((texture_name).gather(sampler_name, position, xesl_int2(0),          \
                             component::x))
  #define xesl_textureGatherGreen2D_sep(texture_name, sampler_name, position) \
      ((texture_name).gather(sampler_name, position, xesl_int2(0),            \
                             component::y))
  #define xesl_textureGatherBlue2D_sep(texture_name, sampler_name, position) \
      ((texture_name).gather(sampler_name, position, xesl_int2(0),           \
                             component::z))
  #define xesl_textureGatherAlpha2D_sep(texture_name, sampler_name, position) \
      ((texture_name).gather(sampler_name, position, xesl_int2(0),            \
                             component::w))
  #define xesl_imageStore2DRGBA(name, position, data) \
      ((name).write(data, xesl_uint2(position)))
#else
  #error Buffers and textures not defined for the target language.
#endif  // XESL_LANGUAGE
// If there's no language specialization doing this already, implement combined
// textures / samplers as separate, with the `xesl_id_sampler_` prefix for
// samplers. The sampler types become the texture types.
#if !XESL_COMBINED_TEXTURE_SAMPLER
  #ifndef xesl_sampler2D
    #define xesl_sampler2D xesl_texture2D
  #endif  // !xesl_sampler2D
  #ifndef xesl_sampler
    #define xesl_sampler(sampler_type, name, glsl_set, glsl_binding, hlsl_t, \
                         hlsl_t_space, hlsl_s, hlsl_s_space, msl_texture,    \
                         msl_sampler)                                        \
        xesl_texture(sampler_type, name, glsl_set, glsl_binding, hlsl_t,     \
                     hlsl_t_space, msl_texture)                              \
        xesl_entry_binding_next                                              \
        xesl_samplerState(xesl_id_sampler_##name, glsl_set, glsl_binding,    \
                          hlsl_s, hlsl_s_space, msl_sampler)
  #endif  // !xesl_sampler
  #ifndef xesl_textureSampleLod2D_comb
    #define xesl_textureSampleLod2D_comb(texture_sampler_name, position, lod) \
        xesl_textureSampleLod2D_sep(texture_sampler_name,                     \
                                    xesl_id_sampler_##texture_sampler_name,   \
                                    position, lod)
  #endif  // !xesl_textureSampleLod2D_comb
  #ifndef xesl_textureGatherRed2D_comb
    #define xesl_textureGatherRed2D_comb(texture_sampler_name, position)    \
        xesl_textureGatherRed2D_sep(texture_sampler_name,                   \
                                    xesl_id_sampler_##texture_sampler_name, \
                                    position)
  #endif  // !xesl_textureGatherRed2D_comb
  #ifndef xesl_textureGatherGreen2D_comb
    #define xesl_textureGatherGreen2D_comb(texture_sampler_name, position)    \
        xesl_textureGatherGreen2D_sep(texture_sampler_name,                   \
                                      xesl_id_sampler_##texture_sampler_name, \
                                      position)
  #endif  // !xesl_textureGatherGreen2D_comb
  #ifndef xesl_textureGatherBlue2D_comb
    #define xesl_textureGatherBlue2D_comb(texture_sampler_name, position)    \
        xesl_textureGatherBlue2D_sep(texture_sampler_name,                   \
                                     xesl_id_sampler_##texture_sampler_name, \
                                     position)
  #endif  // !xesl_textureGatherBlue2D_comb
  #ifndef xesl_textureGatherAlpha2D_comb
    #define xesl_textureGatherAlpha2D_comb(texture_sampler_name, position)    \
        xesl_textureGatherAlpha2D_sep(texture_sampler_name,                   \
                                      xesl_id_sampler_##texture_sampler_name, \
                                      position)
  #endif  // !xesl_textureGatherAlpha2D_comb
#endif  // !XESL_COMBINED_TEXTURE_SAMPLER

// Passing bindings to functions, and also output and input / output parameters.

#if XESL_LANGUAGE_MSL
  #define xesl_function_param_out(type, name) thread type& name
  #define xesl_function_param_inout(type, name) thread type& name
#else
  #define xesl_function_param_out(type, name) out type name
  #define xesl_function_param_inout(type, name) inout type name
#endif  // XESL_LANGUAGE_MSL

#if XESL_LANGUAGE_MSL
  // Prototype parameters.
  #define xesl_function_param_constantBuffer(name) \
      constant xesl_id_constantBuffer_##name& name
  #define xesl_function_param_next_after_constantBuffer ,
  #define xesl_function_param_pushConstants \
      constant xesl_pushConstants_struct& xesl_pushConstants
  #define xesl_function_param_next_after_pushConstants ,
  #define xesl_function_param_uintVectorBuffer(name) const device uint* name
  #define xesl_function_param_next_after_uintVectorBuffer ,
  // Call arguments.
  #define xesl_function_call_constantBuffer(name) (name)
  #define xesl_function_call_next_after_constantBuffer ,
  #define xesl_function_call_pushConstants xesl_pushConstants
  #define xesl_function_call_next_after_pushConstants ,
  #define xesl_function_call_uintVectorBuffer(name) (name)
  #define xesl_function_call_next_after_uintVectorBuffer ,
#endif  // XESL_LANGUAGE

// Prototype parameters.
#ifndef xesl_function_param_constantBuffer
  #define xesl_function_param_constantBuffer(name)
#endif  // !xesl_function_param_constantBuffer
#ifndef xesl_function_param_next_after_constantBuffer
  #define xesl_function_param_next_after_constantBuffer
#endif  // !xesl_function_param_next_after_constantBuffer
#ifndef xesl_function_param_pushConstants
  #define xesl_function_param_pushConstants
#endif  // !xesl_function_param_pushConstants
#ifndef xesl_function_param_next_after_pushConstants
  #define xesl_function_param_next_after_pushConstants
#endif  // !xesl_function_param_next_after_pushConstants
#ifndef xesl_function_param_uintVectorBuffer
  #define xesl_function_param_uintVectorBuffer(name)
#endif  // !xesl_function_param_uintVectorBuffer
#ifndef xesl_function_param_next_after_uintVectorBuffer
  #define xesl_function_param_next_after_uintVectorBuffer
#endif  // !xesl_function_param_next_after_uintVectorBuffer
// Call arguments.
#ifndef xesl_function_call_constantBuffer
  #define xesl_function_call_constantBuffer(name)
#endif  // !xesl_function_call_constantBuffer
#ifndef xesl_function_call_next_after_constantBuffer
  #define xesl_function_call_next_after_constantBuffer
#endif  // !xesl_function_call_next_after_constantBuffer
#ifndef xesl_function_call_pushConstants
  #define xesl_function_call_pushConstants
#endif  // !xesl_function_call_pushConstants
#ifndef xesl_function_call_next_after_pushConstants
  #define xesl_function_call_next_after_pushConstants
#endif  // !xesl_function_call_next_after_pushConstants
#ifndef xesl_function_call_uintVectorBuffer
  #define xesl_function_call_uintVectorBuffer(name)
#endif  // !xesl_function_call_uintVectorBuffer
#ifndef xesl_function_call_next_after_uintVectorBuffer
  #define xesl_function_call_next_after_uintVectorBuffer
#endif  // !xesl_function_call_next_after_uintVectorBuffer

// Attributes.

#if XESL_LANGUAGE_GLSL
  #define xesl_unroll [[unroll]]
  #define xesl_dont_unroll [[dont_unroll]]
  #define xesl_flatten [[flatten]]
  #define xesl_dont_flatten [[dont_flatten]]
#elif XESL_LANGUAGE_HLSL
  #define xesl_unroll [unroll]
  #define xesl_dont_unroll [loop]
  #define xesl_flatten [flatten]
  #define xesl_dont_flatten [branch]
#endif  // XESL_LANGUAGE
#ifndef xesl_unroll
  #define xesl_unroll
#endif  // !xesl_unroll
#ifndef xesl_dont_unroll
  #define xesl_dont_unroll
#endif  // !xesl_dont_unroll
#ifndef xesl_flatten
  #define xesl_flatten
#endif  // !xesl_flatten
#ifndef xesl_dont_flatten
  #define xesl_dont_flatten
#endif  // !xesl_dont_flatten

// Function aliases.
//
// Use the `xesl_var_` prefix for arguments of functions that are not macros and
// for local variables.

#if XESL_LANGUAGE_GLSL
  #define xesl_lessThan lessThan
  #define xesl_lessThanEqual lessThanEqual
  #define xesl_greaterThan greaterThan
  #define xesl_greaterThanEqual greaterThanEqual
  #define xesl_equal equal
  #define xesl_notEqual notEqual
  #define xesl_not not
  #define xesl_select(condition, true_result, false_result) \
      mix(false_result, true_result, condition)
#elif XESL_LANGUAGE_HLSL
  #define xesl_lessThan(x, y) ((x) < (y))
  #define xesl_lessThanEqual(x, y) ((x) <= (y))
  #define xesl_greaterThan(x, y) ((x) > (y))
  #define xesl_greaterThanEqual(x, y) ((x) >= (y))
  #define xesl_equal(x, y) ((x) == (y))
  #define xesl_notEqual(x, y) ((x) != (y))
  #define xesl_not(x) (!(x))
  #define xesl_select(condition, true_result, false_result) \
      ((condition) ? (true_result) : (false_result))
#elif XESL_LANGUAGE_MSL
  #define xesl_lessThan(x, y) ((x) < (y))
  #define xesl_lessThanEqual(x, y) ((x) <= (y))
  #define xesl_greaterThan(x, y) ((x) > (y))
  #define xesl_greaterThanEqual(x, y) ((x) >= (y))
  #define xesl_equal(x, y) ((x) == (y))
  #define xesl_notEqual(x, y) ((x) != (y))
  #define xesl_not(x) (!(x))
  #define xesl_select(condition, true_result, false_result) \
      select(false_result, true_result, condition)
#else
  #error Comparison operations not defined for the target language.
#endif

#if XESL_LANGUAGE_GLSL
  #define xesl_floatBitsToInt floatBitsToInt
  #define xesl_floatBitsToUint floatBitsToUint
  #define xesl_intBitsToFloat intBitsToFloat
  #define xesl_uintBitsToFloat uintBitsToFloat
#elif XESL_LANGUAGE_HLSL
  // Using functions instead of #define for implicit argument conversion.
  int xesl_floatBitsToInt(float xesl_var_value) {
    return asint(xesl_var_value);
  }
  xesl_int2 xesl_floatBitsToInt(xesl_float2 xesl_var_value) {
    return asint(xesl_var_value);
  }
  xesl_int3 xesl_floatBitsToInt(xesl_float3 xesl_var_value) {
    return asint(xesl_var_value);
  }
  xesl_int4 xesl_floatBitsToInt(xesl_float4 xesl_var_value) {
    return asint(xesl_var_value);
  }
  uint xesl_floatBitsToUint(float xesl_var_value) {
    return asuint(xesl_var_value);
  }
  xesl_uint2 xesl_floatBitsToUint(xesl_float2 xesl_var_value) {
    return asuint(xesl_var_value);
  }
  xesl_uint3 xesl_floatBitsToUint(xesl_float3 xesl_var_value) {
    return asuint(xesl_var_value);
  }
  xesl_uint4 xesl_floatBitsToUint(xesl_float4 xesl_var_value) {
    return asuint(xesl_var_value);
  }
  float xesl_intBitsToFloat(int xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float2 xesl_intBitsToFloat(xesl_int2 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float3 xesl_intBitsToFloat(xesl_int3 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float4 xesl_intBitsToFloat(xesl_int4 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  float xesl_uintBitsToFloat(uint xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float2 xesl_uintBitsToFloat(xesl_uint2 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float3 xesl_uintBitsToFloat(xesl_uint3 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
  xesl_float4 xesl_uintBitsToFloat(xesl_uint4 xesl_var_value) {
    return asfloat(xesl_var_value);
  }
#elif XESL_LANGUAGE_MSL
  // Using functions instead of #define for implicit argument conversion.
  int xesl_floatBitsToInt(float xesl_var_value) {
    return as_type<int>(xesl_var_value);
  }
  xesl_int2 xesl_floatBitsToInt(xesl_float2 xesl_var_value) {
    return as_type<xesl_int2>(xesl_var_value);
  }
  xesl_int3 xesl_floatBitsToInt(xesl_float3 xesl_var_value) {
    return as_type<xesl_int3>(xesl_var_value);
  }
  xesl_int4 xesl_floatBitsToInt(xesl_float4 xesl_var_value) {
    return as_type<xesl_int4>(xesl_var_value);
  }
  uint xesl_floatBitsToUint(float xesl_var_value) {
    return as_type<uint>(xesl_var_value);
  }
  xesl_uint2 xesl_floatBitsToUint(xesl_float2 xesl_var_value) {
    return as_type<xesl_uint2>(xesl_var_value);
  }
  xesl_uint3 xesl_floatBitsToUint(xesl_float3 xesl_var_value) {
    return as_type<xesl_uint3>(xesl_var_value);
  }
  xesl_uint4 xesl_floatBitsToUint(xesl_float4 xesl_var_value) {
    return as_type<xesl_uint4>(xesl_var_value);
  }
  float xesl_intBitsToFloat(int xesl_var_value) {
    return as_type<float>(xesl_var_value);
  }
  xesl_float2 xesl_intBitsToFloat(xesl_int2 xesl_var_value) {
    return as_type<xesl_float2>(xesl_var_value);
  }
  xesl_float3 xesl_intBitsToFloat(xesl_int3 xesl_var_value) {
    return as_type<xesl_float3>(xesl_var_value);
  }
  xesl_float4 xesl_intBitsToFloat(xesl_int4 xesl_var_value) {
    return as_type<xesl_float4>(xesl_var_value);
  }
  float xesl_uintBitsToFloat(uint xesl_var_value) {
    return as_type<float>(xesl_var_value);
  }
  xesl_float2 xesl_uintBitsToFloat(xesl_uint2 xesl_var_value) {
    return as_type<xesl_float2>(xesl_var_value);
  }
  xesl_float3 xesl_uintBitsToFloat(xesl_uint3 xesl_var_value) {
    return as_type<xesl_float3>(xesl_var_value);
  }
  xesl_float4 xesl_uintBitsToFloat(xesl_uint4 xesl_var_value) {
    return as_type<xesl_float4>(xesl_var_value);
  }
#else
  #error Float bit casting not defined for the target language.
#endif  // XESL_LANGUAGE

#if XESL_LANGUAGE_GLSL
  float xesl_saturate(float xesl_var_value) {
    return clamp(xesl_var_value, 0.0, 1.0);
  }
  xesl_float2 xesl_saturate(xesl_float2 xesl_var_value) {
    return clamp(xesl_var_value, xesl_float_x2(0.0), xesl_float_x2(1.0));
  }
  xesl_float3 xesl_saturate(xesl_float3 xesl_var_value) {
    return clamp(xesl_var_value, xesl_float_x3(0.0), xesl_float_x3(1.0));
  }
  xesl_float4 xesl_saturate(xesl_float4 xesl_var_value) {
    return clamp(xesl_var_value, xesl_float_x4(0.0), xesl_float_x4(1.0));
  }
#else
  #define xesl_saturate saturate
#endif  // XESL_LANGUAGE_GLSL

// Returning a unsigned integer vector. The result is undefined for zero.
#if XESL_LANGUAGE_GLSL
  uint xesl_firstOneBitLow(int xesl_var_value) {
    return uint(findLSB(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_int2 xesl_var_value) {
    return xesl_uint2(findLSB(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_int3 xesl_var_value) {
    return xesl_uint3(findLSB(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_int4 xesl_var_value) {
    return xesl_uint4(findLSB(xesl_var_value));
  }
  uint xesl_firstOneBitLow(uint xesl_var_value) {
    return uint(findLSB(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_uint2 xesl_var_value) {
    return xesl_uint2(findLSB(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_uint3 xesl_var_value) {
    return xesl_uint3(findLSB(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_uint4 xesl_var_value) {
    return xesl_uint4(findLSB(xesl_var_value));
  }
  // GLSL findMSB finds the highest 0 for a negative value.
  uint xesl_firstOneBitHigh(int xesl_var_value) {
    return uint(findMSB(uint(xesl_var_value)));
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_int2 xesl_var_value) {
    return xesl_uint2(findMSB(xesl_uint2(xesl_var_value)));
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_int3 xesl_var_value) {
    return xesl_uint3(findMSB(xesl_uint3(xesl_var_value)));
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_int4 xesl_var_value) {
    return xesl_uint4(findMSB(xesl_uint4(xesl_var_value)));
  }
  uint xesl_firstOneBitHigh(uint xesl_var_value) {
    return uint(findMSB(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_uint2 xesl_var_value) {
    return xesl_uint2(findMSB(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_uint3 xesl_var_value) {
    return xesl_uint3(findMSB(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_uint4 xesl_var_value) {
    return xesl_uint4(findMSB(xesl_var_value));
  }
#elif XESL_LANGUAGE_HLSL
  uint xesl_firstOneBitLow(int xesl_var_value) {
    return uint(firstbitlow(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_int2 xesl_var_value) {
    return xesl_uint2(firstbitlow(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_int3 xesl_var_value) {
    return xesl_uint3(firstbitlow(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_int4 xesl_var_value) {
    return xesl_uint4(firstbitlow(xesl_var_value));
  }
  uint xesl_firstOneBitLow(uint xesl_var_value) {
    return firstbitlow(xesl_var_value);
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_uint2 xesl_var_value) {
    return firstbitlow(xesl_var_value);
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_uint3 xesl_var_value) {
    return firstbitlow(xesl_var_value);
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_uint4 xesl_var_value) {
    return firstbitlow(xesl_var_value);
  }
  // HLSL firstbithigh finds the highest 0 for a negative value.
  uint xesl_firstOneBitHigh(int xesl_var_value) {
    return uint(firstbithigh(uint(xesl_var_value)));
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_int2 xesl_var_value) {
    return xesl_uint2(firstbithigh(xesl_uint2(xesl_var_value)));
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_int3 xesl_var_value) {
    return xesl_uint3(firstbithigh(xesl_uint3(xesl_var_value)));
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_int4 xesl_var_value) {
    return xesl_uint4(firstbithigh(xesl_uint4(xesl_var_value)));
  }
  uint xesl_firstOneBitHigh(uint xesl_var_value) {
    return firstbithigh(xesl_var_value);
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_uint2 xesl_var_value) {
    return firstbithigh(xesl_var_value);
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_uint3 xesl_var_value) {
    return firstbithigh(xesl_var_value);
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_uint4 xesl_var_value) {
    return firstbithigh(xesl_var_value);
  }
#elif XESL_LANGUAGE_MSL
  uint xesl_firstOneBitLow(int xesl_var_value) {
    return uint(ctz(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_int2 xesl_var_value) {
    return xesl_uint2(ctz(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_int3 xesl_var_value) {
    return xesl_uint3(ctz(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_int4 xesl_var_value) {
    return xesl_uint4(ctz(xesl_var_value));
  }
  uint xesl_firstOneBitLow(uint xesl_var_value) {
    return ctz(xesl_var_value);
  }
  xesl_uint2 xesl_firstOneBitLow(xesl_uint2 xesl_var_value) {
    return ctz(xesl_var_value);
  }
  xesl_uint3 xesl_firstOneBitLow(xesl_uint3 xesl_var_value) {
    return ctz(xesl_var_value);
  }
  xesl_uint4 xesl_firstOneBitLow(xesl_uint4 xesl_var_value) {
    return ctz(xesl_var_value);
  }
  uint xesl_firstOneBitHigh(int xesl_var_value) {
    return 32u - uint(clz(xesl_var_value));
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_int2 xesl_var_value) {
    return xesl_uint_x2(32u) - xesl_uint2(clz(xesl_var_value));
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_int3 xesl_var_value) {
    return xesl_uint_x3(32u) - xesl_uint3(clz(xesl_var_value));
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_int4 xesl_var_value) {
    return xesl_uint_x4(32u) - xesl_uint4(clz(xesl_var_value));
  }
  uint xesl_firstOneBitHigh(uint xesl_var_value) {
    return 32u - clz(xesl_var_value);
  }
  xesl_uint2 xesl_firstOneBitHigh(xesl_uint2 xesl_var_value) {
    return xesl_uint_x2(32u) - clz(xesl_var_value);
  }
  xesl_uint3 xesl_firstOneBitHigh(xesl_uint3 xesl_var_value) {
    return xesl_uint_x3(32u) - clz(xesl_var_value);
  }
  xesl_uint4 xesl_firstOneBitHigh(xesl_uint4 xesl_var_value) {
    return xesl_uint_x4(32u) - clz(xesl_var_value);
  }
#else
  #error Bit count operations not defined for the target language.
#endif  // XESL_LANGUAGE

#if XESL_LANGUAGE_GLSL
  #define xesl_packHalf2x16 packHalf2x16
  #define xesl_unpackHalf2x16 unpackHalf2x16
#elif XESL_LANGUAGE_HLSL
  uint xesl_packHalf2x16(xesl_float2 xesl_var_value) {
    return f32tof16(xesl_var_value.x) | (f32tof16(xesl_var_value.y) << 16u);
  }
  xesl_float2 xesl_unpackHalf2x16(uint xesl_var_value) {
    return f16tof32(xesl_uint_x2(xesl_var_value) >> xesl_uint2(0u, 16u));
  }
#elif XESL_LANGUAGE_MSL
  uint xesl_packHalf2x16(xesl_float2 xesl_var_value) {
    return uint(as_type<ushort>(half(xesl_var_value.x))) |
           (uint(as_type<ushort>(half(xesl_var_value.y))) << 16u);
  }
  xesl_float2 xesl_unpackHalf2x16(uint xesl_var_value) {
    return xesl_float2(as_type<half2>(ushort2(
        xesl_uint_x2(xesl_var_value) >> xesl_uint2(0u, 16u))));
  }
#else
  #error xesl_packHalf2x16 not defined for the target language.
#endif  // XESL_LANGUAGE

#endif  // XENIA_UI_SHADERS_XESL_XESLI_
