// // Copyright 2016 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. // // renderer_utils: // Helper methods pertaining to most or all back-ends. // #ifndef LIBANGLE_RENDERER_RENDERER_UTILS_H_ #define LIBANGLE_RENDERER_RENDERER_UTILS_H_ #include #include #include #include "GLSLANG/ShaderLang.h" #include "common/angleutils.h" #include "common/utilities.h" #include "libANGLE/angletypes.h" namespace angle { struct FeatureSetBase; struct Format; enum class FormatID; } // namespace angle namespace gl { struct FormatType; struct InternalFormat; class State; } // namespace gl namespace egl { class AttributeMap; struct DisplayState; } // namespace egl namespace rx { class ContextImpl; // The possible rotations of the surface/draw framebuffer, particularly for the Vulkan back-end on // Android. enum class SurfaceRotation { Identity, Rotated90Degrees, Rotated180Degrees, Rotated270Degrees, FlippedIdentity, FlippedRotated90Degrees, FlippedRotated180Degrees, FlippedRotated270Degrees, InvalidEnum, EnumCount = InvalidEnum, }; bool IsRotatedAspectRatio(SurfaceRotation rotation); using SpecConstUsageBits = angle::PackedEnumBitSet; void RotateRectangle(const SurfaceRotation rotation, const bool flipY, const int framebufferWidth, const int framebufferHeight, const gl::Rectangle &incoming, gl::Rectangle *outgoing); using MipGenerationFunction = void (*)(size_t sourceWidth, size_t sourceHeight, size_t sourceDepth, const uint8_t *sourceData, size_t sourceRowPitch, size_t sourceDepthPitch, uint8_t *destData, size_t destRowPitch, size_t destDepthPitch); typedef void (*PixelReadFunction)(const uint8_t *source, uint8_t *dest); typedef void (*PixelWriteFunction)(const uint8_t *source, uint8_t *dest); typedef void (*FastCopyFunction)(const uint8_t *source, int srcXAxisPitch, int srcYAxisPitch, uint8_t *dest, int destXAxisPitch, int destYAxisPitch, int width, int height); class FastCopyFunctionMap { public: struct Entry { angle::FormatID formatID; FastCopyFunction func; }; constexpr FastCopyFunctionMap() : FastCopyFunctionMap(nullptr, 0) {} constexpr FastCopyFunctionMap(const Entry *data, size_t size) : mSize(size), mData(data) {} bool has(angle::FormatID formatID) const; FastCopyFunction get(angle::FormatID formatID) const; private: size_t mSize; const Entry *mData; }; struct PackPixelsParams { PackPixelsParams(); PackPixelsParams(const gl::Rectangle &area, const angle::Format &destFormat, GLuint outputPitch, bool reverseRowOrderIn, gl::Buffer *packBufferIn, ptrdiff_t offset); gl::Rectangle area; const angle::Format *destFormat; GLuint outputPitch; gl::Buffer *packBuffer; bool reverseRowOrder; ptrdiff_t offset; SurfaceRotation rotation; }; void PackPixels(const PackPixelsParams ¶ms, const angle::Format &sourceFormat, int inputPitch, const uint8_t *source, uint8_t *destination); using InitializeTextureDataFunction = void (*)(size_t width, size_t height, size_t depth, uint8_t *output, size_t outputRowPitch, size_t outputDepthPitch); using LoadImageFunction = void (*)(size_t width, size_t height, size_t depth, const uint8_t *input, size_t inputRowPitch, size_t inputDepthPitch, uint8_t *output, size_t outputRowPitch, size_t outputDepthPitch); struct LoadImageFunctionInfo { LoadImageFunctionInfo() : loadFunction(nullptr), requiresConversion(false) {} LoadImageFunctionInfo(LoadImageFunction loadFunction, bool requiresConversion) : loadFunction(loadFunction), requiresConversion(requiresConversion) {} LoadImageFunction loadFunction; bool requiresConversion; }; using LoadFunctionMap = LoadImageFunctionInfo (*)(GLenum); bool ShouldUseDebugLayers(const egl::AttributeMap &attribs); void CopyImageCHROMIUM(const uint8_t *sourceData, size_t sourceRowPitch, size_t sourcePixelBytes, size_t sourceDepthPitch, PixelReadFunction pixelReadFunction, uint8_t *destData, size_t destRowPitch, size_t destPixelBytes, size_t destDepthPitch, PixelWriteFunction pixelWriteFunction, GLenum destUnsizedFormat, GLenum destComponentType, size_t width, size_t height, size_t depth, bool unpackFlipY, bool unpackPremultiplyAlpha, bool unpackUnmultiplyAlpha); // Incomplete textures are 1x1 textures filled with black, used when samplers are incomplete. // This helper class encapsulates handling incomplete textures. Because the GL back-end // can take advantage of the driver's incomplete textures, and because clearing multisample // textures is so difficult, we can keep an instance of this class in the back-end instead // of moving the logic to the Context front-end. // This interface allows us to call-back to init a multisample texture. class MultisampleTextureInitializer { public: virtual ~MultisampleTextureInitializer() {} virtual angle::Result initializeMultisampleTextureToBlack(const gl::Context *context, gl::Texture *glTexture) = 0; }; class IncompleteTextureSet final : angle::NonCopyable { public: IncompleteTextureSet(); ~IncompleteTextureSet(); void onDestroy(const gl::Context *context); angle::Result getIncompleteTexture(const gl::Context *context, gl::TextureType type, gl::SamplerFormat format, MultisampleTextureInitializer *multisampleInitializer, gl::Texture **textureOut); private: using TextureMapWithSamplerFormat = angle::PackedEnumMap; TextureMapWithSamplerFormat mIncompleteTextures; gl::Buffer *mIncompleteTextureBufferAttachment; }; // Helpers to set a matrix uniform value based on GLSL or HLSL semantics. // The return value indicate if the data was updated or not. template struct SetFloatUniformMatrixGLSL { static void Run(unsigned int arrayElementOffset, unsigned int elementCount, GLsizei countIn, GLboolean transpose, const GLfloat *value, uint8_t *targetData); }; template struct SetFloatUniformMatrixHLSL { static void Run(unsigned int arrayElementOffset, unsigned int elementCount, GLsizei countIn, GLboolean transpose, const GLfloat *value, uint8_t *targetData); }; // Helper method to de-tranpose a matrix uniform for an API query. void GetMatrixUniform(GLenum type, GLfloat *dataOut, const GLfloat *source, bool transpose); template void GetMatrixUniform(GLenum type, NonFloatT *dataOut, const NonFloatT *source, bool transpose); const angle::Format &GetFormatFromFormatType(GLenum format, GLenum type); angle::Result ComputeStartVertex(ContextImpl *contextImpl, const gl::IndexRange &indexRange, GLint baseVertex, GLint *firstVertexOut); angle::Result GetVertexRangeInfo(const gl::Context *context, GLint firstVertex, GLsizei vertexOrIndexCount, gl::DrawElementsType indexTypeOrInvalid, const void *indices, GLint baseVertex, GLint *startVertexOut, size_t *vertexCountOut); gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY); // Helper method to intialize a FeatureSet with overrides from the DisplayState void ApplyFeatureOverrides(angle::FeatureSetBase *features, const egl::DisplayState &state); template uint32_t LineLoopRestartIndexCountHelper(GLsizei indexCount, const uint8_t *srcPtr) { constexpr In restartIndex = gl::GetPrimitiveRestartIndexFromType(); const In *inIndices = reinterpret_cast(srcPtr); uint32_t numIndices = 0; // See CopyLineLoopIndicesWithRestart() below for more info on how // numIndices is calculated. GLsizei loopStartIndex = 0; for (GLsizei curIndex = 0; curIndex < indexCount; curIndex++) { In vertex = inIndices[curIndex]; if (vertex != restartIndex) { numIndices++; } else { if (curIndex > loopStartIndex) { numIndices += 2; } loopStartIndex = curIndex + 1; } } if (indexCount > loopStartIndex) { numIndices++; } return numIndices; } inline uint32_t GetLineLoopWithRestartIndexCount(gl::DrawElementsType glIndexType, GLsizei indexCount, const uint8_t *srcPtr) { switch (glIndexType) { case gl::DrawElementsType::UnsignedByte: return LineLoopRestartIndexCountHelper(indexCount, srcPtr); case gl::DrawElementsType::UnsignedShort: return LineLoopRestartIndexCountHelper(indexCount, srcPtr); case gl::DrawElementsType::UnsignedInt: return LineLoopRestartIndexCountHelper(indexCount, srcPtr); default: UNREACHABLE(); return 0; } } // Writes the line-strip vertices for a line loop to outPtr, // where outLimit is calculated as in GetPrimitiveRestartIndexCount. template void CopyLineLoopIndicesWithRestart(GLsizei indexCount, const uint8_t *srcPtr, uint8_t *outPtr) { constexpr In restartIndex = gl::GetPrimitiveRestartIndexFromType(); constexpr Out outRestartIndex = gl::GetPrimitiveRestartIndexFromType(); const In *inIndices = reinterpret_cast(srcPtr); Out *outIndices = reinterpret_cast(outPtr); GLsizei loopStartIndex = 0; for (GLsizei curIndex = 0; curIndex < indexCount; curIndex++) { In vertex = inIndices[curIndex]; if (vertex != restartIndex) { *(outIndices++) = static_cast(vertex); } else { if (curIndex > loopStartIndex) { // Emit an extra vertex only if the loop is not empty. *(outIndices++) = inIndices[loopStartIndex]; // Then restart the strip. *(outIndices++) = outRestartIndex; } loopStartIndex = curIndex + 1; } } if (indexCount > loopStartIndex) { // Close the last loop if not empty. *(outIndices++) = inIndices[loopStartIndex]; } } void GetSamplePosition(GLsizei sampleCount, size_t index, GLfloat *xy); angle::Result MultiDrawArraysGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLint *firsts, const GLsizei *counts, GLsizei drawcount); angle::Result MultiDrawArraysIndirectGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const void *indirect, GLsizei drawcount, GLsizei stride); angle::Result MultiDrawArraysInstancedGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLint *firsts, const GLsizei *counts, const GLsizei *instanceCounts, GLsizei drawcount); angle::Result MultiDrawElementsGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLsizei *counts, gl::DrawElementsType type, const GLvoid *const *indices, GLsizei drawcount); angle::Result MultiDrawElementsIndirectGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, gl::DrawElementsType type, const void *indirect, GLsizei drawcount, GLsizei stride); angle::Result MultiDrawElementsInstancedGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLsizei *counts, gl::DrawElementsType type, const GLvoid *const *indices, const GLsizei *instanceCounts, GLsizei drawcount); angle::Result MultiDrawArraysInstancedBaseInstanceGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLint *firsts, const GLsizei *counts, const GLsizei *instanceCounts, const GLuint *baseInstances, GLsizei drawcount); angle::Result MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral(ContextImpl *contextImpl, const gl::Context *context, gl::PrimitiveMode mode, const GLsizei *counts, gl::DrawElementsType type, const GLvoid *const *indices, const GLsizei *instanceCounts, const GLint *baseVertices, const GLuint *baseInstances, GLsizei drawcount); // RAII object making sure reset uniforms is called no matter whether there's an error in draw calls class ResetBaseVertexBaseInstance : angle::NonCopyable { public: ResetBaseVertexBaseInstance(gl::Program *programObject, bool resetBaseVertex, bool resetBaseInstance); ~ResetBaseVertexBaseInstance(); private: gl::Program *mProgramObject; bool mResetBaseVertex; bool mResetBaseInstance; }; angle::FormatID ConvertToSRGB(angle::FormatID formatID); angle::FormatID ConvertToLinear(angle::FormatID formatID); bool IsOverridableLinearFormat(angle::FormatID formatID); enum class PipelineType { Graphics = 0, Compute = 1, InvalidEnum = 2, EnumCount = 2, }; } // namespace rx // MultiDraw macro patterns // These macros are to avoid too much code duplication as we don't want to have if detect for // hasDrawID/BaseVertex/BaseInstance inside for loop in a multiDrawANGLE call Part of these are put // in the header as we want to share with specialized context impl on some platforms for multidraw #define ANGLE_SET_DRAW_ID_UNIFORM_0(drawID) \ {} #define ANGLE_SET_DRAW_ID_UNIFORM_1(drawID) programObject->setDrawIDUniform(drawID) #define ANGLE_SET_DRAW_ID_UNIFORM(cond) ANGLE_SET_DRAW_ID_UNIFORM_##cond #define ANGLE_SET_BASE_VERTEX_UNIFORM_0(baseVertex) \ {} #define ANGLE_SET_BASE_VERTEX_UNIFORM_1(baseVertex) programObject->setBaseVertexUniform(baseVertex); #define ANGLE_SET_BASE_VERTEX_UNIFORM(cond) ANGLE_SET_BASE_VERTEX_UNIFORM_##cond #define ANGLE_SET_BASE_INSTANCE_UNIFORM_0(baseInstance) \ {} #define ANGLE_SET_BASE_INSTANCE_UNIFORM_1(baseInstance) \ programObject->setBaseInstanceUniform(baseInstance) #define ANGLE_SET_BASE_INSTANCE_UNIFORM(cond) ANGLE_SET_BASE_INSTANCE_UNIFORM_##cond #define ANGLE_NOOP_DRAW_ context->noopDraw(mode, counts[drawID]) #define ANGLE_NOOP_DRAW_INSTANCED \ context->noopDrawInstanced(mode, counts[drawID], instanceCounts[drawID]) #define ANGLE_NOOP_DRAW(_instanced) ANGLE_NOOP_DRAW##_instanced #define ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE_ \ gl::MarkTransformFeedbackBufferUsage(context, counts[drawID], 1) #define ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE_INSTANCED \ gl::MarkTransformFeedbackBufferUsage(context, counts[drawID], instanceCounts[drawID]) #define ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced) \ ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE##instanced // Helper macro that casts to a bitfield type then verifies no bits were dropped. #define SetBitField(lhs, rhs) \ do \ { \ auto ANGLE_LOCAL_VAR = rhs; \ lhs = static_cast::type>(ANGLE_LOCAL_VAR); \ ASSERT(static_cast(lhs) == ANGLE_LOCAL_VAR); \ } while (0) #endif // LIBANGLE_RENDERER_RENDERER_UTILS_H_