diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp')
-rw-r--r-- | Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp | 1017 |
1 files changed, 1017 insertions, 0 deletions
diff --git a/Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp new file mode 100644 index 000000000..15cf6271a --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -0,0 +1,1017 @@ +// +// Copyright (c) 2012-2014 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. +// + +// renderergl_utils.cpp: Conversion functions and other utility routines +// specific to the OpenGL renderer. + +#include "libANGLE/renderer/gl/renderergl_utils.h" + +#include <limits> + +#include "common/mathutil.h" +#include "libANGLE/Buffer.h" +#include "libANGLE/Caps.h" +#include "libANGLE/formatutils.h" +#include "libANGLE/renderer/gl/FunctionsGL.h" +#include "libANGLE/renderer/gl/WorkaroundsGL.h" +#include "libANGLE/renderer/gl/formatutilsgl.h" + +#include <algorithm> +#include <sstream> + +using angle::CheckedNumeric; + +namespace rx +{ +VendorID GetVendorID(const FunctionsGL *functions) +{ + std::string nativeVendorString(reinterpret_cast<const char *>(functions->getString(GL_VENDOR))); + if (nativeVendorString.find("Intel") != std::string::npos) + { + return VENDOR_ID_INTEL; + } + else if (nativeVendorString.find("NVIDIA") != std::string::npos) + { + return VENDOR_ID_NVIDIA; + } + else if (nativeVendorString.find("ATI") != std::string::npos || + nativeVendorString.find("AMD") != std::string::npos) + { + return VENDOR_ID_AMD; + } + else if (nativeVendorString.find("Qualcomm") != std::string::npos) + { + return VENDOR_ID_QUALCOMM; + } + else + { + return VENDOR_ID_UNKNOWN; + } +} + +namespace nativegl_gl +{ + +static bool MeetsRequirements(const FunctionsGL *functions, const nativegl::SupportRequirement &requirements) +{ + for (const std::string &extension : requirements.requiredExtensions) + { + if (!functions->hasExtension(extension)) + { + return false; + } + } + + if (functions->version >= requirements.version) + { + return true; + } + else if (!requirements.versionExtensions.empty()) + { + for (const std::string &extension : requirements.versionExtensions) + { + if (!functions->hasExtension(extension)) + { + return false; + } + } + return true; + } + else + { + return false; + } +} + +static gl::TextureCaps GenerateTextureFormatCaps(const FunctionsGL *functions, GLenum internalFormat) +{ + gl::TextureCaps textureCaps; + + const nativegl::InternalFormat &formatInfo = nativegl::GetInternalFormatInfo(internalFormat, functions->standard); + textureCaps.texturable = MeetsRequirements(functions, formatInfo.texture); + textureCaps.filterable = textureCaps.texturable && MeetsRequirements(functions, formatInfo.filter); + textureCaps.renderable = MeetsRequirements(functions, formatInfo.framebufferAttachment); + + // glGetInternalformativ is not available until version 4.2 but may be available through the 3.0 + // extension GL_ARB_internalformat_query + if (textureCaps.renderable && functions->getInternalformativ) + { + GLint numSamples = 0; + functions->getInternalformativ(GL_RENDERBUFFER, internalFormat, GL_NUM_SAMPLE_COUNTS, 1, &numSamples); + + if (numSamples > 0) + { + std::vector<GLint> samples(numSamples); + functions->getInternalformativ(GL_RENDERBUFFER, internalFormat, GL_SAMPLES, + static_cast<GLsizei>(samples.size()), &samples[0]); + for (size_t sampleIndex = 0; sampleIndex < samples.size(); sampleIndex++) + { + textureCaps.sampleCounts.insert(samples[sampleIndex]); + } + } + } + + return textureCaps; +} + +static GLint QuerySingleGLInt(const FunctionsGL *functions, GLenum name) +{ + GLint result = 0; + functions->getIntegerv(name, &result); + return result; +} + +static GLint QuerySingleIndexGLInt(const FunctionsGL *functions, GLenum name, GLuint index) +{ + GLint result; + functions->getIntegeri_v(name, index, &result); + return result; +} + +static GLint QueryGLIntRange(const FunctionsGL *functions, GLenum name, size_t index) +{ + GLint result[2] = {}; + functions->getIntegerv(name, result); + return result[index]; +} + +static GLint64 QuerySingleGLInt64(const FunctionsGL *functions, GLenum name) +{ + GLint64 result = 0; + functions->getInteger64v(name, &result); + return result; +} + +static GLfloat QuerySingleGLFloat(const FunctionsGL *functions, GLenum name) +{ + GLfloat result = 0.0f; + functions->getFloatv(name, &result); + return result; +} + +static GLfloat QueryGLFloatRange(const FunctionsGL *functions, GLenum name, size_t index) +{ + GLfloat result[2] = {}; + functions->getFloatv(name, result); + return result[index]; +} + +static gl::TypePrecision QueryTypePrecision(const FunctionsGL *functions, GLenum shaderType, GLenum precisionType) +{ + gl::TypePrecision precision; + functions->getShaderPrecisionFormat(shaderType, precisionType, precision.range, &precision.precision); + return precision; +} + +static GLint QueryQueryValue(const FunctionsGL *functions, GLenum target, GLenum name) +{ + GLint result; + functions->getQueryiv(target, name, &result); + return result; +} + +static void LimitVersion(gl::Version *curVersion, const gl::Version &maxVersion) +{ + if (*curVersion >= maxVersion) + { + *curVersion = maxVersion; + } +} + +void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, + gl::Extensions *extensions, gl::Version *maxSupportedESVersion) +{ + // Texture format support checks + const gl::FormatSet &allFormats = gl::GetAllSizedInternalFormats(); + for (GLenum internalFormat : allFormats) + { + gl::TextureCaps textureCaps = GenerateTextureFormatCaps(functions, internalFormat); + textureCapsMap->insert(internalFormat, textureCaps); + + if (gl::GetInternalFormatInfo(internalFormat).compressed) + { + caps->compressedTextureFormats.push_back(internalFormat); + } + } + + // Start by assuming ES3.1 support and work down + *maxSupportedESVersion = gl::Version(3, 1); + + // Table 6.28, implementation dependent values + if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->hasGLExtension("GL_ARB_ES3_compatibility") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxElementIndex = QuerySingleGLInt64(functions, GL_MAX_ELEMENT_INDEX); + } + else + { + // Doesn't affect ES3 support, can use a pre-defined limit + caps->maxElementIndex = static_cast<GLint64>(std::numeric_limits<unsigned int>::max()); + } + + if (functions->isAtLeastGL(gl::Version(1, 2)) || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_texture_3D")) + { + caps->max3DTextureSize = QuerySingleGLInt(functions, GL_MAX_3D_TEXTURE_SIZE); + } + else + { + // Can't support ES3 without 3D textures + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + caps->max2DTextureSize = QuerySingleGLInt(functions, GL_MAX_TEXTURE_SIZE); // GL 1.0 / ES 2.0 + caps->maxCubeMapTextureSize = QuerySingleGLInt(functions, GL_MAX_CUBE_MAP_TEXTURE_SIZE); // GL 1.3 / ES 2.0 + + if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_EXT_texture_array") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxArrayTextureLayers = QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS); + } + else + { + // Can't support ES3 without array textures + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(1, 5)) || functions->hasGLExtension("GL_EXT_texture_lod_bias") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxLODBias = QuerySingleGLFloat(functions, GL_MAX_TEXTURE_LOD_BIAS); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_EXT_framebuffer_object") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxRenderbufferSize = QuerySingleGLInt(functions, GL_MAX_RENDERBUFFER_SIZE); + caps->maxColorAttachments = QuerySingleGLInt(functions, GL_MAX_COLOR_ATTACHMENTS); + } + else + { + // Can't support ES2 without framebuffers and renderbuffers + LimitVersion(maxSupportedESVersion, gl::Version(0, 0)); + } + + if (functions->isAtLeastGL(gl::Version(2, 0)) || functions->hasGLExtension("ARB_draw_buffers") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_draw_buffers")) + { + caps->maxDrawBuffers = QuerySingleGLInt(functions, GL_MAX_DRAW_BUFFERS); + } + else + { + // Framebuffer is required to have at least one drawbuffer even if the extension is not + // supported + caps->maxDrawBuffers = 1; + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + caps->maxViewportWidth = QueryGLIntRange(functions, GL_MAX_VIEWPORT_DIMS, 0); // GL 1.0 / ES 2.0 + caps->maxViewportHeight = QueryGLIntRange(functions, GL_MAX_VIEWPORT_DIMS, 1); // GL 1.0 / ES 2.0 + + if (functions->standard == STANDARD_GL_DESKTOP && + (functions->profile & GL_CONTEXT_CORE_PROFILE_BIT) != 0) + { + // Desktop GL core profile deprecated the GL_ALIASED_POINT_SIZE_RANGE query. Use + // GL_POINT_SIZE_RANGE instead. + caps->minAliasedPointSize = QueryGLFloatRange(functions, GL_POINT_SIZE_RANGE, 0); + caps->maxAliasedPointSize = QueryGLFloatRange(functions, GL_POINT_SIZE_RANGE, 1); + } + else + { + caps->minAliasedPointSize = QueryGLFloatRange(functions, GL_ALIASED_POINT_SIZE_RANGE, 0); + caps->maxAliasedPointSize = QueryGLFloatRange(functions, GL_ALIASED_POINT_SIZE_RANGE, 1); + } + + caps->minAliasedLineWidth = QueryGLFloatRange(functions, GL_ALIASED_LINE_WIDTH_RANGE, 0); // GL 1.2 / ES 2.0 + caps->maxAliasedLineWidth = QueryGLFloatRange(functions, GL_ALIASED_LINE_WIDTH_RANGE, 1); // GL 1.2 / ES 2.0 + + // Table 6.29, implementation dependent values (cont.) + if (functions->isAtLeastGL(gl::Version(1, 2)) || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxElementsIndices = QuerySingleGLInt(functions, GL_MAX_ELEMENTS_INDICES); + caps->maxElementsVertices = QuerySingleGLInt(functions, GL_MAX_ELEMENTS_VERTICES); + } + else + { + // Doesn't impact supported version + } + + if (functions->isAtLeastGL(gl::Version(4, 1)) || + functions->hasGLExtension("GL_ARB_get_program_binary") || + functions->isAtLeastGLES(gl::Version(3, 0)) || + functions->hasGLExtension("GL_OES_get_program_binary")) + { + // Able to support the GL_PROGRAM_BINARY_ANGLE format as long as another program binary + // format is available. + GLint numBinaryFormats = QuerySingleGLInt(functions, GL_NUM_PROGRAM_BINARY_FORMATS_OES); + if (numBinaryFormats > 0) + { + caps->programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE); + } + } + else + { + // Doesn't impact supported version + } + + // glGetShaderPrecisionFormat is not available until desktop GL version 4.1 or GL_ARB_ES2_compatibility exists + if (functions->isAtLeastGL(gl::Version(4, 1)) || functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->vertexHighpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_HIGH_FLOAT); + caps->vertexMediumpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_MEDIUM_FLOAT); + caps->vertexLowpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_LOW_FLOAT); + caps->fragmentHighpFloat = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_HIGH_FLOAT); + caps->fragmentMediumpFloat = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT); + caps->fragmentLowpFloat = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_LOW_FLOAT); + caps->vertexHighpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_HIGH_INT); + caps->vertexMediumpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_MEDIUM_INT); + caps->vertexLowpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_LOW_INT); + caps->fragmentHighpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_HIGH_INT); + caps->fragmentMediumpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_MEDIUM_INT); + caps->fragmentLowpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_LOW_INT); + } + else + { + // Doesn't impact supported version, set some default values + caps->vertexHighpFloat.setIEEEFloat(); + caps->vertexMediumpFloat.setIEEEFloat(); + caps->vertexLowpFloat.setIEEEFloat(); + caps->fragmentHighpFloat.setIEEEFloat(); + caps->fragmentMediumpFloat.setIEEEFloat(); + caps->fragmentLowpFloat.setIEEEFloat(); + caps->vertexHighpInt.setTwosComplementInt(32); + caps->vertexMediumpInt.setTwosComplementInt(32); + caps->vertexLowpInt.setTwosComplementInt(32); + caps->fragmentHighpInt.setTwosComplementInt(32); + caps->fragmentMediumpInt.setTwosComplementInt(32); + caps->fragmentLowpInt.setTwosComplementInt(32); + } + + if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->hasGLExtension("GL_ARB_sync") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxServerWaitTimeout = QuerySingleGLInt64(functions, GL_MAX_SERVER_WAIT_TIMEOUT); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Table 6.31, implementation dependent vertex shader limits + if (functions->isAtLeastGL(gl::Version(2, 0)) || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxVertexAttributes = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIBS); + caps->maxVertexUniformComponents = QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_COMPONENTS); + caps->maxVertexTextureImageUnits = QuerySingleGLInt(functions, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS); + } + else + { + // Can't support ES2 version without these caps + LimitVersion(maxSupportedESVersion, gl::Version(0, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 1)) || functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxVertexUniformVectors = QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_VECTORS); + } + else + { + // Doesn't limit ES version, GL_MAX_VERTEX_UNIFORM_COMPONENTS / 4 is acceptable. + caps->maxVertexUniformVectors = caps->maxVertexUniformComponents / 4; + } + + if (functions->isAtLeastGL(gl::Version(3, 1)) || functions->hasGLExtension("GL_ARB_uniform_buffer_object") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxVertexUniformBlocks = QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_BLOCKS); + } + else + { + // Can't support ES3 without uniform blocks + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 2)) || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxVertexOutputComponents = QuerySingleGLInt(functions, GL_MAX_VERTEX_OUTPUT_COMPONENTS); + } + else + { + // There doesn't seem, to be a desktop extension to add this cap, maybe it could be given a safe limit + // instead of limiting the supported ES version. + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Table 6.32, implementation dependent fragment shader limits + if (functions->isAtLeastGL(gl::Version(2, 0)) || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxFragmentUniformComponents = QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_COMPONENTS); + caps->maxTextureImageUnits = QuerySingleGLInt(functions, GL_MAX_TEXTURE_IMAGE_UNITS); + } + else + { + // Can't support ES2 version without these caps + LimitVersion(maxSupportedESVersion, gl::Version(0, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 1)) || functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxFragmentUniformVectors = QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_VECTORS); + } + else + { + // Doesn't limit ES version, GL_MAX_FRAGMENT_UNIFORM_COMPONENTS / 4 is acceptable. + caps->maxFragmentUniformVectors = caps->maxFragmentUniformComponents / 4; + } + + if (functions->isAtLeastGL(gl::Version(3, 1)) || functions->hasGLExtension("GL_ARB_uniform_buffer_object") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxFragmentUniformBlocks = QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_BLOCKS); + } + else + { + // Can't support ES3 without uniform blocks + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 2)) || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxFragmentInputComponents = QuerySingleGLInt(functions, GL_MAX_FRAGMENT_INPUT_COMPONENTS); + } + else + { + // There doesn't seem, to be a desktop extension to add this cap, maybe it could be given a safe limit + // instead of limiting the supported ES version. + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 0)) || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->minProgramTexelOffset = QuerySingleGLInt(functions, GL_MIN_PROGRAM_TEXEL_OFFSET); + caps->maxProgramTexelOffset = QuerySingleGLInt(functions, GL_MAX_PROGRAM_TEXEL_OFFSET); + } + else + { + // Can't support ES3 without texel offset, could possibly be emulated in the shader + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Table 6.33, implementation dependent aggregate shader limits + if (functions->isAtLeastGL(gl::Version(3, 1)) || functions->hasGLExtension("GL_ARB_uniform_buffer_object") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxUniformBufferBindings = QuerySingleGLInt(functions, GL_MAX_UNIFORM_BUFFER_BINDINGS); + caps->maxUniformBlockSize = QuerySingleGLInt64(functions, GL_MAX_UNIFORM_BLOCK_SIZE); + caps->uniformBufferOffsetAlignment = QuerySingleGLInt(functions, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); + caps->maxCombinedUniformBlocks = caps->maxVertexUniformBlocks + caps->maxFragmentInputComponents; + caps->maxCombinedVertexUniformComponents = QuerySingleGLInt64(functions, GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); + caps->maxCombinedFragmentUniformComponents = QuerySingleGLInt64(functions, GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); + } + else + { + // Can't support ES3 without uniform blocks + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 0)) || + functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VARYING_COMPONENTS); + } + else if (functions->isAtLeastGL(gl::Version(2, 0))) + { + caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VARYING_FLOATS); + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(0, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 1)) || functions->hasGLExtension("GL_ARB_ES2_compatibility") || + functions->isAtLeastGLES(gl::Version(2, 0))) + { + caps->maxVaryingVectors = QuerySingleGLInt(functions, GL_MAX_VARYING_VECTORS); + } + else + { + // Doesn't limit ES version, GL_MAX_VARYING_COMPONENTS / 4 is acceptable. + caps->maxVaryingVectors = caps->maxVaryingComponents / 4; + } + + // Determine the max combined texture image units by adding the vertex and fragment limits. If + // the real cap is queried, it would contain the limits for shader types that are not available to ES. + caps->maxCombinedTextureImageUnits = caps->maxVertexTextureImageUnits + caps->maxTextureImageUnits; + + // Table 6.34, implementation dependent transform feedback limits + if (functions->isAtLeastGL(gl::Version(4, 0)) || + functions->hasGLExtension("GL_ARB_transform_feedback2") || + functions->isAtLeastGLES(gl::Version(3, 0))) + { + caps->maxTransformFeedbackInterleavedComponents = QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); + caps->maxTransformFeedbackSeparateAttributes = QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); + caps->maxTransformFeedbackSeparateComponents = QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); + } + else + { + // Can't support ES3 without transform feedback + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Table 6.35, Framebuffer Dependent Values + if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_EXT_framebuffer_multisample") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_multisampled_render_to_texture")) + { + caps->maxSamples = QuerySingleGLInt(functions, GL_MAX_SAMPLES); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Check if index constant sampler array indexing is supported + if (!functions->isAtLeastGL(gl::Version(4, 0)) && + !functions->isAtLeastGLES(gl::Version(2, 0)) && + !functions->hasExtension("GL_ARB_gpu_shader5")) + { + // This should also be required for ES2 but there are some driver support index constant + // sampler array indexing without meeting the requirements above. Don't limit their ES + // version as it would break WebGL for some users. + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Check if sampler objects are supported + if (!functions->isAtLeastGL(gl::Version(3, 3)) && + !functions->hasGLExtension("GL_ARB_sampler_objects") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + // Can't support ES3 without sampler objects + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Can't support ES3 without texture swizzling + if (!functions->isAtLeastGL(gl::Version(3, 3)) && + !functions->hasGLExtension("GL_ARB_texture_swizzle") && + !functions->hasGLExtension("GL_EXT_texture_swizzle") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + + // Texture swizzling is required to work around the luminance texture format not being + // present in the core profile + if (functions->profile & GL_CONTEXT_CORE_PROFILE_BIT) + { + LimitVersion(maxSupportedESVersion, gl::Version(0, 0)); + } + } + + // Can't support ES3 without the GLSL packing builtins. We have a workaround for all + // desktop OpenGL versions starting from 3.3 with the bit packing extension. + if (!functions->isAtLeastGL(gl::Version(4, 2)) && + !(functions->isAtLeastGL(gl::Version(3, 2)) && + functions->hasGLExtension("GL_ARB_shader_bit_encoding")) && + !functions->hasGLExtension("GL_ARB_shading_language_packing") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // ES3 needs to support explicit layout location qualifiers, while it might be possible to + // fake them in our side, we currently don't support that. + if (!functions->isAtLeastGL(gl::Version(3, 3)) && + !functions->hasGLExtension("GL_ARB_explicit_attrib_location") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_texture_multisample")) + { + caps->maxFramebufferWidth = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_WIDTH); + caps->maxFramebufferHeight = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_HEIGHT); + caps->maxFramebufferSamples = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_SAMPLES); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_texture_multisample")) + { + caps->maxSampleMaskWords = QuerySingleGLInt(functions, GL_MAX_SAMPLE_MASK_WORDS); + caps->maxColorTextureSamples = QuerySingleGLInt(functions, GL_MAX_COLOR_TEXTURE_SAMPLES); + caps->maxDepthTextureSamples = QuerySingleGLInt(functions, GL_MAX_DEPTH_TEXTURE_SAMPLES); + caps->maxIntegerSamples = QuerySingleGLInt(functions, GL_MAX_INTEGER_SAMPLES); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_vertex_attrib_binding")) + { + caps->maxVertexAttribRelativeOffset = + QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET); + caps->maxVertexAttribBindings = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_BINDINGS); + caps->maxVertexAttribStride = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_STRIDE); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_shader_storage_buffer_object")) + { + caps->maxCombinedShaderOutputResources = + QuerySingleGLInt(functions, GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES); + caps->maxFragmentShaderStorageBlocks = + QuerySingleGLInt(functions, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS); + caps->maxVertexShaderStorageBlocks = + QuerySingleGLInt(functions, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS); + caps->maxShaderStorageBufferBindings = + QuerySingleGLInt(functions, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + caps->maxShaderStorageBlockSize = + QuerySingleGLInt64(functions, GL_MAX_SHADER_STORAGE_BLOCK_SIZE); + caps->maxCombinedShaderStorageBlocks = + QuerySingleGLInt(functions, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS); + caps->shaderStorageBufferOffsetAlignment = + QuerySingleGLInt(functions, GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_compute_shader")) + { + for (GLuint index = 0u; index < 3u; ++index) + { + caps->maxComputeWorkGroupCount[index] = + QuerySingleIndexGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_COUNT, index); + + caps->maxComputeWorkGroupSize[index] = + QuerySingleIndexGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_SIZE, index); + } + caps->maxComputeWorkGroupInvocations = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS); + caps->maxComputeUniformBlocks = QuerySingleGLInt(functions, GL_MAX_COMPUTE_UNIFORM_BLOCKS); + caps->maxComputeTextureImageUnits = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS); + caps->maxComputeSharedMemorySize = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_SHARED_MEMORY_SIZE); + caps->maxComputeUniformComponents = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_UNIFORM_COMPONENTS); + caps->maxComputeAtomicCounterBuffers = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS); + caps->maxComputeAtomicCounters = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_ATOMIC_COUNTERS); + caps->maxComputeImageUniforms = QuerySingleGLInt(functions, GL_MAX_COMPUTE_IMAGE_UNIFORMS); + caps->maxCombinedComputeUniformComponents = + QuerySingleGLInt(functions, GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS); + caps->maxComputeShaderStorageBlocks = + QuerySingleGLInt(functions, GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_explicit_uniform_location")) + { + caps->maxUniformLocations = QuerySingleGLInt(functions, GL_MAX_UNIFORM_LOCATIONS); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 0)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_texture_gather")) + { + caps->minProgramTextureGatherOffset = + QuerySingleGLInt(functions, GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET); + caps->maxProgramTextureGatherOffset = + QuerySingleGLInt(functions, GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_shader_image_load_store")) + { + caps->maxVertexImageUniforms = QuerySingleGLInt(functions, GL_MAX_VERTEX_IMAGE_UNIFORMS); + caps->maxFragmentImageUniforms = + QuerySingleGLInt(functions, GL_MAX_FRAGMENT_IMAGE_UNIFORMS); + caps->maxImageUnits = QuerySingleGLInt(functions, GL_MAX_IMAGE_UNITS); + caps->maxCombinedImageUniforms = + QuerySingleGLInt(functions, GL_MAX_COMBINED_IMAGE_UNIFORMS); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + if (functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) || + functions->hasGLExtension("GL_ARB_shader_atomic_counters")) + { + caps->maxVertexAtomicCounterBuffers = + QuerySingleGLInt(functions, GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS); + caps->maxVertexAtomicCounters = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATOMIC_COUNTERS); + caps->maxFragmentAtomicCounterBuffers = + QuerySingleGLInt(functions, GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS); + caps->maxFragmentAtomicCounters = + QuerySingleGLInt(functions, GL_MAX_FRAGMENT_ATOMIC_COUNTERS); + caps->maxAtomicCounterBufferBindings = + QuerySingleGLInt(functions, GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS); + caps->maxAtomicCounterBufferSize = + QuerySingleGLInt(functions, GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE); + caps->maxCombinedAtomicCounterBuffers = + QuerySingleGLInt(functions, GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS); + caps->maxCombinedAtomicCounters = + QuerySingleGLInt(functions, GL_MAX_COMBINED_ATOMIC_COUNTERS); + } + else + { + LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); + } + + // TODO(geofflang): The gl-uniform-arrays WebGL conformance test struggles to complete on time + // if the max uniform vectors is too large. Artificially limit the maximum until the test is + // updated. + caps->maxVertexUniformVectors = std::min(1024u, caps->maxVertexUniformVectors); + caps->maxVertexUniformComponents = + std::min(caps->maxVertexUniformVectors * 4, caps->maxVertexUniformComponents); + caps->maxFragmentUniformVectors = std::min(1024u, caps->maxFragmentUniformVectors); + caps->maxFragmentUniformComponents = + std::min(caps->maxFragmentUniformVectors * 4, caps->maxFragmentUniformComponents); + + // If it is not possible to support reading buffer data back, a shadow copy of the buffers must + // be held. This disallows writing to buffers indirectly through transform feedback, thus + // disallowing ES3. + if (!CanMapBufferForRead(functions)) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // Extension support + extensions->setTextureExtensionSupport(*textureCapsMap); + extensions->elementIndexUint = functions->standard == STANDARD_GL_DESKTOP || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_element_index_uint"); + extensions->getProgramBinary = caps->programBinaryFormats.size() > 0; + extensions->readFormatBGRA = functions->isAtLeastGL(gl::Version(1, 2)) || functions->hasGLExtension("GL_EXT_bgra") || + functions->hasGLESExtension("GL_EXT_read_format_bgra"); + extensions->mapBuffer = functions->isAtLeastGL(gl::Version(1, 5)) || + functions->hasGLESExtension("GL_OES_mapbuffer"); + extensions->mapBufferRange = functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_ARB_map_buffer_range") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_map_buffer_range"); + extensions->textureNPOT = functions->standard == STANDARD_GL_DESKTOP || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_texture_npot"); + extensions->drawBuffers = functions->isAtLeastGL(gl::Version(2, 0)) || functions->hasGLExtension("ARB_draw_buffers") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_draw_buffers"); + extensions->textureStorage = true; + extensions->textureFilterAnisotropic = functions->hasGLExtension("GL_EXT_texture_filter_anisotropic") || functions->hasGLESExtension("GL_EXT_texture_filter_anisotropic"); + extensions->occlusionQueryBoolean = + functions->isAtLeastGL(gl::Version(1, 5)) || + functions->hasGLExtension("GL_ARB_occlusion_query2") || + functions->isAtLeastGLES(gl::Version(3, 0)) || + functions->hasGLESExtension("GL_EXT_occlusion_query_boolean"); + extensions->maxTextureAnisotropy = extensions->textureFilterAnisotropic ? QuerySingleGLFloat(functions, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0.0f; + extensions->fence = functions->hasGLExtension("GL_NV_fence") || functions->hasGLESExtension("GL_NV_fence"); + extensions->blendMinMax = functions->isAtLeastGL(gl::Version(1, 5)) || functions->hasGLExtension("GL_EXT_blend_minmax") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_blend_minmax"); + extensions->framebufferBlit = (functions->blitFramebuffer != nullptr); + extensions->framebufferMultisample = caps->maxSamples > 0; + extensions->standardDerivatives = functions->isAtLeastGL(gl::Version(2, 0)) || functions->hasGLExtension("GL_ARB_fragment_shader") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_standard_derivatives"); + extensions->shaderTextureLOD = functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_ARB_shader_texture_lod") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_shader_texture_lod"); + extensions->fragDepth = functions->standard == STANDARD_GL_DESKTOP || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_frag_depth"); + extensions->fboRenderMipmap = functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_EXT_framebuffer_object") || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_fbo_render_mipmap"); + extensions->instancedArrays = functions->isAtLeastGL(gl::Version(3, 1)) || + (functions->hasGLExtension("GL_ARB_instanced_arrays") && + (functions->hasGLExtension("GL_ARB_draw_instanced") || + functions->hasGLExtension("GL_EXT_draw_instanced"))) || + functions->isAtLeastGLES(gl::Version(3, 0)) || + functions->hasGLESExtension("GL_EXT_instanced_arrays"); + extensions->unpackSubimage = functions->standard == STANDARD_GL_DESKTOP || + functions->isAtLeastGLES(gl::Version(3, 0)) || + functions->hasGLESExtension("GL_EXT_unpack_subimage"); + extensions->packSubimage = functions->standard == STANDARD_GL_DESKTOP || + functions->isAtLeastGLES(gl::Version(3, 0)) || + functions->hasGLESExtension("GL_NV_pack_subimage"); + extensions->debugMarker = + functions->isAtLeastGL(gl::Version(4, 3)) || functions->hasGLExtension("GL_KHR_debug") || + functions->isAtLeastGLES(gl::Version(3, 2)) || functions->hasGLESExtension("GL_KHR_debug"); + if (functions->isAtLeastGL(gl::Version(3, 3)) || + functions->hasGLExtension("GL_ARB_timer_query") || + functions->hasGLESExtension("GL_EXT_disjoint_timer_query")) + { + extensions->disjointTimerQuery = true; + extensions->queryCounterBitsTimeElapsed = + QueryQueryValue(functions, GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS); + extensions->queryCounterBitsTimestamp = + QueryQueryValue(functions, GL_TIMESTAMP, GL_QUERY_COUNTER_BITS); + } + + // the EXT_multisample_compatibility is written against ES3.1 but can apply + // to earlier versions so therefore we're only checking for the extension string + // and not the specific GLES version. + extensions->multisampleCompatibility = functions->isAtLeastGL(gl::Version(1, 3)) || + functions->hasGLESExtension("GL_EXT_multisample_compatibility"); + + extensions->framebufferMixedSamples = + functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") || + functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples"); + + extensions->robustness = functions->isAtLeastGL(gl::Version(4, 5)) || + functions->hasGLExtension("GL_KHR_robustness") || + functions->hasGLExtension("GL_ARB_robustness") || + functions->isAtLeastGLES(gl::Version(3, 2)) || + functions->hasGLESExtension("GL_KHR_robustness") || + functions->hasGLESExtension("GL_EXT_robustness"); + + // NV_path_rendering + // We also need interface query which is available in + // >= 4.3 core or ARB_interface_query or >= GLES 3.1 + const bool canEnableGLPathRendering = + functions->hasGLExtension("GL_NV_path_rendering") && + (functions->hasGLExtension("GL_ARB_program_interface_query") || + functions->isAtLeastGL(gl::Version(4, 3))); + + const bool canEnableESPathRendering = + functions->hasGLESExtension("GL_NV_path_rendering") && + functions->isAtLeastGLES(gl::Version(3, 1)); + + extensions->pathRendering = canEnableGLPathRendering || canEnableESPathRendering; +} + +void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds) +{ + VendorID vendor = GetVendorID(functions); + + // Don't use 1-bit alpha formats on desktop GL with AMD or Intel drivers. + workarounds->avoid1BitAlphaTextureFormats = + functions->standard == STANDARD_GL_DESKTOP && + (vendor == VENDOR_ID_AMD || vendor == VENDOR_ID_INTEL); + + workarounds->rgba4IsNotSupportedForColorRendering = + functions->standard == STANDARD_GL_DESKTOP && vendor == VENDOR_ID_INTEL; + + workarounds->emulateAbsIntFunction = vendor == VENDOR_ID_INTEL; + + workarounds->addAndTrueToLoopCondition = vendor == VENDOR_ID_INTEL; + + workarounds->emulateIsnanFloat = vendor == VENDOR_ID_INTEL; + + workarounds->doesSRGBClearsOnLinearFramebufferAttachments = + functions->standard == STANDARD_GL_DESKTOP && + (vendor == VENDOR_ID_INTEL || vendor == VENDOR_ID_AMD); + +#if defined(ANGLE_PLATFORM_APPLE) + workarounds->doWhileGLSLCausesGPUHang = true; + workarounds->useUnusedBlocksWithStandardOrSharedLayout = true; +#endif + + workarounds->finishDoesNotCauseQueriesToBeAvailable = + functions->standard == STANDARD_GL_DESKTOP && vendor == VENDOR_ID_NVIDIA; + + // TODO(cwallez): Disable this workaround for MacOSX versions 10.9 or later. + workarounds->alwaysCallUseProgramAfterLink = true; + + workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA; + workarounds->packOverlappingRowsSeparatelyPackBuffer = vendor == VENDOR_ID_NVIDIA; + + workarounds->initializeCurrentVertexAttributes = vendor == VENDOR_ID_NVIDIA; + +#if defined(ANGLE_PLATFORM_APPLE) + workarounds->unpackLastRowSeparatelyForPaddingInclusion = true; + workarounds->packLastRowSeparatelyForPaddingInclusion = true; +#else + workarounds->unpackLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA; + workarounds->packLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA; +#endif +} + +} + +bool CanMapBufferForRead(const FunctionsGL *functions) +{ + return (functions->mapBufferRange != nullptr) || + (functions->mapBuffer != nullptr && functions->standard == STANDARD_GL_DESKTOP); +} + +uint8_t *MapBufferRangeWithFallback(const FunctionsGL *functions, + GLenum target, + size_t offset, + size_t length, + GLbitfield access) +{ + if (functions->mapBufferRange != nullptr) + { + return reinterpret_cast<uint8_t *>( + functions->mapBufferRange(target, offset, length, access)); + } + else if (functions->mapBuffer != nullptr && + (functions->standard == STANDARD_GL_DESKTOP || access == GL_MAP_WRITE_BIT)) + { + // Only the read and write bits are supported + ASSERT((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) != 0); + + GLenum accessEnum = 0; + if (access == (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) + { + accessEnum = GL_READ_WRITE; + } + else if (access == GL_MAP_READ_BIT) + { + accessEnum = GL_READ_ONLY; + } + else if (access == GL_MAP_WRITE_BIT) + { + accessEnum = GL_WRITE_ONLY; + } + else + { + UNREACHABLE(); + return nullptr; + } + + return reinterpret_cast<uint8_t *>(functions->mapBuffer(target, accessEnum)) + offset; + } + else + { + // No options available + UNREACHABLE(); + return nullptr; + } +} + +gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Extents &size, + const gl::PixelStoreStateBase &state, + GLenum format, + GLenum type, + bool is3D, + const void *pixels) +{ + if (state.pixelBuffer.get() == nullptr) + { + return false; + } + + // We are using an pack or unpack buffer, compute what the driver thinks is going to be the + // last byte read or written. If it is past the end of the buffer, we will need to use the + // workaround otherwise the driver will generate INVALID_OPERATION and not do the operation. + CheckedNumeric<size_t> checkedEndByte; + CheckedNumeric<size_t> pixelBytes; + size_t rowPitch; + + const gl::InternalFormat &glFormat = + gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type)); + ANGLE_TRY_RESULT(glFormat.computePackUnpackEndByte(type, size, state, is3D), checkedEndByte); + ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, size.width, state.alignment, state.rowLength), + rowPitch); + pixelBytes = glFormat.computePixelBytes(type); + + checkedEndByte += reinterpret_cast<intptr_t>(pixels); + + // At this point checkedEndByte is the actual last byte read. + // The driver adds an extra row padding (if any), mimic it. + ANGLE_TRY_CHECKED_MATH(pixelBytes); + if (pixelBytes.ValueOrDie() * size.width < rowPitch) + { + checkedEndByte += rowPitch - pixelBytes * size.width; + } + + ANGLE_TRY_CHECKED_MATH(checkedEndByte); + + return checkedEndByte.ValueOrDie() > static_cast<size_t>(state.pixelBuffer->getSize()); +} +} |