blob: bd5fc340dccfc39bb99182719419e99c6fcede98 [file] [log] [blame]
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// PackedGLEnums_autogen.h:
// Declares ANGLE-specific enums classes for GLEnum and functions operating
// on them.
#ifndef COMMON_PACKEDGLENUMS_H_
#define COMMON_PACKEDGLENUMS_H_
#include "common/PackedEGLEnums_autogen.h"
#include "common/PackedGLEnums_autogen.h"
#include <array>
#include <bitset>
#include <cstddef>
#include <EGL/egl.h>
#include "common/bitset_utils.h"
namespace angle
{
// Return the number of elements of a packed enum, including the InvalidEnum element.
template <typename E>
constexpr size_t EnumSize()
{
using UnderlyingType = typename std::underlying_type<E>::type;
return static_cast<UnderlyingType>(E::EnumCount);
}
// Implementation of AllEnums which allows iterating over all the possible values for a packed enums
// like so:
// for (auto value : AllEnums<MyPackedEnum>()) {
// // Do something with the enum.
// }
template <typename E>
class EnumIterator final
{
private:
using UnderlyingType = typename std::underlying_type<E>::type;
public:
EnumIterator(E value) : mValue(static_cast<UnderlyingType>(value)) {}
EnumIterator &operator++()
{
mValue++;
return *this;
}
bool operator==(const EnumIterator &other) const { return mValue == other.mValue; }
bool operator!=(const EnumIterator &other) const { return mValue != other.mValue; }
E operator*() const { return static_cast<E>(mValue); }
private:
UnderlyingType mValue;
};
template <typename E>
struct AllEnums
{
EnumIterator<E> begin() const { return {static_cast<E>(0)}; }
EnumIterator<E> end() const { return {E::InvalidEnum}; }
};
// PackedEnumMap<E, T> is like an std::array<T, E::EnumCount> but is indexed with enum values. It
// implements all of the std::array interface except with enum values instead of indices.
template <typename E, typename T, size_t MaxSize = EnumSize<E>()>
class PackedEnumMap
{
using UnderlyingType = typename std::underlying_type<E>::type;
using Storage = std::array<T, MaxSize>;
public:
using InitPair = std::pair<E, T>;
constexpr PackedEnumMap() = default;
constexpr PackedEnumMap(std::initializer_list<InitPair> init) : mPrivateData{}
{
// We use a for loop instead of range-for to work around a limitation in MSVC.
for (const InitPair *it = init.begin(); it != init.end(); ++it)
{
// This horrible const_cast pattern is necessary to work around a constexpr limitation.
// See https://stackoverflow.com/q/34199774/ . Note that it should be fixed with C++17.
const_cast<T &>(const_cast<const Storage &>(
mPrivateData)[static_cast<UnderlyingType>(it->first)]) = it->second;
}
}
// types:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using size_type = size_t;
using difference_type = ptrdiff_t;
using iterator = typename Storage::iterator;
using const_iterator = typename Storage::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// No explicit construct/copy/destroy for aggregate type
void fill(const T &u) { mPrivateData.fill(u); }
void swap(PackedEnumMap<E, T, MaxSize> &a) noexcept { mPrivateData.swap(a.mPrivateData); }
// iterators:
iterator begin() noexcept { return mPrivateData.begin(); }
const_iterator begin() const noexcept { return mPrivateData.begin(); }
iterator end() noexcept { return mPrivateData.end(); }
const_iterator end() const noexcept { return mPrivateData.end(); }
reverse_iterator rbegin() noexcept { return mPrivateData.rbegin(); }
const_reverse_iterator rbegin() const noexcept { return mPrivateData.rbegin(); }
reverse_iterator rend() noexcept { return mPrivateData.rend(); }
const_reverse_iterator rend() const noexcept { return mPrivateData.rend(); }
// capacity:
constexpr size_type size() const noexcept { return mPrivateData.size(); }
constexpr size_type max_size() const noexcept { return mPrivateData.max_size(); }
constexpr bool empty() const noexcept { return mPrivateData.empty(); }
// element access:
reference operator[](E n)
{
ASSERT(static_cast<size_t>(n) < mPrivateData.size());
return mPrivateData[static_cast<UnderlyingType>(n)];
}
constexpr const_reference operator[](E n) const
{
ASSERT(static_cast<size_t>(n) < mPrivateData.size());
return mPrivateData[static_cast<UnderlyingType>(n)];
}
const_reference at(E n) const { return mPrivateData.at(static_cast<UnderlyingType>(n)); }
reference at(E n) { return mPrivateData.at(static_cast<UnderlyingType>(n)); }
reference front() { return mPrivateData.front(); }
const_reference front() const { return mPrivateData.front(); }
reference back() { return mPrivateData.back(); }
const_reference back() const { return mPrivateData.back(); }
T *data() noexcept { return mPrivateData.data(); }
const T *data() const noexcept { return mPrivateData.data(); }
bool operator==(const PackedEnumMap &rhs) const { return mPrivateData == rhs.mPrivateData; }
bool operator!=(const PackedEnumMap &rhs) const { return mPrivateData != rhs.mPrivateData; }
private:
Storage mPrivateData;
};
// PackedEnumBitSetE> is like an std::bitset<E::EnumCount> but is indexed with enum values. It
// implements the std::bitset interface except with enum values instead of indices.
template <typename E, typename DataT = uint32_t>
using PackedEnumBitSet = BitSetT<EnumSize<E>(), DataT, E>;
} // namespace angle
namespace gl
{
TextureType TextureTargetToType(TextureTarget target);
TextureTarget NonCubeTextureTypeToTarget(TextureType type);
TextureTarget CubeFaceIndexToTextureTarget(size_t face);
size_t CubeMapTextureTargetToFaceIndex(TextureTarget target);
bool IsCubeMapFaceTarget(TextureTarget target);
constexpr TextureTarget kCubeMapTextureTargetMin = TextureTarget::CubeMapPositiveX;
constexpr TextureTarget kCubeMapTextureTargetMax = TextureTarget::CubeMapNegativeZ;
constexpr TextureTarget kAfterCubeMapTextureTargetMax =
static_cast<TextureTarget>(static_cast<uint8_t>(kCubeMapTextureTargetMax) + 1);
struct AllCubeFaceTextureTargets
{
angle::EnumIterator<TextureTarget> begin() const { return kCubeMapTextureTargetMin; }
angle::EnumIterator<TextureTarget> end() const { return kAfterCubeMapTextureTargetMax; }
};
constexpr ShaderType kGLES2ShaderTypeMin = ShaderType::Vertex;
constexpr ShaderType kGLES2ShaderTypeMax = ShaderType::Fragment;
constexpr ShaderType kAfterGLES2ShaderTypeMax =
static_cast<ShaderType>(static_cast<uint8_t>(kGLES2ShaderTypeMax) + 1);
struct AllGLES2ShaderTypes
{
angle::EnumIterator<ShaderType> begin() const { return kGLES2ShaderTypeMin; }
angle::EnumIterator<ShaderType> end() const { return kAfterGLES2ShaderTypeMax; }
};
constexpr ShaderType kShaderTypeMin = ShaderType::Vertex;
constexpr ShaderType kShaderTypeMax = ShaderType::Compute;
constexpr ShaderType kAfterShaderTypeMax =
static_cast<ShaderType>(static_cast<uint8_t>(kShaderTypeMax) + 1);
struct AllShaderTypes
{
angle::EnumIterator<ShaderType> begin() const { return kShaderTypeMin; }
angle::EnumIterator<ShaderType> end() const { return kAfterShaderTypeMax; }
};
constexpr size_t kGraphicsShaderCount = static_cast<size_t>(ShaderType::EnumCount) - 1u;
// Arrange the shader types in the order of rendering pipeline
constexpr std::array<ShaderType, kGraphicsShaderCount> kAllGraphicsShaderTypes = {
ShaderType::Vertex, ShaderType::Geometry, ShaderType::Fragment};
using ShaderBitSet = angle::PackedEnumBitSet<ShaderType, uint8_t>;
static_assert(sizeof(ShaderBitSet) == sizeof(uint8_t), "Unexpected size");
template <typename T>
using ShaderMap = angle::PackedEnumMap<ShaderType, T>;
TextureType SamplerTypeToTextureType(GLenum samplerType);
bool IsMultisampled(gl::TextureType type);
bool IsArrayTextureType(gl::TextureType type);
enum class PrimitiveMode : uint8_t
{
Points = 0x0,
Lines = 0x1,
LineLoop = 0x2,
LineStrip = 0x3,
Triangles = 0x4,
TriangleStrip = 0x5,
TriangleFan = 0x6,
Unused1 = 0x7,
Unused2 = 0x8,
Unused3 = 0x9,
LinesAdjacency = 0xA,
LineStripAdjacency = 0xB,
TrianglesAdjacency = 0xC,
TriangleStripAdjacency = 0xD,
InvalidEnum = 0xE,
EnumCount = 0xE,
};
template <>
constexpr PrimitiveMode FromGLenum<PrimitiveMode>(GLenum from)
{
if (from >= static_cast<GLenum>(PrimitiveMode::EnumCount))
{
return PrimitiveMode::InvalidEnum;
}
return static_cast<PrimitiveMode>(from);
}
constexpr GLenum ToGLenum(PrimitiveMode from)
{
return static_cast<GLenum>(from);
}
static_assert(ToGLenum(PrimitiveMode::Points) == GL_POINTS, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::Lines) == GL_LINES, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::LineLoop) == GL_LINE_LOOP, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::LineStrip) == GL_LINE_STRIP, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::Triangles) == GL_TRIANGLES, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::TriangleStrip) == GL_TRIANGLE_STRIP,
"PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::TriangleFan) == GL_TRIANGLE_FAN, "PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::LinesAdjacency) == GL_LINES_ADJACENCY,
"PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::LineStripAdjacency) == GL_LINE_STRIP_ADJACENCY,
"PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::TrianglesAdjacency) == GL_TRIANGLES_ADJACENCY,
"PrimitiveMode violation");
static_assert(ToGLenum(PrimitiveMode::TriangleStripAdjacency) == GL_TRIANGLE_STRIP_ADJACENCY,
"PrimitiveMode violation");
std::ostream &operator<<(std::ostream &os, PrimitiveMode value);
enum class DrawElementsType : size_t
{
UnsignedByte = 0,
UnsignedShort = 1,
UnsignedInt = 2,
InvalidEnum = 3,
EnumCount = 3,
};
template <>
constexpr DrawElementsType FromGLenum<DrawElementsType>(GLenum from)
{
GLenum scaled = (from - GL_UNSIGNED_BYTE);
// This code sequence generates a ROR instruction on x86/arm. We want to check if the lowest bit
// of scaled is set and if (scaled >> 1) is greater than a non-pot value. If we rotate the
// lowest bit to the hightest bit both conditions can be checked with a single test.
static_assert(sizeof(GLenum) == 4, "Update (scaled << 31) to sizeof(GLenum) * 8 - 1");
GLenum packed = (scaled >> 1) | (scaled << 31);
// operator ? with a simple assignment usually translates to a cmov instruction and thus avoids
// a branch.
packed = (packed >= static_cast<GLenum>(DrawElementsType::EnumCount))
? static_cast<GLenum>(DrawElementsType::InvalidEnum)
: packed;
return static_cast<DrawElementsType>(packed);
}
constexpr GLenum ToGLenum(DrawElementsType from)
{
return ((static_cast<GLenum>(from) << 1) + GL_UNSIGNED_BYTE);
}
#define ANGLE_VALIDATE_PACKED_ENUM(type, packed, glenum) \
static_assert(ToGLenum(type::packed) == glenum, #type " violation"); \
static_assert(FromGLenum<type>(glenum) == type::packed, #type " violation")
ANGLE_VALIDATE_PACKED_ENUM(DrawElementsType, UnsignedByte, GL_UNSIGNED_BYTE);
ANGLE_VALIDATE_PACKED_ENUM(DrawElementsType, UnsignedShort, GL_UNSIGNED_SHORT);
ANGLE_VALIDATE_PACKED_ENUM(DrawElementsType, UnsignedInt, GL_UNSIGNED_INT);
std::ostream &operator<<(std::ostream &os, DrawElementsType value);
enum class VertexAttribType
{
Byte = 0, // GLenum == 0x1400
UnsignedByte = 1, // GLenum == 0x1401
Short = 2, // GLenum == 0x1402
UnsignedShort = 3, // GLenum == 0x1403
Int = 4, // GLenum == 0x1404
UnsignedInt = 5, // GLenum == 0x1405
Float = 6, // GLenum == 0x1406
Unused1 = 7, // GLenum == 0x1407
Unused2 = 8, // GLenum == 0x1408
Unused3 = 9, // GLenum == 0x1409
Unused4 = 10, // GLenum == 0x140A
HalfFloat = 11, // GLenum == 0x140B
Fixed = 12, // GLenum == 0x140C
MaxBasicType = 12,
UnsignedInt2101010 = 13, // GLenum == 0x8368
HalfFloatOES = 14, // GLenum == 0x8D61
Int2101010 = 15, // GLenum == 0x8D9F
UnsignedInt1010102 = 16, // GLenum == 0x8DF6
Int1010102 = 17, // GLenum == 0x8DF7
InvalidEnum = 18,
EnumCount = 18,
};
template <>
constexpr VertexAttribType FromGLenum<VertexAttribType>(GLenum from)
{
GLenum packed = from - GL_BYTE;
if (packed <= static_cast<GLenum>(VertexAttribType::MaxBasicType))
return static_cast<VertexAttribType>(packed);
if (from == GL_UNSIGNED_INT_2_10_10_10_REV)
return VertexAttribType::UnsignedInt2101010;
if (from == GL_HALF_FLOAT_OES)
return VertexAttribType::HalfFloatOES;
if (from == GL_INT_2_10_10_10_REV)
return VertexAttribType::Int2101010;
if (from == GL_UNSIGNED_INT_10_10_10_2_OES)
return VertexAttribType::UnsignedInt1010102;
if (from == GL_INT_10_10_10_2_OES)
return VertexAttribType::Int1010102;
return VertexAttribType::InvalidEnum;
}
constexpr GLenum ToGLenum(VertexAttribType from)
{
// This could be optimized using a constexpr table.
if (from == VertexAttribType::Int2101010)
return GL_INT_2_10_10_10_REV;
if (from == VertexAttribType::HalfFloatOES)
return GL_HALF_FLOAT_OES;
if (from == VertexAttribType::UnsignedInt2101010)
return GL_UNSIGNED_INT_2_10_10_10_REV;
if (from == VertexAttribType::UnsignedInt1010102)
return GL_UNSIGNED_INT_10_10_10_2_OES;
if (from == VertexAttribType::Int1010102)
return GL_INT_10_10_10_2_OES;
return static_cast<GLenum>(from) + GL_BYTE;
}
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Byte, GL_BYTE);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, UnsignedByte, GL_UNSIGNED_BYTE);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Short, GL_SHORT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, UnsignedShort, GL_UNSIGNED_SHORT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Int, GL_INT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, UnsignedInt, GL_UNSIGNED_INT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Float, GL_FLOAT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, HalfFloat, GL_HALF_FLOAT);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Fixed, GL_FIXED);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Int2101010, GL_INT_2_10_10_10_REV);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, HalfFloatOES, GL_HALF_FLOAT_OES);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, UnsignedInt2101010, GL_UNSIGNED_INT_2_10_10_10_REV);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, Int1010102, GL_INT_10_10_10_2_OES);
ANGLE_VALIDATE_PACKED_ENUM(VertexAttribType, UnsignedInt1010102, GL_UNSIGNED_INT_10_10_10_2_OES);
std::ostream &operator<<(std::ostream &os, VertexAttribType value);
// Typesafe object handles.
template <typename T>
struct ResourceTypeToID;
template <typename T>
struct IsResourceIDType;
// Clang Format doesn't like the following X macro.
// clang-format off
#define ANGLE_ID_TYPES_OP(X) \
X(Buffer) \
X(FenceNV) \
X(Framebuffer) \
X(MemoryObject) \
X(Path) \
X(ProgramPipeline) \
X(Query) \
X(Renderbuffer) \
X(Sampler) \
X(Semaphore) \
X(Texture) \
X(TransformFeedback) \
X(VertexArray)
// clang-format on
#define ANGLE_DEFINE_ID_TYPE(Type) \
class Type; \
struct Type##ID \
{ \
GLuint value; \
}; \
template <> \
struct ResourceTypeToID<Type> \
{ \
using IDType = Type##ID; \
}; \
template <> \
struct IsResourceIDType<Type##ID> \
{ \
static constexpr bool value = true; \
};
ANGLE_ID_TYPES_OP(ANGLE_DEFINE_ID_TYPE)
#undef ANGLE_DEFINE_ID_TYPE
#undef ANGLE_ID_TYPES_OP
// Shaders and programs are a bit special as they share IDs.
struct ShaderProgramID
{
GLuint value;
};
template <>
struct IsResourceIDType<ShaderProgramID>
{
constexpr static bool value = true;
};
class Shader;
template <>
struct ResourceTypeToID<Shader>
{
using IDType = ShaderProgramID;
};
class Program;
template <>
struct ResourceTypeToID<Program>
{
using IDType = ShaderProgramID;
};
template <typename T>
struct ResourceTypeToID
{
using IDType = void;
};
template <typename T>
struct IsResourceIDType
{
static constexpr bool value = false;
};
template <typename T>
bool ValueEquals(T lhs, T rhs)
{
return lhs.value == rhs.value;
}
// Util funcs for resourceIDs
template <typename T>
typename std::enable_if<IsResourceIDType<T>::value, bool>::type operator==(const T &lhs,
const T &rhs)
{
return lhs.value == rhs.value;
}
template <typename T>
typename std::enable_if<IsResourceIDType<T>::value, bool>::type operator!=(const T &lhs,
const T &rhs)
{
return lhs.value != rhs.value;
}
template <typename T>
typename std::enable_if<IsResourceIDType<T>::value, bool>::type operator<(const T &lhs,
const T &rhs)
{
return lhs.value < rhs.value;
}
// Used to unbox typed values.
template <typename ResourceIDType>
GLuint GetIDValue(ResourceIDType id);
template <>
inline GLuint GetIDValue(GLuint id)
{
return id;
}
template <typename ResourceIDType>
inline GLuint GetIDValue(ResourceIDType id)
{
return id.value;
}
// First case: handling packed enums.
template <typename EnumT, typename FromT>
typename std::enable_if<std::is_enum<EnumT>::value, EnumT>::type FromGL(FromT from)
{
return FromGLenum<EnumT>(from);
}
// Second case: handling non-pointer resource ids.
template <typename EnumT, typename FromT>
typename std::enable_if<!std::is_pointer<FromT>::value && !std::is_enum<EnumT>::value, EnumT>::type
FromGL(FromT from)
{
return {from};
}
// Third case: handling pointer resource ids.
template <typename EnumT, typename FromT>
typename std::enable_if<std::is_pointer<FromT>::value && !std::is_enum<EnumT>::value, EnumT>::type
FromGL(FromT from)
{
return reinterpret_cast<EnumT>(from);
}
} // namespace gl
namespace egl
{
MessageType ErrorCodeToMessageType(EGLint errorCode);
} // namespace egl
namespace egl_gl
{
gl::TextureTarget EGLCubeMapTargetToCubeMapTarget(EGLenum eglTarget);
gl::TextureTarget EGLImageTargetToTextureTarget(EGLenum eglTarget);
gl::TextureType EGLTextureTargetToTextureType(EGLenum eglTarget);
} // namespace egl_gl
#endif // COMMON_PACKEDGLENUMS_H_