summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/renderer/renderer_utils.cpp1583
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 &params,
+ 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