text: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #ifndef TNT_FILAMENT_DRIVER_GLUTILS_H
  #define TNT_FILAMENT_DRIVER_GLUTILS_H
  
  #include <utils/compiler.h>
  #include <utils/Log.h>
  
  #include <filament/driver/DriverEnums.h>
  
  #include "driver/opengl/gl_headers.h"
  
  namespace filament {
  namespace GLUtils {
  
  void checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
  void checkFramebufferStatus(utils::io::ostream& out, const char* function, size_t line) noexcept;
  
  #ifdef NDEBUG
  #define CHECK_GL_ERROR(out)
  #define CHECK_GL_FRAMEBUFFER_STATUS(out)
  #else
  #define CHECK_GL_ERROR(out) { GLUtils::checkGLError(out, __PRETTY_FUNCTION__, __LINE__); }
  #define CHECK_GL_FRAMEBUFFER_STATUS(out) { GLUtils::checkFramebufferStatus(out, __PRETTY_FUNCTION__, __LINE__); }
  #endif
  
  constexpr inline GLuint getComponentCount(filament::driver::ElementType type) noexcept {
      using ElementType = filament::driver::ElementType;
      switch (type) {
          case ElementType::BYTE:
          case ElementType::UBYTE:
          case ElementType::SHORT:
          case ElementType::USHORT:
          case ElementType::INT:
          case ElementType::UINT:
          case ElementType::FLOAT:
          case ElementType::HALF:
              return 1;
          case ElementType::FLOAT2:
          case ElementType::HALF2:
          case ElementType::BYTE2:
          case ElementType::UBYTE2:
          case ElementType::SHORT2:
          case ElementType::USHORT2:
              return 2;
          case ElementType::FLOAT3:
          case ElementType::HALF3:
          case ElementType::BYTE3:
          case ElementType::UBYTE3:
          case ElementType::SHORT3:
          case ElementType::USHORT3:
              return 3;
          case ElementType::FLOAT4:
          case ElementType::HALF4:
          case ElementType::BYTE4:
          case ElementType::UBYTE4:
          case ElementType::SHORT4:
          case ElementType::USHORT4:
              return 4;
      }
  }
  
  // ------------------------------------------------------------------------------------------------
  // Our enums to GLenum conversions
  // ------------------------------------------------------------------------------------------------
  
  constexpr inline GLboolean getNormalization(bool normalized) noexcept {
      return GLboolean(normalized ? GL_TRUE : GL_FALSE);
  }
  
  constexpr inline GLenum getComponentType(filament::driver::ElementType type) noexcept {
      using ElementType = filament::driver::ElementType;
      switch (type) {
          case ElementType::BYTE:
          case ElementType::BYTE2:
          case ElementType::BYTE3:
          case ElementType::BYTE4:
              return GL_BYTE;
          case ElementType::UBYTE:
          case ElementType::UBYTE2:
          case ElementType::UBYTE3:
          case ElementType::UBYTE4:
              return GL_UNSIGNED_BYTE;
          case ElementType::SHORT:
          case ElementType::SHORT2:
          case ElementType::SHORT3:
          case ElementType::SHORT4:
              return GL_SHORT;
          case ElementType::USHORT:
          case ElementType::USHORT2:
          case ElementType::USHORT3:
          case ElementType::USHORT4:
              return GL_UNSIGNED_SHORT;
          case ElementType::INT:
              return GL_INT;
          case ElementType::UINT:
              return GL_UNSIGNED_INT;
          case ElementType::FLOAT:
          case ElementType::FLOAT2:
          case ElementType::FLOAT3:
          case ElementType::FLOAT4:
              return GL_FLOAT;
          case ElementType::HALF:
          case ElementType::HALF2:
          case ElementType::HALF3:
          case ElementType::HALF4:
              return GL_HALF_FLOAT;
      }
  }
  
  constexpr inline GLenum getCubemapTarget(filament::driver::TextureCubemapFace face) noexcept {
      return GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(face);
  }
  
  constexpr inline GLenum getWrapMode(filament::driver::SamplerWrapMode mode) noexcept {
      using SamplerWrapMode = filament::driver::SamplerWrapMode;
      switch (mode) {
          case SamplerWrapMode::REPEAT:
              return GL_REPEAT;
          case SamplerWrapMode::CLAMP_TO_EDGE:
              return GL_CLAMP_TO_EDGE;
          case SamplerWrapMode::MIRRORED_REPEAT:
              return GL_MIRRORED_REPEAT;
      }
  }
  
  constexpr inline GLenum getTextureFilter(filament::driver::SamplerMinFilter filter) noexcept {
      using SamplerMinFilter = filament::driver::SamplerMinFilter;
      switch (filter) {
          case SamplerMinFilter::NEAREST:
          case SamplerMinFilter::LINEAR:
              return GL_NEAREST + GLenum(filter);
          case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
          case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
          case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
          case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
              return GL_NEAREST_MIPMAP_NEAREST
                     - GLenum(SamplerMinFilter::NEAREST_MIPMAP_NEAREST) + GLenum(filter);
      }
  }
  
  constexpr inline GLenum getTextureFilter(filament::driver::SamplerMagFilter filter) noexcept {
      return GL_NEAREST + GLenum(filter);
  }
  
  
  constexpr inline GLenum getBlendEquationMode(filament::driver::BlendEquation mode) noexcept {
      using BlendEquation = filament::driver::BlendEquation;
      switch (mode) {
          case BlendEquation::ADD:               return GL_FUNC_ADD;
          case BlendEquation::SUBTRACT:          return GL_FUNC_SUBTRACT;
          case BlendEquation::REVERSE_SUBTRACT:  return GL_FUNC_REVERSE_SUBTRACT;
          case BlendEquation::MIN:               return GL_MIN;
          case BlendEquation::MAX:               return GL_MAX;
      }
  }
  
  constexpr inline GLenum getBlendFunctionMode(filament::driver::BlendFunction mode) noexcept {
      using BlendFunction = filament::driver::BlendFunction;
      switch (mode) {
          case BlendFunction::ZERO:                  return GL_ZERO;
          case BlendFunction::ONE:                   return GL_ONE;
          case BlendFunction::SRC_COLOR:             return GL_SRC_COLOR;
          case BlendFunction::ONE_MINUS_SRC_COLOR:   return GL_ONE_MINUS_SRC_COLOR;
          case BlendFunction::DST_COLOR:             return GL_DST_COLOR;
          case BlendFunction::ONE_MINUS_DST_COLOR:   return GL_ONE_MINUS_DST_COLOR;
          case BlendFunction::SRC_ALPHA:             return GL_SRC_ALPHA;
          case BlendFunction::ONE_MINUS_SRC_ALPHA:   return GL_ONE_MINUS_SRC_ALPHA;
          case BlendFunction::DST_ALPHA:             return GL_DST_ALPHA;
          case BlendFunction::ONE_MINUS_DST_ALPHA:   return GL_ONE_MINUS_DST_ALPHA;
          case BlendFunction::SRC_ALPHA_SATURATE:    return GL_SRC_ALPHA_SATURATE;
      }
  }
  
  constexpr inline GLenum getTextureCompareMode(filament::driver::SamplerCompareMode mode) noexcept {
      return mode == filament::driver::SamplerCompareMode::NONE ?
             GL_NONE : GL_COMPARE_REF_TO_TEXTURE;
  }
  
  constexpr inline GLenum getTextureCompareFunc(filament::driver::SamplerCompareFunc func) noexcept {
      using SamplerCompareFunc = filament::driver::SamplerCompareFunc;
      switch (func) {
          case SamplerCompareFunc::LE:    return GL_LEQUAL;
          case SamplerCompareFunc::GE:    return GL_GEQUAL;
          case SamplerCompareFunc::L:     return GL_LESS;
          case SamplerCompareFunc::G:     return GL_GREATER;
          case SamplerCompareFunc::E:     return GL_EQUAL;
          case SamplerCompareFunc::NE:    return GL_NOTEQUAL;
          case SamplerCompareFunc::A:     return GL_ALWAYS;
          case SamplerCompareFunc::N:     return GL_NEVER;
      }
  }
  
  constexpr inline GLenum getDepthFunc(driver::SamplerCompareFunc func) noexcept {
      return getTextureCompareFunc(func);
  }
  
  constexpr inline GLenum getFormat(filament::driver::PixelDataFormat format) noexcept {
      using PixelDataFormat = filament::driver::PixelDataFormat;
      switch (format) {
          case PixelDataFormat::R:                return GL_RED;
          case PixelDataFormat::R_INTEGER:        return GL_RED_INTEGER;
          case PixelDataFormat::RG:               return GL_RG;
          case PixelDataFormat::RG_INTEGER:       return GL_RG_INTEGER;
          case PixelDataFormat::RGB:              return GL_RGB;
          case PixelDataFormat::RGB_INTEGER:      return GL_RGB_INTEGER;
          case PixelDataFormat::RGBA:             return GL_RGBA;
          case PixelDataFormat::RGBA_INTEGER:     return GL_RGBA_INTEGER;
          case PixelDataFormat::RGBM:             return GL_RGBA;
          case PixelDataFormat::DEPTH_COMPONENT:  return GL_DEPTH_COMPONENT;
          case PixelDataFormat::DEPTH_STENCIL:    return GL_DEPTH_STENCIL;
          case PixelDataFormat::STENCIL_INDEX:    return GL_STENCIL_INDEX;
          case PixelDataFormat::ALPHA:            return GL_ALPHA;
      }
  }
  
  constexpr inline GLenum getType(filament::driver::PixelDataType type) noexcept {
      using PixelDataType = filament::driver::PixelDataType;
      switch (type) {
          case PixelDataType::UBYTE:              return GL_UNSIGNED_BYTE;
          case PixelDataType::BYTE:               return GL_BYTE;
          case PixelDataType::USHORT:             return GL_UNSIGNED_SHORT;
          case PixelDataType::SHORT:              return GL_SHORT;
          case PixelDataType::UINT:               return GL_UNSIGNED_INT;
          case PixelDataType::INT:                return GL_INT;
          case PixelDataType::HALF:               return GL_HALF_FLOAT;
          case PixelDataType::FLOAT:              return GL_FLOAT;
          case PixelDataType::COMPRESSED:         return 0; // should never happen
      }
  }
  
  // clang looses it on this one, and generates a huge jump table when
  // inlined. So we don't  mark it as inline (only constexpr) which solves the problem,
  // strangely, when not inlined, clang simply generates an array lookup.
  constexpr /* inline */ GLenum getInternalFormat(filament::driver::TextureFormat format) noexcept {
      using TextureFormat = filament::driver::TextureFormat;
      switch (format) {
          // 8-bits per element
          case TextureFormat::R8:                return GL_R8;
          case TextureFormat::R8_SNORM:          return GL_R8_SNORM;
          case TextureFormat::R8UI:              return GL_R8UI;
          case TextureFormat::R8I:               return GL_R8I;
          case TextureFormat::STENCIL8:          return GL_STENCIL_INDEX8;
  
          // 16-bits per element
          case TextureFormat::R16F:              return GL_R16F;
          case TextureFormat::R16UI:             return GL_R16UI;
          case TextureFormat::R16I:              return GL_R16I;
          case TextureFormat::RG8:               return GL_RG8;
          case TextureFormat::RG8_SNORM:         return GL_RG8_SNORM;
          case TextureFormat::RG8UI:             return GL_RG8UI;
          case TextureFormat::RG8I:              return GL_RG8I;
          case TextureFormat::RGB565:            return GL_RGB565;
          case TextureFormat::RGB5_A1:           return GL_RGB5_A1;
          case TextureFormat::RGBA4:             return GL_RGBA4;
          case TextureFormat::DEPTH16:           return GL_DEPTH_COMPONENT16;
  
          // 24-bits per element
          case TextureFormat::RGB8:              return GL_RGB8;
          case TextureFormat::SRGB8:             return GL_SRGB8;
          case TextureFormat::RGB8_SNORM:        return GL_RGB8_SNORM;
          case TextureFormat::RGB8UI:            return GL_RGB8UI;
          case TextureFormat::RGB8I:             return GL_RGB8I;
          case TextureFormat::DEPTH24:           return GL_DEPTH_COMPONENT24;
  
          // 32-bits per element
          case TextureFormat::R32F:              return GL_R32F;
          case TextureFormat::R32UI:             return GL_R32UI;
          case TextureFormat::R32I:              return GL_R32I;
          case TextureFormat::RG16F:             return GL_RG16F;
          case TextureFormat::RG16UI:            return GL_RG16UI;
          case TextureFormat::RG16I:             return GL_RG16I;
          case TextureFormat::R11F_G11F_B10F:    return GL_R11F_G11F_B10F;
          case TextureFormat::RGB9_E5:           return GL_RGB9_E5;
          case TextureFormat::RGBA8:             return GL_RGBA8;
          case TextureFormat::SRGB8_A8:          return GL_SRGB8_ALPHA8;
          case TextureFormat::RGBA8_SNORM:       return GL_RGBA8_SNORM;
          case TextureFormat::RGBM:              return GL_RGBA8;
          case TextureFormat::RGB10_A2:          return GL_RGB10_A2;
          case TextureFormat::RGBA8UI:           return GL_RGBA8UI;
          case TextureFormat::RGBA8I:            return GL_RGBA8I;
          case TextureFormat::DEPTH32F:          return GL_DEPTH_COMPONENT32F;
          case TextureFormat::DEPTH24_STENCIL8:  return GL_DEPTH24_STENCIL8;
          case TextureFormat::DEPTH32F_STENCIL8: return GL_DEPTH32F_STENCIL8;
  
          // 48-bits per element
          case TextureFormat::RGB16F:            return GL_RGB16F;
          case TextureFormat::RGB16UI:           return GL_RGB16UI;
          case TextureFormat::RGB16I:            return GL_RGB16I;
  
          // 64-bits per element
          case TextureFormat::RG32F:             return GL_RG32F;
          case TextureFormat::RG32UI:            return GL_RG32UI;
          case TextureFormat::RG32I:             return GL_RG32I;
          case TextureFormat::RGBA16F:           return GL_RGBA16F;
          case TextureFormat::RGBA16UI:          return GL_RGBA16UI;
          case TextureFormat::RGBA16I:           return GL_RGBA16I;
  
          // 96-bits per element
          case TextureFormat::RGB32F:            return GL_RGB32F;
          case TextureFormat::RGB32UI:           return GL_RGB32UI;
          case TextureFormat::RGB32I:            return GL_RGB32I;
  
          // 128-bits per element
          case TextureFormat::RGBA32F:           return GL_RGBA32F;
          case TextureFormat::RGBA32UI:          return GL_RGBA32UI;
          case TextureFormat::RGBA32I:           return GL_RGBA32I;
  
          // compressed formats
  #if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_4_3) || defined(GL_ARB_ES3_compatibility)
          case TextureFormat::EAC_R11:           return GL_COMPRESSED_R11_EAC;
          case TextureFormat::EAC_R11_SIGNED:    return GL_COMPRESSED_SIGNED_R11_EAC;
          case TextureFormat::EAC_RG11:          return GL_COMPRESSED_RG11_EAC;
          case TextureFormat::EAC_RG11_SIGNED:   return GL_COMPRESSED_SIGNED_RG11_EAC;
          case TextureFormat::ETC2_RGB8:         return GL_COMPRESSED_RGB8_ETC2;
          case TextureFormat::ETC2_SRGB8:        return GL_COMPRESSED_SRGB8_ETC2;
          case TextureFormat::ETC2_RGB8_A1:      return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
          case TextureFormat::ETC2_SRGB8_A1:     return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;
          case TextureFormat::ETC2_EAC_RGBA8:    return GL_COMPRESSED_RGBA8_ETC2_EAC;
          case TextureFormat::ETC2_EAC_SRGBA8:   return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
  #else
          case TextureFormat::EAC_R11:
          case TextureFormat::EAC_R11_SIGNED:
          case TextureFormat::EAC_RG11:
          case TextureFormat::EAC_RG11_SIGNED:
          case TextureFormat::ETC2_RGB8:
          case TextureFormat::ETC2_SRGB8:
          case TextureFormat::ETC2_RGB8_A1:
          case TextureFormat::ETC2_SRGB8_A1:
          case TextureFormat::ETC2_EAC_RGBA8:
          case TextureFormat::ETC2_EAC_SRGBA8:
              // this should not happen
              return 0;
  #endif
  
  #if defined(GL_EXT_texture_compression_s3tc)
          case TextureFormat::DXT1_RGB:          return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
          case TextureFormat::DXT1_RGBA:         return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
          case TextureFormat::DXT3_RGBA:         return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
          case TextureFormat::DXT5_RGBA:         return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  #else
          case TextureFormat::DXT1_RGB:
          case TextureFormat::DXT1_RGBA:
          case TextureFormat::DXT3_RGBA:
          case TextureFormat::DXT5_RGBA:
              // this should not happen
              return 0;
  #endif
      }
  };
  
  } // namespace GLUtils
  } // namespace filament
  
  #endif // TNT_FILAMENT_DRIVER_GLUTILS_H
  