// // Copyright 2012 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. // // angletypes.h : Defines a variety of structures and enum types that are used throughout libGLESv2 #ifndef LIBANGLE_ANGLETYPES_H_ #define LIBANGLE_ANGLETYPES_H_ #include #include "common/Color.h" #include "common/FixedVector.h" #include "common/MemoryBuffer.h" #include "common/PackedEnums.h" #include "common/bitset_utils.h" #include "common/hash_utils.h" #include "common/vector_utils.h" #include "libANGLE/Constants.h" #include "libANGLE/Error.h" #include "libANGLE/RefCountObject.h" #include #include #include #include #include #include #include namespace angle { template struct Extents { Extents() : width(0), height(0), depth(0) {} Extents(T width_, T height_, T depth_) : width(width_), height(height_), depth(depth_) {} Extents(const Extents &other) = default; Extents &operator=(const Extents &other) = default; bool empty() const { return (width * height * depth) == 0; } T width; T height; T depth; }; template struct Offset { public: constexpr Offset() : x(0), y(0), z(0) {} constexpr Offset(T x_in, T y_in, T z_in) : x(x_in), y(y_in), z(z_in) {} T x; T y; T z; }; template inline bool operator==(const Extents &lhs, const Extents &rhs) { return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depth == rhs.depth; } template inline bool operator!=(const Extents &lhs, const Extents &rhs) { return !(lhs == rhs); } template inline bool operator==(const Offset &a, const Offset &b) { return a.x == b.x && a.y == b.y && a.z == b.z; } template inline bool operator!=(const Offset &a, const Offset &b) { return !(a == b); } } // namespace angle namespace gl { class Buffer; class Texture; enum class Command { // The Blit command carries the bitmask of which buffers are being blit. The command passed to // the backends is: // // Blit + (Color?0x1) + (Depth?0x2) + (Stencil?0x4) Blit, BlitAll = Blit + 0x7, Clear, ClearTexture, CopyImage, Dispatch, Draw, GenerateMipmap, Invalidate, ReadPixels, TexImage, Other, }; enum CommandBlitBuffer { CommandBlitBufferColor = 0x1, CommandBlitBufferDepth = 0x2, CommandBlitBufferStencil = 0x4, }; enum class InitState { MayNeedInit, Initialized, }; template struct RectangleImpl { RectangleImpl() : x(T(0)), y(T(0)), width(T(0)), height(T(0)) {} constexpr RectangleImpl(T x_in, T y_in, T width_in, T height_in) : x(x_in), y(y_in), width(width_in), height(height_in) {} explicit constexpr RectangleImpl(const T corners[4]) : x(corners[0]), y(corners[1]), width(corners[2] - corners[0]), height(corners[3] - corners[1]) {} template explicit constexpr RectangleImpl(const RectangleImpl rect) : x(rect.x), y(rect.y), width(rect.width), height(rect.height) {} T x0() const { return x; } T y0() const { return y; } T x1() const { return x + width; } T y1() const { return y + height; } bool isReversedX() const { return width < T(0); } bool isReversedY() const { return height < T(0); } // Returns a rectangle with the same area but flipped in X, Y, neither or both. RectangleImpl flip(bool flipX, bool flipY) const { RectangleImpl flipped = *this; if (flipX) { flipped.x = flipped.x + flipped.width; flipped.width = -flipped.width; } if (flipY) { flipped.y = flipped.y + flipped.height; flipped.height = -flipped.height; } return flipped; } // Returns a rectangle with the same area but with height and width guaranteed to be positive. RectangleImpl removeReversal() const { return flip(isReversedX(), isReversedY()); } bool encloses(const RectangleImpl &inside) const { return x0() <= inside.x0() && y0() <= inside.y0() && x1() >= inside.x1() && y1() >= inside.y1(); } bool empty() const; T x; T y; T width; T height; }; template bool operator==(const RectangleImpl &a, const RectangleImpl &b); template bool operator!=(const RectangleImpl &a, const RectangleImpl &b); using Rectangle = RectangleImpl; // Calculate the intersection of two rectangles. Returns false if the intersection is empty. [[nodiscard]] bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection); // Calculate the smallest rectangle that covers both rectangles. This rectangle may cover areas // not covered by the two rectangles, for example in this situation: // // +--+ +----+ // | ++-+ -> | | // +-++ | | | // +--+ +----+ // void GetEnclosingRectangle(const Rectangle &rect1, const Rectangle &rect2, Rectangle *rectUnion); // Extend the source rectangle to cover parts (or all of) the second rectangle, in such a way that // no area is covered that isn't covered by both rectangles. For example: // // +--+ +--+ // source --> | | | | // ++--+-+ -> | | // |+--+ | | | // +-----+ +--+ // void ExtendRectangle(const Rectangle &source, const Rectangle &extend, Rectangle *extended); using Extents = angle::Extents; using Offset = angle::Offset; constexpr Offset kOffsetZero(0, 0, 0); struct Box { Box() : x(0), y(0), z(0), width(0), height(0), depth(0) {} Box(int x_in, int y_in, int z_in, int width_in, int height_in, int depth_in) : x(x_in), y(y_in), z(z_in), width(width_in), height(height_in), depth(depth_in) {} template Box(const O &offset, const E &size) : x(offset.x), y(offset.y), z(offset.z), width(size.width), height(size.height), depth(size.depth) {} bool valid() const; bool operator==(const Box &other) const; bool operator!=(const Box &other) const; Rectangle toRect() const; // Whether the Box has offset 0 and the same extents as argument. bool coversSameExtent(const Extents &size) const; bool contains(const Box &other) const; size_t volume() const; void extend(const Box &other); int x; int y; int z; int width; int height; int depth; }; struct RasterizerState final { // This will zero-initialize the struct, including padding. RasterizerState(); RasterizerState(const RasterizerState &other); RasterizerState &operator=(const RasterizerState &other); bool cullFace; CullFaceMode cullMode; GLenum frontFace; PolygonMode polygonMode; bool polygonOffsetPoint; bool polygonOffsetLine; bool polygonOffsetFill; GLfloat polygonOffsetFactor; GLfloat polygonOffsetUnits; GLfloat polygonOffsetClamp; bool depthClamp; // pointDrawMode/multiSample are only used in the D3D back-end right now. bool pointDrawMode; bool multiSample; bool rasterizerDiscard; bool dither; bool isPolygonOffsetEnabled() const { static_assert(static_cast(PolygonMode::Point) == 0, "PolygonMode::Point"); static_assert(static_cast(PolygonMode::Line) == 1, "PolygonMode::Line"); static_assert(static_cast(PolygonMode::Fill) == 2, "PolygonMode::Fill"); return (1 << static_cast(polygonMode)) & ((polygonOffsetPoint << 0) | (polygonOffsetLine << 1) | (polygonOffsetFill << 2)); } }; bool operator==(const RasterizerState &a, const RasterizerState &b); bool operator!=(const RasterizerState &a, const RasterizerState &b); struct BlendState final { // This will zero-initialize the struct, including padding. BlendState(); BlendState(const BlendState &other); bool blend; GLenum sourceBlendRGB; GLenum destBlendRGB; GLenum sourceBlendAlpha; GLenum destBlendAlpha; GLenum blendEquationRGB; GLenum blendEquationAlpha; bool colorMaskRed; bool colorMaskGreen; bool colorMaskBlue; bool colorMaskAlpha; }; bool operator==(const BlendState &a, const BlendState &b); bool operator!=(const BlendState &a, const BlendState &b); struct DepthStencilState final { // This will zero-initialize the struct, including padding. DepthStencilState(); DepthStencilState(const DepthStencilState &other); DepthStencilState &operator=(const DepthStencilState &other); bool isDepthMaskedOut() const; bool isStencilMaskedOut(GLuint framebufferStencilSize) const; bool isStencilNoOp(GLuint framebufferStencilSize) const; bool isStencilBackNoOp(GLuint framebufferStencilSize) const; bool depthTest; GLenum depthFunc; bool depthMask; bool stencilTest; GLenum stencilFunc; GLuint stencilMask; GLenum stencilFail; GLenum stencilPassDepthFail; GLenum stencilPassDepthPass; GLuint stencilWritemask; GLenum stencilBackFunc; GLuint stencilBackMask; GLenum stencilBackFail; GLenum stencilBackPassDepthFail; GLenum stencilBackPassDepthPass; GLuint stencilBackWritemask; }; bool operator==(const DepthStencilState &a, const DepthStencilState &b); bool operator!=(const DepthStencilState &a, const DepthStencilState &b); // Packs a sampler state for completeness checks: // * minFilter: 5 values (3 bits) // * magFilter: 2 values (1 bit) // * wrapS: 3 values (2 bits) // * wrapT: 3 values (2 bits) // * compareMode: 1 bit (for == GL_NONE). // This makes a total of 9 bits. We can pack this easily into 32 bits: // * minFilter: 8 bits // * magFilter: 8 bits // * wrapS: 8 bits // * wrapT: 4 bits // * compareMode: 4 bits struct PackedSamplerCompleteness { uint8_t minFilter; uint8_t magFilter; uint8_t wrapS; uint8_t wrapTCompareMode; }; static_assert(sizeof(PackedSamplerCompleteness) == sizeof(uint32_t), "Unexpected size"); // State from Table 6.10 (state per sampler object) class SamplerState final { public: // This will zero-initialize the struct, including padding. SamplerState(); SamplerState(const SamplerState &other); SamplerState &operator=(const SamplerState &other); static SamplerState CreateDefaultForTarget(TextureType type); GLenum getMinFilter() const { return mMinFilter; } bool setMinFilter(GLenum minFilter); GLenum getMagFilter() const { return mMagFilter; } bool setMagFilter(GLenum magFilter); GLenum getWrapS() const { return mWrapS; } bool setWrapS(GLenum wrapS); GLenum getWrapT() const { return mWrapT; } bool setWrapT(GLenum wrapT); GLenum getWrapR() const { return mWrapR; } bool setWrapR(GLenum wrapR); bool usesBorderColor() const { return mWrapS == GL_CLAMP_TO_BORDER || mWrapT == GL_CLAMP_TO_BORDER || mWrapR == GL_CLAMP_TO_BORDER; } float getMaxAnisotropy() const { return mMaxAnisotropy; } bool setMaxAnisotropy(float maxAnisotropy); GLfloat getMinLod() const { return mMinLod; } bool setMinLod(GLfloat minLod); GLfloat getMaxLod() const { return mMaxLod; } bool setMaxLod(GLfloat maxLod); GLenum getCompareMode() const { return mCompareMode; } bool setCompareMode(GLenum compareMode); GLenum getCompareFunc() const { return mCompareFunc; } bool setCompareFunc(GLenum compareFunc); GLenum getSRGBDecode() const { return mSRGBDecode; } bool setSRGBDecode(GLenum sRGBDecode); bool setBorderColor(const ColorGeneric &color); const ColorGeneric &getBorderColor() const { return mBorderColor; } bool sameCompleteness(const SamplerState &samplerState) const { return mCompleteness.packed == samplerState.mCompleteness.packed; } private: void updateWrapTCompareMode(); GLenum mMinFilter; GLenum mMagFilter; GLenum mWrapS; GLenum mWrapT; GLenum mWrapR; // From EXT_texture_filter_anisotropic float mMaxAnisotropy; GLfloat mMinLod; GLfloat mMaxLod; GLenum mCompareMode; GLenum mCompareFunc; GLenum mSRGBDecode; ColorGeneric mBorderColor; union Completeness { uint32_t packed; PackedSamplerCompleteness typed; }; Completeness mCompleteness; }; bool operator==(const SamplerState &a, const SamplerState &b); bool operator!=(const SamplerState &a, const SamplerState &b); struct DrawArraysIndirectCommand { GLuint count; GLuint instanceCount; GLuint first; GLuint baseInstance; }; static_assert(sizeof(DrawArraysIndirectCommand) == 16, "Unexpected size of DrawArraysIndirectCommand"); struct DrawElementsIndirectCommand { GLuint count; GLuint primCount; GLuint firstIndex; GLint baseVertex; GLuint baseInstance; }; static_assert(sizeof(DrawElementsIndirectCommand) == 20, "Unexpected size of DrawElementsIndirectCommand"); struct ImageUnit { ImageUnit(); ImageUnit(const ImageUnit &other); ~ImageUnit(); BindingPointer texture; GLint level; GLboolean layered; GLint layer; GLenum access; GLenum format; }; using ImageUnitTextureTypeMap = std::map; struct PixelStoreStateBase { GLint alignment = 4; GLint rowLength = 0; GLint skipRows = 0; GLint skipPixels = 0; GLint imageHeight = 0; GLint skipImages = 0; }; struct PixelUnpackState : PixelStoreStateBase {}; struct PixelPackState : PixelStoreStateBase { bool reverseRowOrder = false; }; // Used in VertexArray. using VertexArrayBufferBindingMask = angle::BitSet; // Used in Program and VertexArray. using AttributesMask = angle::BitSet; // Used in Program using ProgramUniformBlockMask = angle::BitSet; template using ProgramUniformBlockArray = std::array; template using UniformBufferBindingArray = std::array; // Used in Framebuffer / Program using DrawBufferMask = angle::BitSet8; class BlendStateExt final { static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS == 8, "Only up to 8 draw buffers supported."); public: template struct StorageType final { static_assert(ElementCount <= 256, "ElementCount cannot exceed 256."); #if defined(ANGLE_IS_64_BIT_CPU) // Always use uint64_t on 64-bit systems static constexpr size_t kBits = 8; #else static constexpr size_t kBits = ElementCount > 16 ? 8 : 4; #endif using Type = typename std::conditional::type; static constexpr Type kMaxValueMask = (kBits == 8) ? 0xFF : 0xF; static constexpr Type GetMask(const size_t drawBuffers) { ASSERT(drawBuffers > 0); ASSERT(drawBuffers <= IMPLEMENTATION_MAX_DRAW_BUFFERS); return static_cast(0xFFFFFFFFFFFFFFFFull >> (64 - drawBuffers * kBits)); } // A multiplier that is used to replicate 4- or 8-bit value 8 times. static constexpr Type kReplicator = (kBits == 8) ? 0x0101010101010101ull : 0x11111111; // Extract packed `Bits`-bit value of index `index`. `values` variable contains up to 8 // packed values. static constexpr ElementType GetValueIndexed(const size_t index, const Type values) { ASSERT(index < IMPLEMENTATION_MAX_DRAW_BUFFERS); return static_cast((values >> (index * kBits)) & kMaxValueMask); } // Replicate `Bits`-bit value 8 times and mask the result. static constexpr Type GetReplicatedValue(const ElementType value, const Type mask) { ASSERT(static_cast(value) <= kMaxValueMask); return (static_cast(value) * kReplicator) & mask; } // Replace `Bits`-bit value of index `index` in `target` with `value`. static constexpr void SetValueIndexed(const size_t index, const ElementType value, Type *target) { ASSERT(static_cast(value) <= kMaxValueMask); ASSERT(index < IMPLEMENTATION_MAX_DRAW_BUFFERS); // Bitmask with set bits that contain the value of index `index`. const Type selector = kMaxValueMask << (index * kBits); // Shift the new `value` to its position in the packed value. const Type builtValue = static_cast(value) << (index * kBits); // Mark differing bits of `target` and `builtValue`, then flip the bits on those // positions in `target`. // Taken from https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge *target = *target ^ ((*target ^ builtValue) & selector); } // Compare two packed sets of eight 4-bit values and return an 8-bit diff mask. static constexpr DrawBufferMask GetDiffMask(const uint32_t packedValue1, const uint32_t packedValue2) { uint32_t diff = packedValue1 ^ packedValue2; // For each 4-bit value that is different between inputs, set the msb to 1 and other // bits to 0. diff = (diff | ((diff & 0x77777777) + 0x77777777)) & 0x88888888; // By this point, `diff` looks like a...b...c...d...e...f...g...h... (dots mean zeros). // To get DrawBufferMask, we need to compress this 32-bit value to 8 bits, i.e. abcdefgh // Multiplying the lower half of `diff` by 0x249 (0x200 + 0x40 + 0x8 + 0x1) produces: // ................e...f...g...h... + // .............e...f...g...h...... + // ..........e...f...g...h......... + // .......e...f...g...h............ // ________________________________ = // .......e..ef.efgefghfgh.gh..h... // ^^^^ // Similar operation is applied to the upper word. // This calculation could be replaced with a single PEXT instruction from BMI2 set. diff = ((((diff & 0xFFFF0000) * 0x249) >> 24) & 0xF0) | (((diff * 0x249) >> 12) & 0xF); return DrawBufferMask(static_cast(diff)); } // Compare two packed sets of eight 8-bit values and return an 8-bit diff mask. static constexpr DrawBufferMask GetDiffMask(const uint64_t packedValue1, const uint64_t packedValue2) { uint64_t diff = packedValue1 ^ packedValue2; // For each 8-bit value that is different between inputs, set the msb to 1 and other // bits to 0. diff = (diff | ((diff & 0x7F7F7F7F7F7F7F7F) + 0x7F7F7F7F7F7F7F7F)) & 0x8080808080808080; // By this point, `diff` looks like (dots mean zeros): // a.......b.......c.......d.......e.......f.......g.......h....... // To get DrawBufferMask, we need to compress this 64-bit value to 8 bits, i.e. abcdefgh // Multiplying `diff` by 0x0002040810204081 produces: // a.......b.......c.......d.......e.......f.......g.......h....... + // .b.......c.......d.......e.......f.......g.......h.............. + // ..c.......d.......e.......f.......g.......h..................... + // ...d.......e.......f.......g.......h............................ + // ....e.......f.......g.......h................................... + // .....f.......g.......h.......................................... + // ......g.......h................................................. + // .......h........................................................ // ________________________________________________________________ = // abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... // ^^^^^^^^ // This operation could be replaced with a single PEXT instruction from BMI2 set. diff = 0x0002040810204081 * diff >> 56; return DrawBufferMask(static_cast(diff)); } }; using FactorStorage = StorageType()>; using EquationStorage = StorageType()>; using ColorMaskStorage = StorageType; static_assert(std::is_same::value && std::is_same::value, "Factor and Equation storage must be 64-bit."); BlendStateExt(const size_t drawBuffers = 1); BlendStateExt(const BlendStateExt &other); BlendStateExt &operator=(const BlendStateExt &other); ///////// Blending Toggle ///////// void setEnabled(const bool enabled); void setEnabledIndexed(const size_t index, const bool enabled); ///////// Color Write Mask ///////// constexpr static uint8_t kColorMaskRGBA = 0xf; static constexpr size_t PackColorMask(const bool red, const bool green, const bool blue, const bool alpha) { return (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0); } static constexpr void UnpackColorMask(const size_t value, bool *red, bool *green, bool *blue, bool *alpha) { *red = static_cast(value & 1); *green = static_cast(value & 2); *blue = static_cast(value & 4); *alpha = static_cast(value & 8); } ColorMaskStorage::Type expandColorMaskValue(const bool red, const bool green, const bool blue, const bool alpha) const; ColorMaskStorage::Type expandColorMaskIndexed(const size_t index) const; void setColorMask(const bool red, const bool green, const bool blue, const bool alpha); void setColorMaskIndexed(const size_t index, const uint8_t value); void setColorMaskIndexed(const size_t index, const bool red, const bool green, const bool blue, const bool alpha); uint8_t getColorMaskIndexed(const size_t index) const; void getColorMaskIndexed(const size_t index, bool *red, bool *green, bool *blue, bool *alpha) const; DrawBufferMask compareColorMask(ColorMaskStorage::Type other) const; ///////// Blend Equation ///////// EquationStorage::Type expandEquationValue(const GLenum mode) const; EquationStorage::Type expandEquationValue(const gl::BlendEquationType equation) const; EquationStorage::Type expandEquationColorIndexed(const size_t index) const; EquationStorage::Type expandEquationAlphaIndexed(const size_t index) const; void setEquations(const GLenum modeColor, const GLenum modeAlpha); void setEquationsIndexed(const size_t index, const GLenum modeColor, const GLenum modeAlpha); void setEquationsIndexed(const size_t index, const size_t otherIndex, const BlendStateExt &other); BlendEquationType getEquationColorIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return EquationStorage::GetValueIndexed(index, mEquationColor); } BlendEquationType getEquationAlphaIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return EquationStorage::GetValueIndexed(index, mEquationAlpha); } DrawBufferMask compareEquations(const EquationStorage::Type color, const EquationStorage::Type alpha) const; DrawBufferMask compareEquations(const BlendStateExt &other) const { return compareEquations(other.mEquationColor, other.mEquationAlpha); } ///////// Blend Factors ///////// FactorStorage::Type expandFactorValue(const GLenum func) const; FactorStorage::Type expandFactorValue(const gl::BlendFactorType func) const; FactorStorage::Type expandSrcColorIndexed(const size_t index) const; FactorStorage::Type expandDstColorIndexed(const size_t index) const; FactorStorage::Type expandSrcAlphaIndexed(const size_t index) const; FactorStorage::Type expandDstAlphaIndexed(const size_t index) const; void setFactors(const GLenum srcColor, const GLenum dstColor, const GLenum srcAlpha, const GLenum dstAlpha); void setFactorsIndexed(const size_t index, const gl::BlendFactorType srcColorFactor, const gl::BlendFactorType dstColorFactor, const gl::BlendFactorType srcAlphaFactor, const gl::BlendFactorType dstAlphaFactor); void setFactorsIndexed(const size_t index, const GLenum srcColor, const GLenum dstColor, const GLenum srcAlpha, const GLenum dstAlpha); void setFactorsIndexed(const size_t index, const size_t otherIndex, const BlendStateExt &other); BlendFactorType getSrcColorIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return FactorStorage::GetValueIndexed(index, mSrcColor); } BlendFactorType getDstColorIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return FactorStorage::GetValueIndexed(index, mDstColor); } BlendFactorType getSrcAlphaIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return FactorStorage::GetValueIndexed(index, mSrcAlpha); } BlendFactorType getDstAlphaIndexed(size_t index) const { ASSERT(index < mDrawBufferCount); return FactorStorage::GetValueIndexed(index, mDstAlpha); } DrawBufferMask compareFactors(const FactorStorage::Type srcColor, const FactorStorage::Type dstColor, const FactorStorage::Type srcAlpha, const FactorStorage::Type dstAlpha) const; DrawBufferMask compareFactors(const BlendStateExt &other) const { return compareFactors(other.mSrcColor, other.mDstColor, other.mSrcAlpha, other.mDstAlpha); } constexpr FactorStorage::Type getSrcColorBits() const { return mSrcColor; } constexpr FactorStorage::Type getSrcAlphaBits() const { return mSrcAlpha; } constexpr FactorStorage::Type getDstColorBits() const { return mDstColor; } constexpr FactorStorage::Type getDstAlphaBits() const { return mDstAlpha; } constexpr EquationStorage::Type getEquationColorBits() const { return mEquationColor; } constexpr EquationStorage::Type getEquationAlphaBits() const { return mEquationAlpha; } constexpr ColorMaskStorage::Type getAllColorMaskBits() const { return mAllColorMask; } constexpr ColorMaskStorage::Type getColorMaskBits() const { return mColorMask; } constexpr DrawBufferMask getAllEnabledMask() const { return mAllEnabledMask; } constexpr DrawBufferMask getEnabledMask() const { return mEnabledMask; } constexpr DrawBufferMask getUsesAdvancedBlendEquationMask() const { return mUsesAdvancedBlendEquationMask; } constexpr DrawBufferMask getUsesExtendedBlendFactorMask() const { return mUsesExtendedBlendFactorMask; } constexpr uint8_t getDrawBufferCount() const { return mDrawBufferCount; } constexpr void setSrcColorBits(const FactorStorage::Type srcColor) { mSrcColor = srcColor; } constexpr void setSrcAlphaBits(const FactorStorage::Type srcAlpha) { mSrcAlpha = srcAlpha; } constexpr void setDstColorBits(const FactorStorage::Type dstColor) { mDstColor = dstColor; } constexpr void setDstAlphaBits(const FactorStorage::Type dstAlpha) { mDstAlpha = dstAlpha; } constexpr void setEquationColorBits(const EquationStorage::Type equationColor) { mEquationColor = equationColor; } constexpr void setEquationAlphaBits(const EquationStorage::Type equationAlpha) { mEquationAlpha = equationAlpha; } constexpr void setColorMaskBits(const ColorMaskStorage::Type colorMask) { mColorMask = colorMask; } constexpr void setEnabledMask(const DrawBufferMask enabledMask) { mEnabledMask = enabledMask; } ///////// Data Members ///////// private: uint64_t mParameterMask; FactorStorage::Type mSrcColor; FactorStorage::Type mDstColor; FactorStorage::Type mSrcAlpha; FactorStorage::Type mDstAlpha; EquationStorage::Type mEquationColor; EquationStorage::Type mEquationAlpha; ColorMaskStorage::Type mAllColorMask; ColorMaskStorage::Type mColorMask; DrawBufferMask mAllEnabledMask; DrawBufferMask mEnabledMask; // Cache of whether the blend equation for each index is from KHR_blend_equation_advanced. DrawBufferMask mUsesAdvancedBlendEquationMask; // Cache of whether the blend factor for each index is from EXT_blend_func_extended. DrawBufferMask mUsesExtendedBlendFactorMask; uint8_t mDrawBufferCount; ANGLE_MAYBE_UNUSED_PRIVATE_FIELD uint8_t kUnused[3] = {}; }; static_assert(sizeof(BlendStateExt) == sizeof(uint64_t) + (sizeof(BlendStateExt::FactorStorage::Type) * 4 + sizeof(BlendStateExt::EquationStorage::Type) * 2 + sizeof(BlendStateExt::ColorMaskStorage::Type) * 2 + sizeof(DrawBufferMask) * 4 + sizeof(uint8_t)) + sizeof(uint8_t) * 3, "The BlendStateExt class must not contain gaps."); // Used in StateCache using StorageBuffersMask = angle::BitSet; template using SampleMaskArray = std::array; template using TexLevelArray = std::array; using TexLevelMask = angle::BitSet; enum class ComponentType { Float = 0, Int = 1, UnsignedInt = 2, NoType = 3, EnumCount = 4, InvalidEnum = 4, }; constexpr ComponentType GLenumToComponentType(GLenum componentType) { switch (componentType) { case GL_FLOAT: return ComponentType::Float; case GL_INT: return ComponentType::Int; case GL_UNSIGNED_INT: return ComponentType::UnsignedInt; case GL_NONE: return ComponentType::NoType; default: return ComponentType::InvalidEnum; } } constexpr angle::PackedEnumMap kComponentMasks = {{ {ComponentType::Float, 0x10001}, {ComponentType::Int, 0x00001}, {ComponentType::UnsignedInt, 0x10000}, }}; constexpr size_t kMaxComponentTypeMaskIndex = 16; using ComponentTypeMask = angle::BitSet; ANGLE_INLINE void SetComponentTypeMask(ComponentType type, size_t index, ComponentTypeMask *mask) { ASSERT(index <= kMaxComponentTypeMaskIndex); *mask &= ~(0x10001 << index); *mask |= kComponentMasks[type] << index; } ANGLE_INLINE ComponentType GetComponentTypeMask(ComponentTypeMask mask, size_t index) { ASSERT(index <= kMaxComponentTypeMaskIndex); uint32_t mask_bits = mask.bits() >> index & 0x10001; switch (mask_bits) { case 0x10001: return ComponentType::Float; case 0x00001: return ComponentType::Int; case 0x10000: return ComponentType::UnsignedInt; default: return ComponentType::InvalidEnum; } } ANGLE_INLINE ComponentTypeMask GetActiveComponentTypeMask(gl::AttributesMask activeAttribLocations) { const uint32_t activeAttribs = static_cast(activeAttribLocations.bits()); // Ever attrib index takes one bit from the lower 16-bits and another bit from the upper // 16-bits at the same index. return ComponentTypeMask(activeAttribs << kMaxComponentTypeMaskIndex | activeAttribs); } ANGLE_INLINE DrawBufferMask GetComponentTypeMaskDiff(ComponentTypeMask mask1, ComponentTypeMask mask2) { const uint32_t diff = static_cast((mask1 ^ mask2).bits()); return DrawBufferMask(static_cast(diff | (diff >> gl::kMaxComponentTypeMaskIndex))); } bool ValidateComponentTypeMasks(unsigned long outputTypes, unsigned long inputTypes, unsigned long outputMask, unsigned long inputMask); // Helpers for performing WebGL 2.0 clear validation // Extracted component type has always one of these four values: // * 0x10001 - float or normalized // * 0x00001 - int // * 0x10000 - unsigned int // * 0x00000 - unused or disabled // The following functions rely on these. static_assert(kComponentMasks[ComponentType::Float] == 0x10001); static_assert(kComponentMasks[ComponentType::Int] == 0x00001); static_assert(kComponentMasks[ComponentType::UnsignedInt] == 0x10000); // Used for clearBufferuiv ANGLE_INLINE bool IsComponentTypeFloatOrInt(ComponentTypeMask mask, size_t index) { ASSERT(index <= kMaxComponentTypeMaskIndex); // 0x10001 or 0x00001 return ((mask.bits() >> index) & 0x00001) != 0; } // Used for clearBufferiv ANGLE_INLINE bool IsComponentTypeFloatOrUnsignedInt(ComponentTypeMask mask, size_t index) { ASSERT(index <= kMaxComponentTypeMaskIndex); // 0x10001 or 0x10000 return ((mask.bits() >> index) & 0x10000) != 0; } // Used for clearBufferfv ANGLE_INLINE bool IsComponentTypeIntOrUnsignedInt(ComponentTypeMask mask, size_t index) { ASSERT(index <= kMaxComponentTypeMaskIndex); // 0x00001 or 0x10000; this expression is more efficient than two explicit comparisons return ((((mask.bits() >> kMaxComponentTypeMaskIndex) ^ mask.bits()) >> index) & 1) != 0; } // Used for clear ANGLE_INLINE DrawBufferMask GetIntOrUnsignedIntDrawBufferMask(ComponentTypeMask mask) { static_assert(DrawBufferMask::size() <= 8); return DrawBufferMask( static_cast((mask.bits() >> kMaxComponentTypeMaskIndex) ^ mask.bits())); } // GL_ANGLE_blob_cache state struct BlobCacheCallbacks { GLSETBLOBPROCANGLE setFunction = nullptr; GLGETBLOBPROCANGLE getFunction = nullptr; const void *userParam = nullptr; }; enum class RenderToTextureImageIndex { // The default image of the texture, where data is expected to be. Default = 0, // Intermediate multisampled images for EXT_multisampled_render_to_texture. // These values must match log2(SampleCount). IntermediateImage2xMultisampled = 1, IntermediateImage4xMultisampled = 2, IntermediateImage8xMultisampled = 3, IntermediateImage16xMultisampled = 4, // We currently only support up to 16xMSAA in backends that use this enum. InvalidEnum = 5, EnumCount = 5, }; template using RenderToTextureImageMap = angle::PackedEnumMap; constexpr size_t kCubeFaceCount = 6; template using CubeFaceArray = std::array; template using TextureTypeMap = angle::PackedEnumMap; using TextureMap = TextureTypeMap>; // ShaderVector can contain one item per shader. It differs from ShaderMap in that the values are // not indexed by ShaderType. template using ShaderVector = angle::FixedVector(ShaderType::EnumCount)>; template using AttachmentArray = std::array; template using AttachmentVector = angle::FixedVector; using AttachmentsMask = angle::BitSet; template using DrawBuffersArray = std::array; template using DrawBuffersVector = angle::FixedVector; template using AttribArray = std::array; template using AttribVector = angle::FixedVector; using ActiveTextureMask = angle::BitSet; template using ActiveTextureArray = std::array; using ActiveTextureTypeArray = ActiveTextureArray; using ImageUnitMask = angle::BitSet; using SupportedSampleSet = std::set; template using TransformFeedbackBuffersArray = std::array; using ClipDistanceEnableBits = angle::BitSet32; template using QueryTypeMap = angle::PackedEnumMap; constexpr size_t kBarrierVectorDefaultSize = 16; template using BarrierVector = angle::FastVector; using BufferBarrierVector = BarrierVector; using SamplerBindingVector = std::vector>; using BufferVector = std::vector>; struct TextureAndLayout { Texture *texture; GLenum layout; }; using TextureBarrierVector = BarrierVector; // OffsetBindingPointer.getSize() returns the size specified by the user, which may be larger than // the size of the bound buffer. This function reduces the returned size to fit the bound buffer if // necessary. Returns 0 if no buffer is bound or if integer overflow occurs. GLsizeiptr GetBoundBufferAvailableSize(const OffsetBindingPointer &binding); // A texture level index. template class LevelIndexWrapper { public: LevelIndexWrapper() = default; explicit constexpr LevelIndexWrapper(T levelIndex) : mLevelIndex(levelIndex) {} constexpr LevelIndexWrapper(const LevelIndexWrapper &other) = default; constexpr LevelIndexWrapper &operator=(const LevelIndexWrapper &other) = default; constexpr T get() const { return mLevelIndex; } LevelIndexWrapper &operator++() { ++mLevelIndex; return *this; } constexpr bool operator<(const LevelIndexWrapper &other) const { return mLevelIndex < other.mLevelIndex; } constexpr bool operator<=(const LevelIndexWrapper &other) const { return mLevelIndex <= other.mLevelIndex; } constexpr bool operator>(const LevelIndexWrapper &other) const { return mLevelIndex > other.mLevelIndex; } constexpr bool operator>=(const LevelIndexWrapper &other) const { return mLevelIndex >= other.mLevelIndex; } constexpr bool operator==(const LevelIndexWrapper &other) const { return mLevelIndex == other.mLevelIndex; } constexpr bool operator!=(const LevelIndexWrapper &other) const { return mLevelIndex != other.mLevelIndex; } constexpr LevelIndexWrapper operator+(T other) const { return LevelIndexWrapper(mLevelIndex + other); } constexpr LevelIndexWrapper operator-(T other) const { return LevelIndexWrapper(mLevelIndex - other); } constexpr T operator-(LevelIndexWrapper other) const { return mLevelIndex - other.mLevelIndex; } private: T mLevelIndex; }; // A GL texture level index. using LevelIndex = LevelIndexWrapper; enum class MultisamplingMode { // Regular multisampling Regular = 0, // GL_EXT_multisampled_render_to_texture renderbuffer/texture attachments which perform implicit // resolve of multisampled data. MultisampledRenderToTexture, }; } // namespace gl namespace rx { // A macro that determines whether an object has a given runtime type. #if defined(__clang__) # if __has_feature(cxx_rtti) # define ANGLE_HAS_DYNAMIC_CAST 1 # endif #elif !defined(NDEBUG) && (!defined(_MSC_VER) || defined(_CPPRTTI)) && \ (!defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || \ defined(__GXX_RTTI)) # define ANGLE_HAS_DYNAMIC_CAST 1 #endif #ifdef ANGLE_HAS_DYNAMIC_CAST # define ANGLE_HAS_DYNAMIC_TYPE(type, obj) (dynamic_cast(obj) != nullptr) # undef ANGLE_HAS_DYNAMIC_CAST #else # define ANGLE_HAS_DYNAMIC_TYPE(type, obj) (obj != nullptr) #endif // Downcast a base implementation object (EG TextureImpl to TextureD3D) template inline DestT *GetAs(SrcT *src) { ASSERT(ANGLE_HAS_DYNAMIC_TYPE(DestT *, src)); return static_cast(src); } template inline const DestT *GetAs(const SrcT *src) { ASSERT(ANGLE_HAS_DYNAMIC_TYPE(const DestT *, src)); return static_cast(src); } #undef ANGLE_HAS_DYNAMIC_TYPE // Downcast a GL object to an Impl (EG gl::Texture to rx::TextureD3D) template inline DestT *GetImplAs(SrcT *src) { return GetAs(src->getImplementation()); } template inline DestT *SafeGetImplAs(SrcT *src) { return src != nullptr ? GetAs(src->getImplementation()) : nullptr; } } // namespace rx #include "angletypes.inc" namespace angle { enum class NativeWindowSystem { X11, Wayland, Gbm, NullCompute, Other, }; struct FeatureOverrides { std::vector enabled; std::vector disabled; bool allDisabled = false; }; // 160-bit SHA-1 hash key used for hasing a program. BlobCache opts in using fixed keys for // simplicity and efficiency. static constexpr size_t kBlobCacheKeyLength = angle::base::kSHA1Length; using BlobCacheKey = std::array; class BlobCacheValue // To be replaced with std::span when C++20 is required { public: BlobCacheValue() : mPtr(nullptr), mSize(0) {} BlobCacheValue(const uint8_t *ptr, size_t size) : mPtr(ptr), mSize(size) {} // A very basic struct to hold the pointer and size together. The objects of this class // don't own the memory. const uint8_t *data() { return mPtr; } size_t size() { return mSize; } const uint8_t &operator[](size_t pos) const { ASSERT(pos < mSize); return mPtr[pos]; } private: const uint8_t *mPtr; size_t mSize; }; bool CompressBlob(const size_t cacheSize, const uint8_t *cacheData, MemoryBuffer *compressedData); bool DecompressBlob(const uint8_t *compressedData, const size_t compressedSize, size_t maxUncompressedDataSize, MemoryBuffer *uncompressedData); uint32_t GenerateCRC32(const uint8_t *data, size_t size); uint32_t InitCRC32(); uint32_t UpdateCRC32(uint32_t prevCrc32, const uint8_t *data, size_t size); } // namespace angle namespace std { template <> struct hash { // Simple routine to hash four ints. size_t operator()(const angle::BlobCacheKey &key) const { return angle::ComputeGenericHash(key.data(), key.size()); } }; } // namespace std namespace angle { // Under certain circumstances, such as for increased parallelism, the backend may defer an // operation to be done at the end of a call after the locks have been unlocked. The entry point // function passes an |UnlockedTailCall| through the frontend to the backend. If it is set, the // entry point would execute it at the end of the call. // // Since the function is called without any locks, care must be taken to minimize the amount of work // in such calls and ensure thread safety (for example by using fine grained locks inside the call // itself). // // Some entry points pass a void pointer argument to UnlockedTailCall::run method intended to // contain the return value filled by the backend, the rest of the entry points pass in a // nullptr. Regardless, Display::terminate runs pending tail calls passing in a nullptr, so // the tail calls that return a value in the argument still have to guard against a nullptr // parameter. class UnlockedTailCall final : angle::NonCopyable { public: using CallType = std::function; UnlockedTailCall(); ~UnlockedTailCall(); void add(CallType &&call); ANGLE_INLINE void run(void *resultOut) { if (!mCalls.empty()) { runImpl(resultOut); } } bool any() const { return !mCalls.empty(); } private: void runImpl(void *resultOut); // Typically, there is only one tail call. It is possible to end up with 2 tail calls currently // with unMakeCurrent destroying both the read and draw surfaces, each adding a tail call in the // Vulkan backend. // // Some apps will create multiple windows surfaces and not call corresponding destroy api, which // cause many tail calls been added, so remove the max call count limitations. std::vector mCalls; }; enum class JobThreadSafety { Safe, Unsafe, }; enum class JobResultExpectancy { // Whether the compile or link job's results are immediately needed. This is the case for GLES1 // programs for example, or shader compilation in glCreateShaderProgramv. Immediate, // Whether the compile or link job's results are needed after the end of the current entry point // call. In this case, the job may be done in an unlocked tail call. Future, }; // Zero-based for better array indexing enum FramebufferBinding { FramebufferBindingRead = 0, FramebufferBindingDraw, FramebufferBindingSingletonMax, FramebufferBindingBoth = FramebufferBindingSingletonMax, FramebufferBindingMax, FramebufferBindingUnknown = FramebufferBindingMax, }; inline FramebufferBinding EnumToFramebufferBinding(GLenum enumValue) { switch (enumValue) { case GL_READ_FRAMEBUFFER: return FramebufferBindingRead; case GL_DRAW_FRAMEBUFFER: return FramebufferBindingDraw; case GL_FRAMEBUFFER: return FramebufferBindingBoth; default: UNREACHABLE(); return FramebufferBindingUnknown; } } inline GLenum FramebufferBindingToEnum(FramebufferBinding binding) { switch (binding) { case FramebufferBindingRead: return GL_READ_FRAMEBUFFER; case FramebufferBindingDraw: return GL_DRAW_FRAMEBUFFER; case FramebufferBindingBoth: return GL_FRAMEBUFFER; default: UNREACHABLE(); return GL_NONE; } } template class DestroyThenDelete { public: DestroyThenDelete() = default; DestroyThenDelete(const ContextT *context) : mContext(context) {} void operator()(ObjT *obj) { (void)(obj->onDestroy(mContext)); delete obj; } private: const ContextT *mContext = nullptr; }; template using UniqueObjectPointer = std::unique_ptr>; } // namespace angle namespace gl { class State; // Focal Point information for foveated rendering struct FocalPoint { float focalX; float focalY; float gainX; float gainY; float foveaArea; constexpr FocalPoint() : focalX(0), focalY(0), gainX(0), gainY(0), foveaArea(0) {} FocalPoint(float fX, float fY, float gX, float gY, float fArea) : focalX(fX), focalY(fY), gainX(gX), gainY(gY), foveaArea(fArea) {} FocalPoint(const FocalPoint &other) = default; FocalPoint &operator=(const FocalPoint &other) = default; bool operator==(const FocalPoint &other) const { return focalX == other.focalX && focalY == other.focalY && gainX == other.gainX && gainY == other.gainY && foveaArea == other.foveaArea; } bool operator!=(const FocalPoint &other) const { return !(*this == other); } bool valid() const { return gainX > 0 && gainY > 0; } }; constexpr FocalPoint kDefaultFocalPoint = FocalPoint(); class FoveationState { public: FoveationState() { mConfigured = false; mFoveatedFeatureBits = 0; mMinPixelDensity = 0.0f; mFocalPoints.fill(kDefaultFocalPoint); } FoveationState &operator=(const FoveationState &other) = default; void configure() { mConfigured = true; } bool isConfigured() const { return mConfigured; } bool isFoveated() const { // Consider foveated if at least 1 focal point is valid return std::any_of(mFocalPoints.begin(), mFocalPoints.end(), [](const FocalPoint &focalPoint) { return focalPoint.valid(); }); } bool operator==(const FoveationState &other) const { return mConfigured == other.mConfigured && mFoveatedFeatureBits == other.mFoveatedFeatureBits && mMinPixelDensity == other.mMinPixelDensity && mFocalPoints == other.mFocalPoints; } bool operator!=(const FoveationState &other) const { return !(*this == other); } void setFoveatedFeatureBits(const GLuint features) { mFoveatedFeatureBits = features; } GLuint getFoveatedFeatureBits() const { return mFoveatedFeatureBits; } void setMinPixelDensity(const GLfloat density) { mMinPixelDensity = density; } GLfloat getMinPixelDensity() const { return mMinPixelDensity; } GLuint getMaxNumFocalPoints() const { return gl::IMPLEMENTATION_MAX_FOCAL_POINTS; } void setFocalPoint(uint32_t layer, uint32_t focalPointIndex, const FocalPoint &focalPoint) { mFocalPoints[getIndex(layer, focalPointIndex)] = focalPoint; } const FocalPoint &getFocalPoint(uint32_t layer, uint32_t focalPointIndex) const { return mFocalPoints[getIndex(layer, focalPointIndex)]; } GLuint getSupportedFoveationFeatures() const { return GL_FOVEATION_ENABLE_BIT_QCOM; } private: size_t getIndex(uint32_t layer, uint32_t focalPointIndex) const { ASSERT(layer < IMPLEMENTATION_MAX_NUM_LAYERS && focalPointIndex < IMPLEMENTATION_MAX_FOCAL_POINTS); return (layer * IMPLEMENTATION_MAX_FOCAL_POINTS) + focalPointIndex; } bool mConfigured; GLuint mFoveatedFeatureBits; GLfloat mMinPixelDensity; static constexpr size_t kMaxFocalPoints = IMPLEMENTATION_MAX_NUM_LAYERS * IMPLEMENTATION_MAX_FOCAL_POINTS; std::array mFocalPoints; }; enum class BufferStorage : bool { // The buffer storage is mutable Mutable, // The buffer storage is immutable Immutable, }; } // namespace gl #endif // LIBANGLE_ANGLETYPES_H_