diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp | 1583 |
1 files changed, 1583 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp b/gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp new file mode 100644 index 0000000000..152de2f25d --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp @@ -0,0 +1,1583 @@ +// +// 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. +// + +#include "libANGLE/renderer/renderer_utils.h" + +#include "common/string_utils.h" +#include "common/system_utils.h" +#include "common/utilities.h" +#include "image_util/copyimage.h" +#include "image_util/imageformats.h" +#include "libANGLE/AttributeMap.h" +#include "libANGLE/Context.h" +#include "libANGLE/Context.inl.h" +#include "libANGLE/Display.h" +#include "libANGLE/formatutils.h" +#include "libANGLE/renderer/ContextImpl.h" +#include "libANGLE/renderer/Format.h" +#include "platform/Feature.h" + +#include <string.h> +#include <cctype> + +namespace angle +{ +namespace +{ +// For the sake of feature name matching, underscore is ignored, and the names are matched +// case-insensitive. This allows feature names to be overriden both in snake_case (previously used +// by ANGLE) and camelCase. The second string (user-provided name) can end in `*` for wildcard +// matching. +bool FeatureNameMatch(const std::string &a, const std::string &b) +{ + size_t ai = 0; + size_t bi = 0; + + while (ai < a.size() && bi < b.size()) + { + if (a[ai] == '_') + { + ++ai; + } + if (b[bi] == '_') + { + ++bi; + } + if (b[bi] == '*' && bi + 1 == b.size()) + { + // If selected feature name ends in wildcard, match it. + return true; + } + if (std::tolower(a[ai++]) != std::tolower(b[bi++])) + { + return false; + } + } + + return ai == a.size() && bi == b.size(); +} +} // anonymous namespace + +// FeatureSetBase implementation +void FeatureSetBase::overrideFeatures(const std::vector<std::string> &featureNames, bool enabled) +{ + for (const std::string &name : featureNames) + { + const bool hasWildcard = name.back() == '*'; + for (auto iter : members) + { + const std::string &featureName = iter.first; + FeatureInfo *feature = iter.second; + + if (!FeatureNameMatch(featureName, name)) + { + continue; + } + + feature->enabled = enabled; + + // If name has a wildcard, try to match it with all features. Otherwise, bail on first + // match, as names are unique. + if (!hasWildcard) + { + break; + } + } + } +} + +void FeatureSetBase::populateFeatureList(FeatureList *features) const +{ + for (FeatureMap::const_iterator it = members.begin(); it != members.end(); it++) + { + features->push_back(it->second); + } +} +} // namespace angle + +namespace rx +{ + +namespace +{ +// Both D3D and Vulkan support the same set of standard sample positions for 1, 2, 4, 8, and 16 +// samples. See: +// +// - https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218.aspx +// +// - +// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#primsrast-multisampling +using SamplePositionsArray = std::array<float, 32>; +constexpr std::array<SamplePositionsArray, 5> kSamplePositions = { + {{{0.5f, 0.5f}}, + {{0.75f, 0.75f, 0.25f, 0.25f}}, + {{0.375f, 0.125f, 0.875f, 0.375f, 0.125f, 0.625f, 0.625f, 0.875f}}, + {{0.5625f, 0.3125f, 0.4375f, 0.6875f, 0.8125f, 0.5625f, 0.3125f, 0.1875f, 0.1875f, 0.8125f, + 0.0625f, 0.4375f, 0.6875f, 0.9375f, 0.9375f, 0.0625f}}, + {{0.5625f, 0.5625f, 0.4375f, 0.3125f, 0.3125f, 0.625f, 0.75f, 0.4375f, + 0.1875f, 0.375f, 0.625f, 0.8125f, 0.8125f, 0.6875f, 0.6875f, 0.1875f, + 0.375f, 0.875f, 0.5f, 0.0625f, 0.25f, 0.125f, 0.125f, 0.75f, + 0.0f, 0.5f, 0.9375f, 0.25f, 0.875f, 0.9375f, 0.0625f, 0.0f}}}}; + +struct IncompleteTextureParameters +{ + GLenum sizedInternalFormat; + GLenum format; + GLenum type; + GLubyte clearColor[4]; +}; + +// Note that for gl::SamplerFormat::Shadow, the clearColor datatype needs to be GLushort and as such +// we will reinterpret GLubyte[4] as GLushort[2]. +constexpr angle::PackedEnumMap<gl::SamplerFormat, IncompleteTextureParameters> + kIncompleteTextureParameters = { + {gl::SamplerFormat::Float, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}}, + {gl::SamplerFormat::Unsigned, + {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}}, + {gl::SamplerFormat::Signed, {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, {0, 0, 0, 127}}}, + {gl::SamplerFormat::Shadow, + {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, {0, 0, 0, 0}}}}; + +void CopyColor(gl::ColorF *color) +{ + // No-op +} + +void PremultiplyAlpha(gl::ColorF *color) +{ + color->red *= color->alpha; + color->green *= color->alpha; + color->blue *= color->alpha; +} + +void UnmultiplyAlpha(gl::ColorF *color) +{ + if (color->alpha != 0.0f) + { + float invAlpha = 1.0f / color->alpha; + color->red *= invAlpha; + color->green *= invAlpha; + color->blue *= invAlpha; + } +} + +void ClipChannelsR(gl::ColorF *color) +{ + color->green = 0.0f; + color->blue = 0.0f; + color->alpha = 1.0f; +} + +void ClipChannelsRG(gl::ColorF *color) +{ + color->blue = 0.0f; + color->alpha = 1.0f; +} + +void ClipChannelsRGB(gl::ColorF *color) +{ + color->alpha = 1.0f; +} + +void ClipChannelsLuminance(gl::ColorF *color) +{ + color->alpha = 1.0f; +} + +void ClipChannelsAlpha(gl::ColorF *color) +{ + color->red = 0.0f; + color->green = 0.0f; + color->blue = 0.0f; +} + +void ClipChannelsNoOp(gl::ColorF *color) {} + +void WriteUintColor(const gl::ColorF &color, + PixelWriteFunction colorWriteFunction, + uint8_t *destPixelData) +{ + gl::ColorUI destColor( + static_cast<unsigned int>(color.red * 255), static_cast<unsigned int>(color.green * 255), + static_cast<unsigned int>(color.blue * 255), static_cast<unsigned int>(color.alpha * 255)); + colorWriteFunction(reinterpret_cast<const uint8_t *>(&destColor), destPixelData); +} + +void WriteFloatColor(const gl::ColorF &color, + PixelWriteFunction colorWriteFunction, + uint8_t *destPixelData) +{ + colorWriteFunction(reinterpret_cast<const uint8_t *>(&color), destPixelData); +} + +template <int cols, int rows, bool IsColumnMajor> +inline int GetFlattenedIndex(int col, int row) +{ + if (IsColumnMajor) + { + return col * rows + row; + } + else + { + return row * cols + col; + } +} + +template <typename T, + bool IsSrcColumnMajor, + int colsSrc, + int rowsSrc, + bool IsDstColumnMajor, + int colsDst, + int rowsDst> +void ExpandMatrix(T *target, const GLfloat *value) +{ + static_assert(colsSrc <= colsDst && rowsSrc <= rowsDst, "Can only expand!"); + + constexpr int kDstFlatSize = colsDst * rowsDst; + T staging[kDstFlatSize] = {0}; + + for (int r = 0; r < rowsSrc; r++) + { + for (int c = 0; c < colsSrc; c++) + { + int srcIndex = GetFlattenedIndex<colsSrc, rowsSrc, IsSrcColumnMajor>(c, r); + int dstIndex = GetFlattenedIndex<colsDst, rowsDst, IsDstColumnMajor>(c, r); + + staging[dstIndex] = static_cast<T>(value[srcIndex]); + } + } + + memcpy(target, staging, kDstFlatSize * sizeof(T)); +} + +template <bool IsSrcColumMajor, + int colsSrc, + int rowsSrc, + bool IsDstColumnMajor, + int colsDst, + int rowsDst> +void SetFloatUniformMatrix(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + const GLfloat *value, + uint8_t *targetData) +{ + unsigned int count = + std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn)); + + const unsigned int targetMatrixStride = colsDst * rowsDst; + GLfloat *target = reinterpret_cast<GLfloat *>( + targetData + arrayElementOffset * sizeof(GLfloat) * targetMatrixStride); + + for (unsigned int i = 0; i < count; i++) + { + ExpandMatrix<GLfloat, IsSrcColumMajor, colsSrc, rowsSrc, IsDstColumnMajor, colsDst, + rowsDst>(target, value); + + target += targetMatrixStride; + value += colsSrc * rowsSrc; + } +} + +void SetFloatUniformMatrixFast(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + size_t matrixSize, + const GLfloat *value, + uint8_t *targetData) +{ + const unsigned int count = + std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn)); + + const uint8_t *valueData = reinterpret_cast<const uint8_t *>(value); + targetData = targetData + arrayElementOffset * matrixSize; + + memcpy(targetData, valueData, matrixSize * count); +} +} // anonymous namespace + +bool IsRotatedAspectRatio(SurfaceRotation rotation) +{ + switch (rotation) + { + case SurfaceRotation::Rotated90Degrees: + case SurfaceRotation::Rotated270Degrees: + case SurfaceRotation::FlippedRotated90Degrees: + case SurfaceRotation::FlippedRotated270Degrees: + return true; + default: + return false; + } +} + +void RotateRectangle(const SurfaceRotation rotation, + const bool flipY, + const int framebufferWidth, + const int framebufferHeight, + const gl::Rectangle &incoming, + gl::Rectangle *outgoing) +{ + // GLES's y-axis points up; Vulkan's points down. + switch (rotation) + { + case SurfaceRotation::Identity: + // Do not rotate gl_Position (surface matches the device's orientation): + outgoing->x = incoming.x; + outgoing->y = flipY ? framebufferHeight - incoming.y - incoming.height : incoming.y; + outgoing->width = incoming.width; + outgoing->height = incoming.height; + break; + case SurfaceRotation::Rotated90Degrees: + // Rotate gl_Position 90 degrees: + outgoing->x = incoming.y; + outgoing->y = flipY ? incoming.x : framebufferWidth - incoming.x - incoming.width; + outgoing->width = incoming.height; + outgoing->height = incoming.width; + break; + case SurfaceRotation::Rotated180Degrees: + // Rotate gl_Position 180 degrees: + outgoing->x = framebufferWidth - incoming.x - incoming.width; + outgoing->y = flipY ? incoming.y : framebufferHeight - incoming.y - incoming.height; + outgoing->width = incoming.width; + outgoing->height = incoming.height; + break; + case SurfaceRotation::Rotated270Degrees: + // Rotate gl_Position 270 degrees: + outgoing->x = framebufferHeight - incoming.y - incoming.height; + outgoing->y = flipY ? framebufferWidth - incoming.x - incoming.width : incoming.x; + outgoing->width = incoming.height; + outgoing->height = incoming.width; + break; + default: + UNREACHABLE(); + break; + } +} + +PackPixelsParams::PackPixelsParams() + : destFormat(nullptr), + outputPitch(0), + packBuffer(nullptr), + offset(0), + rotation(SurfaceRotation::Identity) +{} + +PackPixelsParams::PackPixelsParams(const gl::Rectangle &areaIn, + const angle::Format &destFormat, + GLuint outputPitchIn, + bool reverseRowOrderIn, + gl::Buffer *packBufferIn, + ptrdiff_t offsetIn) + : area(areaIn), + destFormat(&destFormat), + outputPitch(outputPitchIn), + packBuffer(packBufferIn), + reverseRowOrder(reverseRowOrderIn), + offset(offsetIn), + rotation(SurfaceRotation::Identity) +{} + +void PackPixels(const PackPixelsParams ¶ms, + const angle::Format &sourceFormat, + int inputPitchIn, + const uint8_t *sourceIn, + uint8_t *destWithoutOffset) +{ + uint8_t *destWithOffset = destWithoutOffset + params.offset; + + const uint8_t *source = sourceIn; + int inputPitch = inputPitchIn; + int destWidth = params.area.width; + int destHeight = params.area.height; + int xAxisPitch = 0; + int yAxisPitch = 0; + switch (params.rotation) + { + case SurfaceRotation::Identity: + // The source image is not rotated (i.e. matches the device's orientation), and may or + // may not be y-flipped. The image is row-major. Each source row (one step along the + // y-axis for each step in the dest y-axis) is inputPitch past the previous row. Along + // a row, each source pixel (one step along the x-axis for each step in the dest + // x-axis) is sourceFormat.pixelBytes past the previous pixel. + xAxisPitch = sourceFormat.pixelBytes; + if (params.reverseRowOrder) + { + // The source image is y-flipped, which means we start at the last row, and each + // source row is BEFORE the previous row. + source += inputPitchIn * (params.area.height - 1); + inputPitch = -inputPitch; + yAxisPitch = -inputPitchIn; + } + else + { + yAxisPitch = inputPitchIn; + } + break; + case SurfaceRotation::Rotated90Degrees: + // The source image is rotated 90 degrees counter-clockwise. Y-flip is always applied + // to rotated images. The image is column-major. Each source column (one step along + // the source x-axis for each step in the dest y-axis) is inputPitch past the previous + // column. Along a column, each source pixel (one step along the y-axis for each step + // in the dest x-axis) is sourceFormat.pixelBytes past the previous pixel. + xAxisPitch = inputPitchIn; + yAxisPitch = sourceFormat.pixelBytes; + destWidth = params.area.height; + destHeight = params.area.width; + break; + case SurfaceRotation::Rotated180Degrees: + // The source image is rotated 180 degrees. Y-flip is always applied to rotated + // images. The image is row-major, but upside down. Each source row (one step along + // the y-axis for each step in the dest y-axis) is inputPitch after the previous row. + // Along a row, each source pixel (one step along the x-axis for each step in the dest + // x-axis) is sourceFormat.pixelBytes BEFORE the previous pixel. + xAxisPitch = -static_cast<int>(sourceFormat.pixelBytes); + yAxisPitch = inputPitchIn; + source += sourceFormat.pixelBytes * (params.area.width - 1); + break; + case SurfaceRotation::Rotated270Degrees: + // The source image is rotated 270 degrees counter-clockwise (or 90 degrees clockwise). + // Y-flip is always applied to rotated images. The image is column-major, where each + // column (one step in the source x-axis for one step in the dest y-axis) is inputPitch + // BEFORE the previous column. Along a column, each source pixel (one step along the + // y-axis for each step in the dest x-axis) is sourceFormat.pixelBytes BEFORE the + // previous pixel. The first pixel is at the end of the source. + xAxisPitch = -inputPitchIn; + yAxisPitch = -static_cast<int>(sourceFormat.pixelBytes); + destWidth = params.area.height; + destHeight = params.area.width; + source += inputPitch * (params.area.height - 1) + + sourceFormat.pixelBytes * (params.area.width - 1); + break; + default: + UNREACHABLE(); + break; + } + + if (params.rotation == SurfaceRotation::Identity && sourceFormat == *params.destFormat) + { + // Direct copy possible + for (int y = 0; y < params.area.height; ++y) + { + memcpy(destWithOffset + y * params.outputPitch, source + y * inputPitch, + params.area.width * sourceFormat.pixelBytes); + } + return; + } + + FastCopyFunction fastCopyFunc = sourceFormat.fastCopyFunctions.get(params.destFormat->id); + + if (fastCopyFunc) + { + // Fast copy is possible through some special function + fastCopyFunc(source, xAxisPitch, yAxisPitch, destWithOffset, params.destFormat->pixelBytes, + params.outputPitch, destWidth, destHeight); + return; + } + + PixelWriteFunction pixelWriteFunction = params.destFormat->pixelWriteFunction; + ASSERT(pixelWriteFunction != nullptr); + + // Maximum size of any Color<T> type used. + uint8_t temp[16]; + static_assert(sizeof(temp) >= sizeof(gl::ColorF) && sizeof(temp) >= sizeof(gl::ColorUI) && + sizeof(temp) >= sizeof(gl::ColorI) && + sizeof(temp) >= sizeof(angle::DepthStencil), + "Unexpected size of pixel struct."); + + PixelReadFunction pixelReadFunction = sourceFormat.pixelReadFunction; + ASSERT(pixelReadFunction != nullptr); + + for (int y = 0; y < destHeight; ++y) + { + for (int x = 0; x < destWidth; ++x) + { + uint8_t *dest = + destWithOffset + y * params.outputPitch + x * params.destFormat->pixelBytes; + const uint8_t *src = source + y * yAxisPitch + x * xAxisPitch; + + // readFunc and writeFunc will be using the same type of color, CopyTexImage + // will not allow the copy otherwise. + pixelReadFunction(src, temp); + pixelWriteFunction(temp, dest); + } + } +} + +bool FastCopyFunctionMap::has(angle::FormatID formatID) const +{ + return (get(formatID) != nullptr); +} + +namespace +{ + +const FastCopyFunctionMap::Entry *getEntry(const FastCopyFunctionMap::Entry *entry, + size_t numEntries, + angle::FormatID formatID) +{ + const FastCopyFunctionMap::Entry *end = entry + numEntries; + while (entry != end) + { + if (entry->formatID == formatID) + { + return entry; + } + ++entry; + } + + return nullptr; +} + +} // namespace + +FastCopyFunction FastCopyFunctionMap::get(angle::FormatID formatID) const +{ + const FastCopyFunctionMap::Entry *entry = getEntry(mData, mSize, formatID); + return entry ? entry->func : nullptr; +} + +bool ShouldUseDebugLayers(const egl::AttributeMap &attribs) +{ + EGLAttrib debugSetting = + attribs.get(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, EGL_DONT_CARE); + + // Prefer to enable debug layers when available. +#if defined(ANGLE_ENABLE_ASSERTS) + return (debugSetting != EGL_FALSE); +#else + return (debugSetting == EGL_TRUE); +#endif // defined(ANGLE_ENABLE_ASSERTS) +} + +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) +{ + using ConversionFunction = void (*)(gl::ColorF *); + ConversionFunction conversionFunction = CopyColor; + if (unpackPremultiplyAlpha != unpackUnmultiplyAlpha) + { + if (unpackPremultiplyAlpha) + { + conversionFunction = PremultiplyAlpha; + } + else + { + conversionFunction = UnmultiplyAlpha; + } + } + + auto clipChannelsFunction = ClipChannelsNoOp; + switch (destUnsizedFormat) + { + case GL_RED: + clipChannelsFunction = ClipChannelsR; + break; + case GL_RG: + clipChannelsFunction = ClipChannelsRG; + break; + case GL_RGB: + clipChannelsFunction = ClipChannelsRGB; + break; + case GL_LUMINANCE: + clipChannelsFunction = ClipChannelsLuminance; + break; + case GL_ALPHA: + clipChannelsFunction = ClipChannelsAlpha; + break; + } + + auto writeFunction = (destComponentType == GL_UNSIGNED_INT) ? WriteUintColor : WriteFloatColor; + + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + const uint8_t *sourcePixelData = + sourceData + y * sourceRowPitch + x * sourcePixelBytes + z * sourceDepthPitch; + + gl::ColorF sourceColor; + pixelReadFunction(sourcePixelData, reinterpret_cast<uint8_t *>(&sourceColor)); + + conversionFunction(&sourceColor); + clipChannelsFunction(&sourceColor); + + size_t destY = 0; + if (unpackFlipY) + { + destY += (height - 1); + destY -= y; + } + else + { + destY += y; + } + + uint8_t *destPixelData = + destData + destY * destRowPitch + x * destPixelBytes + z * destDepthPitch; + writeFunction(sourceColor, pixelWriteFunction, destPixelData); + } + } + } +} + +// IncompleteTextureSet implementation. +IncompleteTextureSet::IncompleteTextureSet() : mIncompleteTextureBufferAttachment(nullptr) {} + +IncompleteTextureSet::~IncompleteTextureSet() {} + +void IncompleteTextureSet::onDestroy(const gl::Context *context) +{ + // Clear incomplete textures. + for (auto &incompleteTextures : mIncompleteTextures) + { + for (auto &incompleteTexture : incompleteTextures) + { + if (incompleteTexture.get() != nullptr) + { + incompleteTexture->onDestroy(context); + incompleteTexture.set(context, nullptr); + } + } + } + if (mIncompleteTextureBufferAttachment != nullptr) + { + mIncompleteTextureBufferAttachment->onDestroy(context); + mIncompleteTextureBufferAttachment = nullptr; + } +} + +angle::Result IncompleteTextureSet::getIncompleteTexture( + const gl::Context *context, + gl::TextureType type, + gl::SamplerFormat format, + MultisampleTextureInitializer *multisampleInitializer, + gl::Texture **textureOut) +{ + *textureOut = mIncompleteTextures[format][type].get(); + if (*textureOut != nullptr) + { + return angle::Result::Continue; + } + + ContextImpl *implFactory = context->getImplementation(); + + gl::Extents colorSize(1, 1, 1); + gl::PixelUnpackState unpack; + unpack.alignment = 1; + gl::Box area(0, 0, 0, 1, 1, 1); + const IncompleteTextureParameters &incompleteTextureParam = + kIncompleteTextureParameters[format]; + + // Cube map arrays are expected to have layer counts that are multiples of 6 + constexpr int kCubeMapArraySize = 6; + if (type == gl::TextureType::CubeMapArray) + { + // From the GLES 3.2 spec: + // 8.18. IMMUTABLE-FORMAT TEXTURE IMAGES + // TexStorage3D Errors + // An INVALID_OPERATION error is generated if any of the following conditions hold: + // * target is TEXTURE_CUBE_MAP_ARRAY and depth is not a multiple of 6 + // Since ANGLE treats incomplete textures as immutable, respect that here. + colorSize.depth = kCubeMapArraySize; + area.depth = kCubeMapArraySize; + } + + // If a texture is external use a 2D texture for the incomplete texture + gl::TextureType createType = (type == gl::TextureType::External) ? gl::TextureType::_2D : type; + + gl::Texture *tex = + new gl::Texture(implFactory, {std::numeric_limits<GLuint>::max()}, createType); + angle::UniqueObjectPointer<gl::Texture, gl::Context> t(tex, context); + + // This is a bit of a kludge but is necessary to consume the error. + gl::Context *mutableContext = const_cast<gl::Context *>(context); + + if (createType == gl::TextureType::Buffer) + { + constexpr uint32_t kBufferInitData = 0; + mIncompleteTextureBufferAttachment = + new gl::Buffer(implFactory, {std::numeric_limits<GLuint>::max()}); + ANGLE_TRY(mIncompleteTextureBufferAttachment->bufferData( + mutableContext, gl::BufferBinding::Texture, &kBufferInitData, sizeof(kBufferInitData), + gl::BufferUsage::StaticDraw)); + } + else if (createType == gl::TextureType::_2DMultisample) + { + ANGLE_TRY(t->setStorageMultisample(mutableContext, createType, 1, + incompleteTextureParam.sizedInternalFormat, colorSize, + true)); + } + else + { + ANGLE_TRY(t->setStorage(mutableContext, createType, 1, + incompleteTextureParam.sizedInternalFormat, colorSize)); + } + + if (type == gl::TextureType::CubeMap) + { + for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets()) + { + ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr, face, 0, area, + incompleteTextureParam.format, incompleteTextureParam.type, + incompleteTextureParam.clearColor)); + } + } + else if (type == gl::TextureType::CubeMapArray) + { + // We need to provide enough pixel data to fill the array of six faces + GLubyte incompleteCubeArrayPixels[kCubeMapArraySize][4]; + for (int i = 0; i < kCubeMapArraySize; ++i) + { + incompleteCubeArrayPixels[i][0] = incompleteTextureParam.clearColor[0]; + incompleteCubeArrayPixels[i][1] = incompleteTextureParam.clearColor[1]; + incompleteCubeArrayPixels[i][2] = incompleteTextureParam.clearColor[2]; + incompleteCubeArrayPixels[i][3] = incompleteTextureParam.clearColor[3]; + } + + ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr, + gl::NonCubeTextureTypeToTarget(createType), 0, area, + incompleteTextureParam.format, incompleteTextureParam.type, + *incompleteCubeArrayPixels)); + } + else if (type == gl::TextureType::_2DMultisample) + { + // Call a specialized clear function to init a multisample texture. + ANGLE_TRY(multisampleInitializer->initializeMultisampleTextureToBlack(context, t.get())); + } + else if (type == gl::TextureType::Buffer) + { + ANGLE_TRY(t->setBuffer(context, mIncompleteTextureBufferAttachment, + incompleteTextureParam.sizedInternalFormat)); + } + else + { + ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr, + gl::NonCubeTextureTypeToTarget(createType), 0, area, + incompleteTextureParam.format, incompleteTextureParam.type, + incompleteTextureParam.clearColor)); + } + + if (format == gl::SamplerFormat::Shadow) + { + // To avoid the undefined spec behavior for shadow samplers with a depth texture, we set the + // compare mode to GL_COMPARE_REF_TO_TEXTURE + ASSERT(!t->hasObservers()); + t->setCompareMode(context, GL_COMPARE_REF_TO_TEXTURE); + } + + ANGLE_TRY(t->syncState(context, gl::Command::Other)); + + mIncompleteTextures[format][type].set(context, t.release()); + *textureOut = mIncompleteTextures[format][type].get(); + return angle::Result::Continue; +} + +#define ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \ + template void SetFloatUniformMatrix##api<cols, rows>::Run( \ + unsigned int, unsigned int, GLsizei, GLboolean, const GLfloat *, uint8_t *) + +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 2); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 3); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 3); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 2); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 2); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 3); + +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 2); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 3); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 3); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 2); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 4); +ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 4); + +#undef ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC + +#define ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \ + template void SetFloatUniformMatrix##api<cols, 4>::Run(unsigned int, unsigned int, GLsizei, \ + GLboolean, const GLfloat *, uint8_t *) + +template <int cols> +struct SetFloatUniformMatrixGLSL<cols, 4> +{ + static void Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData); +}; + +ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 4); +ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 4); +ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 4); + +#undef ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC + +#define ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \ + template void SetFloatUniformMatrix##api<4, rows>::Run(unsigned int, unsigned int, GLsizei, \ + GLboolean, const GLfloat *, uint8_t *) + +template <int rows> +struct SetFloatUniformMatrixHLSL<4, rows> +{ + static void Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData); +}; + +ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 2); +ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 3); +ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 4); + +#undef ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC + +template <int cols> +void SetFloatUniformMatrixGLSL<cols, 4>::Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData) +{ + const bool isSrcColumnMajor = !transpose; + if (isSrcColumnMajor) + { + // Both src and dst matrixs are has same layout, + // a single memcpy updates all the matrices + constexpr size_t srcMatrixSize = sizeof(GLfloat) * cols * 4; + SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value, + targetData); + } + else + { + // fallback to general cases + SetFloatUniformMatrix<false, cols, 4, true, cols, 4>(arrayElementOffset, elementCount, + countIn, value, targetData); + } +} + +template <int cols, int rows> +void SetFloatUniformMatrixGLSL<cols, rows>::Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData) +{ + const bool isSrcColumnMajor = !transpose; + // GLSL expects matrix uniforms to be column-major, and each column is padded to 4 rows. + if (isSrcColumnMajor) + { + SetFloatUniformMatrix<true, cols, rows, true, cols, 4>(arrayElementOffset, elementCount, + countIn, value, targetData); + } + else + { + SetFloatUniformMatrix<false, cols, rows, true, cols, 4>(arrayElementOffset, elementCount, + countIn, value, targetData); + } +} + +template <int rows> +void SetFloatUniformMatrixHLSL<4, rows>::Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData) +{ + const bool isSrcColumnMajor = !transpose; + if (!isSrcColumnMajor) + { + // Both src and dst matrixs are has same layout, + // a single memcpy updates all the matrices + constexpr size_t srcMatrixSize = sizeof(GLfloat) * 4 * rows; + SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value, + targetData); + } + else + { + // fallback to general cases + SetFloatUniformMatrix<true, 4, rows, false, 4, rows>(arrayElementOffset, elementCount, + countIn, value, targetData); + } +} + +template <int cols, int rows> +void SetFloatUniformMatrixHLSL<cols, rows>::Run(unsigned int arrayElementOffset, + unsigned int elementCount, + GLsizei countIn, + GLboolean transpose, + const GLfloat *value, + uint8_t *targetData) +{ + const bool isSrcColumnMajor = !transpose; + // Internally store matrices as row-major to accomodate HLSL matrix indexing. Each row is + // padded to 4 columns. + if (!isSrcColumnMajor) + { + SetFloatUniformMatrix<false, cols, rows, false, 4, rows>(arrayElementOffset, elementCount, + countIn, value, targetData); + } + else + { + SetFloatUniformMatrix<true, cols, rows, false, 4, rows>(arrayElementOffset, elementCount, + countIn, value, targetData); + } +} + +template void GetMatrixUniform<GLint>(GLenum, GLint *, const GLint *, bool); +template void GetMatrixUniform<GLuint>(GLenum, GLuint *, const GLuint *, bool); + +void GetMatrixUniform(GLenum type, GLfloat *dataOut, const GLfloat *source, bool transpose) +{ + int columns = gl::VariableColumnCount(type); + int rows = gl::VariableRowCount(type); + for (GLint col = 0; col < columns; ++col) + { + for (GLint row = 0; row < rows; ++row) + { + GLfloat *outptr = dataOut + ((col * rows) + row); + const GLfloat *inptr = + transpose ? source + ((row * 4) + col) : source + ((col * 4) + row); + *outptr = *inptr; + } + } +} + +template <typename NonFloatT> +void GetMatrixUniform(GLenum type, NonFloatT *dataOut, const NonFloatT *source, bool transpose) +{ + UNREACHABLE(); +} + +const angle::Format &GetFormatFromFormatType(GLenum format, GLenum type) +{ + GLenum sizedInternalFormat = gl::GetInternalFormatInfo(format, type).sizedInternalFormat; + angle::FormatID angleFormatID = angle::Format::InternalFormatToID(sizedInternalFormat); + return angle::Format::Get(angleFormatID); +} + +angle::Result ComputeStartVertex(ContextImpl *contextImpl, + const gl::IndexRange &indexRange, + GLint baseVertex, + GLint *firstVertexOut) +{ + // The entire index range should be within the limits of a 32-bit uint because the largest + // GL index type is GL_UNSIGNED_INT. + ASSERT(indexRange.start <= std::numeric_limits<uint32_t>::max() && + indexRange.end <= std::numeric_limits<uint32_t>::max()); + + // The base vertex is only used in DrawElementsIndirect. Given the assertion above and the + // type of mBaseVertex (GLint), adding them both as 64-bit ints is safe. + int64_t startVertexInt64 = + static_cast<int64_t>(baseVertex) + static_cast<int64_t>(indexRange.start); + + // OpenGL ES 3.2 spec section 10.5: "Behavior of DrawElementsOneInstance is undefined if the + // vertex ID is negative for any element" + ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 >= 0); + + // OpenGL ES 3.2 spec section 10.5: "If the vertex ID is larger than the maximum value + // representable by type, it should behave as if the calculation were upconverted to 32-bit + // unsigned integers(with wrapping on overflow conditions)." ANGLE does not fully handle + // these rules, an overflow error is returned if the start vertex cannot be stored in a + // 32-bit signed integer. + ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 <= std::numeric_limits<GLint>::max()); + + *firstVertexOut = static_cast<GLint>(startVertexInt64); + return angle::Result::Continue; +} + +angle::Result GetVertexRangeInfo(const gl::Context *context, + GLint firstVertex, + GLsizei vertexOrIndexCount, + gl::DrawElementsType indexTypeOrInvalid, + const void *indices, + GLint baseVertex, + GLint *startVertexOut, + size_t *vertexCountOut) +{ + if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum) + { + gl::IndexRange indexRange; + ANGLE_TRY(context->getState().getVertexArray()->getIndexRange( + context, indexTypeOrInvalid, vertexOrIndexCount, indices, &indexRange)); + ANGLE_TRY(ComputeStartVertex(context->getImplementation(), indexRange, baseVertex, + startVertexOut)); + *vertexCountOut = indexRange.vertexCount(); + } + else + { + *startVertexOut = firstVertex; + *vertexCountOut = vertexOrIndexCount; + } + return angle::Result::Continue; +} + +gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY) +{ + // If the scissor test isn't enabled, assume it has infinite size. Its intersection with the + // rect would be the rect itself. + // + // Note that on Vulkan, returning this (as opposed to a fixed max-int-sized rect) could lead to + // unnecessary pipeline creations if two otherwise identical pipelines are used on framebuffers + // with different sizes. If such usage is observed in an application, we should investigate + // possible optimizations. + if (!glState.isScissorTestEnabled()) + { + return rect; + } + + gl::Rectangle clippedRect; + if (!gl::ClipRectangle(glState.getScissor(), rect, &clippedRect)) + { + return gl::Rectangle(); + } + + if (invertY) + { + clippedRect.y = rect.height - clippedRect.y - clippedRect.height; + } + + return clippedRect; +} + +void LogFeatureStatus(const angle::FeatureSetBase &features, + const std::vector<std::string> &featureNames, + bool enabled) +{ + for (const std::string &name : featureNames) + { + const bool hasWildcard = name.back() == '*'; + for (auto iter : features.getFeatures()) + { + const std::string &featureName = iter.first; + + if (!angle::FeatureNameMatch(featureName, name)) + { + continue; + } + + INFO() << "Feature: " << featureName << (enabled ? " enabled" : " disabled"); + + if (!hasWildcard) + { + break; + } + } + } +} + +void ApplyFeatureOverrides(angle::FeatureSetBase *features, const egl::DisplayState &state) +{ + features->overrideFeatures(state.featureOverridesEnabled, true); + features->overrideFeatures(state.featureOverridesDisabled, false); + + // Override with environment as well. + constexpr char kAngleFeatureOverridesEnabledEnvName[] = "ANGLE_FEATURE_OVERRIDES_ENABLED"; + constexpr char kAngleFeatureOverridesDisabledEnvName[] = "ANGLE_FEATURE_OVERRIDES_DISABLED"; + constexpr char kAngleFeatureOverridesEnabledPropertyName[] = + "debug.angle.feature_overrides_enabled"; + constexpr char kAngleFeatureOverridesDisabledPropertyName[] = + "debug.angle.feature_overrides_disabled"; + std::vector<std::string> overridesEnabled = + angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty( + kAngleFeatureOverridesEnabledEnvName, kAngleFeatureOverridesEnabledPropertyName, ":"); + std::vector<std::string> overridesDisabled = + angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty( + kAngleFeatureOverridesDisabledEnvName, kAngleFeatureOverridesDisabledPropertyName, ":"); + + features->overrideFeatures(overridesEnabled, true); + LogFeatureStatus(*features, overridesEnabled, true); + + features->overrideFeatures(overridesDisabled, false); + LogFeatureStatus(*features, overridesDisabled, false); +} + +void GetSamplePosition(GLsizei sampleCount, size_t index, GLfloat *xy) +{ + ASSERT(gl::isPow2(sampleCount)); + if (sampleCount > 16) + { + // Vulkan (and D3D11) doesn't have standard sample positions for 32 and 64 samples (and no + // drivers are known to support that many samples) + xy[0] = 0.5f; + xy[1] = 0.5f; + } + else + { + size_t indexKey = static_cast<size_t>(gl::log2(sampleCount)); + ASSERT(indexKey < kSamplePositions.size() && + (2 * index + 1) < kSamplePositions[indexKey].size()); + + xy[0] = kSamplePositions[indexKey][2 * index]; + xy[1] = kSamplePositions[indexKey][2 * index + 1]; + } +} + +// These macros are to avoid code too much duplication for variations of multi draw types +#define DRAW_ARRAYS__ contextImpl->drawArrays(context, mode, firsts[drawID], counts[drawID]) +#define DRAW_ARRAYS_INSTANCED_ \ + contextImpl->drawArraysInstanced(context, mode, firsts[drawID], counts[drawID], \ + instanceCounts[drawID]) +#define DRAW_ELEMENTS__ \ + contextImpl->drawElements(context, mode, counts[drawID], type, indices[drawID]) +#define DRAW_ELEMENTS_INSTANCED_ \ + contextImpl->drawElementsInstanced(context, mode, counts[drawID], type, indices[drawID], \ + instanceCounts[drawID]) +#define DRAW_ARRAYS_INSTANCED_BASE_INSTANCE \ + contextImpl->drawArraysInstancedBaseInstance(context, mode, firsts[drawID], counts[drawID], \ + instanceCounts[drawID], baseInstances[drawID]) +#define DRAW_ELEMENTS_INSTANCED_BASE_VERTEX_BASE_INSTANCE \ + contextImpl->drawElementsInstancedBaseVertexBaseInstance( \ + context, mode, counts[drawID], type, indices[drawID], instanceCounts[drawID], \ + baseVertices[drawID], baseInstances[drawID]) +#define DRAW_CALL(drawType, instanced, bvbi) DRAW_##drawType##instanced##bvbi + +#define MULTI_DRAW_BLOCK(drawType, instanced, bvbi, hasDrawID, hasBaseVertex, hasBaseInstance) \ + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) \ + { \ + if (ANGLE_NOOP_DRAW(instanced)) \ + { \ + ANGLE_TRY(contextImpl->handleNoopDrawEvent()); \ + continue; \ + } \ + ANGLE_SET_DRAW_ID_UNIFORM(hasDrawID)(drawID); \ + ANGLE_SET_BASE_VERTEX_UNIFORM(hasBaseVertex)(baseVertices[drawID]); \ + ANGLE_SET_BASE_INSTANCE_UNIFORM(hasBaseInstance)(baseInstances[drawID]); \ + ANGLE_TRY(DRAW_CALL(drawType, instanced, bvbi)); \ + ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced); \ + gl::MarkShaderStorageUsage(context); \ + } + +angle::Result MultiDrawArraysGeneral(ContextImpl *contextImpl, + const gl::Context *context, + gl::PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + GLsizei drawcount) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + MULTI_DRAW_BLOCK(ARRAYS, _, _, 1, 0, 0) + } + else + { + MULTI_DRAW_BLOCK(ARRAYS, _, _, 0, 0, 0) + } + + return angle::Result::Continue; +} + +angle::Result MultiDrawArraysIndirectGeneral(ContextImpl *contextImpl, + const gl::Context *context, + gl::PrimitiveMode mode, + const void *indirect, + GLsizei drawcount, + GLsizei stride) +{ + const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect); + + for (auto count = 0; count < drawcount; count++) + { + ANGLE_TRY(contextImpl->drawArraysIndirect( + context, mode, reinterpret_cast<const gl::DrawArraysIndirectCommand *>(indirectPtr))); + if (stride == 0) + { + indirectPtr += sizeof(gl::DrawArraysIndirectCommand); + } + else + { + indirectPtr += stride; + } + } + + return angle::Result::Continue; +} + +angle::Result MultiDrawArraysInstancedGeneral(ContextImpl *contextImpl, + const gl::Context *context, + gl::PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + const GLsizei *instanceCounts, + GLsizei drawcount) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 1, 0, 0) + } + else + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 0, 0, 0) + } + + return angle::Result::Continue; +} + +angle::Result MultiDrawElementsGeneral(ContextImpl *contextImpl, + const gl::Context *context, + gl::PrimitiveMode mode, + const GLsizei *counts, + gl::DrawElementsType type, + const GLvoid *const *indices, + GLsizei drawcount) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + MULTI_DRAW_BLOCK(ELEMENTS, _, _, 1, 0, 0) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _, _, 0, 0, 0) + } + + return angle::Result::Continue; +} + +angle::Result MultiDrawElementsIndirectGeneral(ContextImpl *contextImpl, + const gl::Context *context, + gl::PrimitiveMode mode, + gl::DrawElementsType type, + const void *indirect, + GLsizei drawcount, + GLsizei stride) +{ + const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect); + + for (auto count = 0; count < drawcount; count++) + { + ANGLE_TRY(contextImpl->drawElementsIndirect( + context, mode, type, + reinterpret_cast<const gl::DrawElementsIndirectCommand *>(indirectPtr))); + if (stride == 0) + { + indirectPtr += sizeof(gl::DrawElementsIndirectCommand); + } + else + { + indirectPtr += stride; + } + } + + return angle::Result::Continue; +} + +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) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 1, 0, 0) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 0, 0, 0) + } + + return angle::Result::Continue; +} + +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) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform(); + ResetBaseVertexBaseInstance resetUniforms(programObject, false, hasBaseInstance); + + if (hasDrawID && hasBaseInstance) + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 1) + } + else if (hasDrawID) + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 0) + } + else if (hasBaseInstance) + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 1) + } + else + { + MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 0) + } + + return angle::Result::Continue; +} + +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) +{ + gl::Program *programObject = context->getState().getLinkedProgram(context); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + const bool hasBaseVertex = programObject && programObject->hasBaseVertexUniform(); + const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform(); + ResetBaseVertexBaseInstance resetUniforms(programObject, hasBaseVertex, hasBaseInstance); + + if (hasDrawID) + { + if (hasBaseVertex) + { + if (hasBaseInstance) + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 1) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 0) + } + } + else + { + if (hasBaseInstance) + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 1) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 0) + } + } + } + else + { + if (hasBaseVertex) + { + if (hasBaseInstance) + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 1) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 0) + } + } + else + { + if (hasBaseInstance) + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 1) + } + else + { + MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 0) + } + } + } + + return angle::Result::Continue; +} + +ResetBaseVertexBaseInstance::ResetBaseVertexBaseInstance(gl::Program *programObject, + bool resetBaseVertex, + bool resetBaseInstance) + : mProgramObject(programObject), + mResetBaseVertex(resetBaseVertex), + mResetBaseInstance(resetBaseInstance) +{} + +ResetBaseVertexBaseInstance::~ResetBaseVertexBaseInstance() +{ + if (mProgramObject) + { + // Reset emulated uniforms to zero to avoid affecting other draw calls + if (mResetBaseVertex) + { + mProgramObject->setBaseVertexUniform(0); + } + + if (mResetBaseInstance) + { + mProgramObject->setBaseInstanceUniform(0); + } + } +} + +angle::FormatID ConvertToSRGB(angle::FormatID formatID) +{ + switch (formatID) + { + case angle::FormatID::R8_UNORM: + return angle::FormatID::R8_UNORM_SRGB; + case angle::FormatID::R8G8_UNORM: + return angle::FormatID::R8G8_UNORM_SRGB; + case angle::FormatID::R8G8B8_UNORM: + return angle::FormatID::R8G8B8_UNORM_SRGB; + case angle::FormatID::R8G8B8A8_UNORM: + return angle::FormatID::R8G8B8A8_UNORM_SRGB; + case angle::FormatID::B8G8R8A8_UNORM: + return angle::FormatID::B8G8R8A8_UNORM_SRGB; + case angle::FormatID::BC1_RGB_UNORM_BLOCK: + return angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK; + case angle::FormatID::BC1_RGBA_UNORM_BLOCK: + return angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK; + case angle::FormatID::BC2_RGBA_UNORM_BLOCK: + return angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK; + case angle::FormatID::BC3_RGBA_UNORM_BLOCK: + return angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK; + case angle::FormatID::BC7_RGBA_UNORM_BLOCK: + return angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK; + case angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK: + return angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK; + case angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK: + return angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK; + case angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK: + return angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK; + case angle::FormatID::ASTC_4x4_UNORM_BLOCK: + return angle::FormatID::ASTC_4x4_SRGB_BLOCK; + case angle::FormatID::ASTC_5x4_UNORM_BLOCK: + return angle::FormatID::ASTC_5x4_SRGB_BLOCK; + case angle::FormatID::ASTC_5x5_UNORM_BLOCK: + return angle::FormatID::ASTC_5x5_SRGB_BLOCK; + case angle::FormatID::ASTC_6x5_UNORM_BLOCK: + return angle::FormatID::ASTC_6x5_SRGB_BLOCK; + case angle::FormatID::ASTC_6x6_UNORM_BLOCK: + return angle::FormatID::ASTC_6x6_SRGB_BLOCK; + case angle::FormatID::ASTC_8x5_UNORM_BLOCK: + return angle::FormatID::ASTC_8x5_SRGB_BLOCK; + case angle::FormatID::ASTC_8x6_UNORM_BLOCK: + return angle::FormatID::ASTC_8x6_SRGB_BLOCK; + case angle::FormatID::ASTC_8x8_UNORM_BLOCK: + return angle::FormatID::ASTC_8x8_SRGB_BLOCK; + case angle::FormatID::ASTC_10x5_UNORM_BLOCK: + return angle::FormatID::ASTC_10x5_SRGB_BLOCK; + case angle::FormatID::ASTC_10x6_UNORM_BLOCK: + return angle::FormatID::ASTC_10x6_SRGB_BLOCK; + case angle::FormatID::ASTC_10x8_UNORM_BLOCK: + return angle::FormatID::ASTC_10x8_SRGB_BLOCK; + case angle::FormatID::ASTC_10x10_UNORM_BLOCK: + return angle::FormatID::ASTC_10x10_SRGB_BLOCK; + case angle::FormatID::ASTC_12x10_UNORM_BLOCK: + return angle::FormatID::ASTC_12x10_SRGB_BLOCK; + case angle::FormatID::ASTC_12x12_UNORM_BLOCK: + return angle::FormatID::ASTC_12x12_SRGB_BLOCK; + default: + return angle::FormatID::NONE; + } +} + +angle::FormatID ConvertToLinear(angle::FormatID formatID) +{ + switch (formatID) + { + case angle::FormatID::R8_UNORM_SRGB: + return angle::FormatID::R8_UNORM; + case angle::FormatID::R8G8_UNORM_SRGB: + return angle::FormatID::R8G8_UNORM; + case angle::FormatID::R8G8B8_UNORM_SRGB: + return angle::FormatID::R8G8B8_UNORM; + case angle::FormatID::R8G8B8A8_UNORM_SRGB: + return angle::FormatID::R8G8B8A8_UNORM; + case angle::FormatID::B8G8R8A8_UNORM_SRGB: + return angle::FormatID::B8G8R8A8_UNORM; + case angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK: + return angle::FormatID::BC1_RGB_UNORM_BLOCK; + case angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK: + return angle::FormatID::BC1_RGBA_UNORM_BLOCK; + case angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK: + return angle::FormatID::BC2_RGBA_UNORM_BLOCK; + case angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK: + return angle::FormatID::BC3_RGBA_UNORM_BLOCK; + case angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK: + return angle::FormatID::BC7_RGBA_UNORM_BLOCK; + case angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK: + return angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK; + case angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK: + return angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK; + case angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK: + return angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK; + case angle::FormatID::ASTC_4x4_SRGB_BLOCK: + return angle::FormatID::ASTC_4x4_UNORM_BLOCK; + case angle::FormatID::ASTC_5x4_SRGB_BLOCK: + return angle::FormatID::ASTC_5x4_UNORM_BLOCK; + case angle::FormatID::ASTC_5x5_SRGB_BLOCK: + return angle::FormatID::ASTC_5x5_UNORM_BLOCK; + case angle::FormatID::ASTC_6x5_SRGB_BLOCK: + return angle::FormatID::ASTC_6x5_UNORM_BLOCK; + case angle::FormatID::ASTC_6x6_SRGB_BLOCK: + return angle::FormatID::ASTC_6x6_UNORM_BLOCK; + case angle::FormatID::ASTC_8x5_SRGB_BLOCK: + return angle::FormatID::ASTC_8x5_UNORM_BLOCK; + case angle::FormatID::ASTC_8x6_SRGB_BLOCK: + return angle::FormatID::ASTC_8x6_UNORM_BLOCK; + case angle::FormatID::ASTC_8x8_SRGB_BLOCK: + return angle::FormatID::ASTC_8x8_UNORM_BLOCK; + case angle::FormatID::ASTC_10x5_SRGB_BLOCK: + return angle::FormatID::ASTC_10x5_UNORM_BLOCK; + case angle::FormatID::ASTC_10x6_SRGB_BLOCK: + return angle::FormatID::ASTC_10x6_UNORM_BLOCK; + case angle::FormatID::ASTC_10x8_SRGB_BLOCK: + return angle::FormatID::ASTC_10x8_UNORM_BLOCK; + case angle::FormatID::ASTC_10x10_SRGB_BLOCK: + return angle::FormatID::ASTC_10x10_UNORM_BLOCK; + case angle::FormatID::ASTC_12x10_SRGB_BLOCK: + return angle::FormatID::ASTC_12x10_UNORM_BLOCK; + case angle::FormatID::ASTC_12x12_SRGB_BLOCK: + return angle::FormatID::ASTC_12x12_UNORM_BLOCK; + default: + return angle::FormatID::NONE; + } +} + +bool IsOverridableLinearFormat(angle::FormatID formatID) +{ + return ConvertToSRGB(formatID) != angle::FormatID::NONE; +} +} // namespace rx |