Edit

kc3-lang/angle/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-09-13 16:04:20
    Hash : e84b0154
    Message : Update extension boolean names. This is in preparation for auto-gen, which uses a simple naming scheme. This fixes the bool names to be totally consistent with the extension names. Bug: angleproject:6379 Change-Id: Ia212449be04accb0e4f006b55b1813ab4481fa0b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3157417 Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: Cody Northrop <cnorthrop@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
  • //
    // Copyright 2018 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.
    //
    // vk_utils:
    //    Helper functions for the Vulkan Caps.
    //
    
    #include "libANGLE/renderer/vulkan/vk_caps_utils.h"
    
    #include <type_traits>
    
    #include "common/utilities.h"
    #include "libANGLE/Caps.h"
    #include "libANGLE/formatutils.h"
    #include "libANGLE/renderer/driver_utils.h"
    #include "libANGLE/renderer/vulkan/DisplayVk.h"
    #include "libANGLE/renderer/vulkan/RendererVk.h"
    #include "libANGLE/renderer/vulkan/vk_cache_utils.h"
    #include "vk_format_utils.h"
    
    namespace
    {
    constexpr unsigned int kComponentsPerVector = 4;
    }  // anonymous namespace
    
    namespace rx
    {
    
    namespace vk
    {
    namespace
    {
    // Checks to see if each format can be reinterpreted to an equivalent format in a different
    // colorspace. If all supported formats can be reinterpreted, it returns true. Formats which are not
    // supported at all are ignored and not counted as failures.
    bool FormatReinterpretationSupported(const std::vector<GLenum> &optionalSizedFormats,
                                         const RendererVk *rendererVk,
                                         bool checkLinearColorspace)
    {
        for (GLenum glFormat : optionalSizedFormats)
        {
            const gl::TextureCaps &baseCaps = rendererVk->getNativeTextureCaps().get(glFormat);
            if (baseCaps.texturable && baseCaps.filterable)
            {
                const Format &vkFormat = rendererVk->getFormat(glFormat);
                // For capability query, we use the renderable format since that is what we are capable
                // of when we fallback.
                angle::FormatID imageFormatID = vkFormat.getActualRenderableImageFormatID();
    
                angle::FormatID reinterpretedFormatID = checkLinearColorspace
                                                            ? ConvertToLinear(imageFormatID)
                                                            : ConvertToSRGB(imageFormatID);
    
                const Format &reinterpretedVkFormat = rendererVk->getFormat(reinterpretedFormatID);
    
                if (reinterpretedVkFormat.getActualRenderableImageFormatID() != reinterpretedFormatID)
                {
                    return false;
                }
    
                if (!rendererVk->haveSameFormatFeatureBits(imageFormatID, reinterpretedFormatID))
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    bool GetTextureSRGBDecodeSupport(const RendererVk *rendererVk)
    {
        static constexpr bool kLinearColorspace = true;
    
        // GL_SRGB and GL_SRGB_ALPHA unsized formats are also required by the spec, but the only valid
        // type for them is GL_UNSIGNED_BYTE, so they are fully included in the sized formats listed
        // here
        std::vector<GLenum> optionalSizedSRGBFormats = {
            GL_SRGB8,
            GL_SRGB8_ALPHA8_EXT,
            GL_COMPRESSED_SRGB_S3TC_DXT1_EXT,
            GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
            GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
            GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
        };
    
        if (!FormatReinterpretationSupported(optionalSizedSRGBFormats, rendererVk, kLinearColorspace))
        {
            return false;
        }
    
        return true;
    }
    
    bool GetTextureSRGBOverrideSupport(const RendererVk *rendererVk,
                                       const gl::Extensions &supportedExtensions)
    {
        static constexpr bool kNonLinearColorspace = false;
    
        // If the given linear format is supported, we also need to support its corresponding nonlinear
        // format. If the given linear format is NOT supported, we don't care about its corresponding
        // nonlinear format.
        std::vector<GLenum> optionalLinearFormats     = {GL_RGB8,
                                                     GL_RGBA8,
                                                     GL_COMPRESSED_RGB8_ETC2,
                                                     GL_COMPRESSED_RGBA8_ETC2_EAC,
                                                     GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
                                                     GL_COMPRESSED_RGBA_ASTC_4x4,
                                                     GL_COMPRESSED_RGBA_ASTC_5x4,
                                                     GL_COMPRESSED_RGBA_ASTC_5x5,
                                                     GL_COMPRESSED_RGBA_ASTC_6x5,
                                                     GL_COMPRESSED_RGBA_ASTC_6x6,
                                                     GL_COMPRESSED_RGBA_ASTC_8x5,
                                                     GL_COMPRESSED_RGBA_ASTC_8x6,
                                                     GL_COMPRESSED_RGBA_ASTC_8x8,
                                                     GL_COMPRESSED_RGBA_ASTC_10x5,
                                                     GL_COMPRESSED_RGBA_ASTC_10x6,
                                                     GL_COMPRESSED_RGBA_ASTC_10x8,
                                                     GL_COMPRESSED_RGBA_ASTC_10x10,
                                                     GL_COMPRESSED_RGBA_ASTC_12x10,
                                                     GL_COMPRESSED_RGBA_ASTC_12x12};
        std::vector<GLenum> optionalS3TCLinearFormats = {
            GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT};
        std::vector<GLenum> optionalR8LinearFormats   = {GL_R8};
        std::vector<GLenum> optionalRG8LinearFormats  = {GL_RG8};
        std::vector<GLenum> optionalBPTCLinearFormats = {GL_COMPRESSED_RGBA_BPTC_UNORM_EXT};
    
        if (!FormatReinterpretationSupported(optionalLinearFormats, rendererVk, kNonLinearColorspace))
        {
            return false;
        }
    
        if (supportedExtensions.textureCompressionS3tcSrgbEXT)
        {
            if (!FormatReinterpretationSupported(optionalS3TCLinearFormats, rendererVk,
                                                 kNonLinearColorspace))
            {
                return false;
            }
        }
    
        if (supportedExtensions.textureSRGBR8EXT)
        {
            if (!FormatReinterpretationSupported(optionalR8LinearFormats, rendererVk,
                                                 kNonLinearColorspace))
            {
                return false;
            }
        }
    
        if (supportedExtensions.textureSRGBRG8EXT)
        {
            if (!FormatReinterpretationSupported(optionalRG8LinearFormats, rendererVk,
                                                 kNonLinearColorspace))
            {
                return false;
            }
        }
    
        if (supportedExtensions.textureCompressionBptcEXT)
        {
            if (!FormatReinterpretationSupported(optionalBPTCLinearFormats, rendererVk,
                                                 kNonLinearColorspace))
            {
                return false;
            }
        }
    
        return true;
    }
    
    bool HasTexelBufferSupport(const RendererVk *rendererVk, GLenum formatGL)
    {
        const Format &formatVk = rendererVk->getFormat(formatGL);
    
        return rendererVk->hasBufferFormatFeatureBits(
            formatVk.getActualBufferFormat(false).id,
            VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT);
    }
    
    bool HasTextureBufferSupport(const RendererVk *rendererVk)
    {
        // The following formats don't have mandatory UNIFORM_TEXEL_BUFFER support in Vulkan.
        //
        //     VK_FORMAT_R32G32B32_UINT
        //     VK_FORMAT_R32G32B32_SINT
        //     VK_FORMAT_R32G32B32_SFLOAT
        //
        // Additionally, the following formats don't have mandatory STORAGE_TEXEL_BUFFER support:
        //
        //     VK_FORMAT_R8_UINT
        //     VK_FORMAT_R8_SINT
        //     VK_FORMAT_R8_UNORM
        //     VK_FORMAT_R8G8_UINT
        //     VK_FORMAT_R8G8_SINT
        //     VK_FORMAT_R8G8_UNORM
        //     VK_FORMAT_R16_UINT
        //     VK_FORMAT_R16_SINT
        //     VK_FORMAT_R16_SFLOAT
        //     VK_FORMAT_R16G16_UINT
        //     VK_FORMAT_R16G16_SINT
        //     VK_FORMAT_R16G16_SFLOAT
        //     VK_FORMAT_R32G32B32_UINT
        //     VK_FORMAT_R32G32B32_SINT
        //     VK_FORMAT_R32G32B32_SFLOAT
        //
        // The formats that have mandatory support for both features (and don't need to be checked) are:
        //
        //     VK_FORMAT_R8G8B8A8_UINT
        //     VK_FORMAT_R8G8B8A8_SINT
        //     VK_FORMAT_R8G8B8A8_UNORM
        //     VK_FORMAT_R16G16B16A16_UINT
        //     VK_FORMAT_R16G16B16A16_SINT
        //     VK_FORMAT_R16G16B16A16_SFLOAT
        //     VK_FORMAT_R32_UINT
        //     VK_FORMAT_R32_SINT
        //     VK_FORMAT_R32_SFLOAT
        //     VK_FORMAT_R32G32_UINT
        //     VK_FORMAT_R32G32_SINT
        //     VK_FORMAT_R32G32_SFLOAT
        //     VK_FORMAT_R32G32B32A32_UINT
        //     VK_FORMAT_R32G32B32A32_SINT
        //     VK_FORMAT_R32G32B32A32_SFLOAT
        //
    
        const std::array<GLenum, 12> &optionalFormats = {
            GL_R8,   GL_R8I,  GL_R8UI,  GL_RG8,   GL_RG8I,  GL_RG8UI,
            GL_R16F, GL_R16I, GL_R16UI, GL_RG16F, GL_RG16I, GL_RG16UI,
        };
    
        for (GLenum formatGL : optionalFormats)
        {
            if (!HasTexelBufferSupport(rendererVk, formatGL))
            {
                return false;
            }
        }
    
        // TODO: RGB32 formats currently don't have STORAGE_TEXEL_BUFFER support on any known platform.
        // Despite this limitation, we expose EXT_texture_buffer.  http://anglebug.com/3573
        if (rendererVk->getFeatures().exposeNonConformantExtensionsAndVersions.enabled)
        {
            return true;
        }
    
        const std::array<GLenum, 3> &optionalFormats2 = {
            GL_RGB32F,
            GL_RGB32I,
            GL_RGB32UI,
        };
    
        for (GLenum formatGL : optionalFormats2)
        {
            if (!HasTexelBufferSupport(rendererVk, formatGL))
            {
                return false;
            }
        }
    
        return true;
    }
    
    bool CanSupportYuvInternalFormat(const RendererVk *rendererVk)
    {
        // The following formats are not mandatory in Vulkan, even when VK_KHR_sampler_ycbcr_conversion
        // is supported. GL_ANGLE_yuv_internal_format requires support for sampling only the
        // 8-bit 2-plane YUV format (VK_FORMAT_G8_B8R8_2PLANE_420_UNORM), if the ICD supports that we
        // can expose the extension.
        //
        // Various test cases need multiple YUV formats. It would be preferrable to have support for the
        // 3 plane 8 bit YUV format (VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM) as well.
    
        const Format &twoPlane8bitYuvFormat = rendererVk->getFormat(GL_G8_B8R8_2PLANE_420_UNORM_ANGLE);
        bool twoPlane8bitYuvFormatSupported = rendererVk->hasImageFormatFeatureBits(
            twoPlane8bitYuvFormat.getActualImageFormatID(vk::ImageAccess::SampleOnly),
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
    
        const Format &threePlane8bitYuvFormat =
            rendererVk->getFormat(GL_G8_B8_R8_3PLANE_420_UNORM_ANGLE);
        bool threePlane8bitYuvFormatSupported = rendererVk->hasImageFormatFeatureBits(
            threePlane8bitYuvFormat.getActualImageFormatID(vk::ImageAccess::SampleOnly),
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
    
        return twoPlane8bitYuvFormatSupported && threePlane8bitYuvFormatSupported;
    }
    }  // namespace
    }  // namespace vk
    
    template <typename LargerInt>
    GLint LimitToInt(const LargerInt physicalDeviceValue)
    {
        static_assert(sizeof(LargerInt) >= sizeof(int32_t), "Incorrect usage of LimitToInt");
    
        // Limit to INT_MAX / 2 instead of INT_MAX.  If the limit is queried as float, the imprecision
        // in floating point can cause the value to exceed INT_MAX.  This trips dEQP up.
        return static_cast<GLint>(std::min(
            physicalDeviceValue, static_cast<LargerInt>(std::numeric_limits<int32_t>::max() / 2)));
    }
    
    void RendererVk::ensureCapsInitialized() const
    {
        if (mCapsInitialized)
            return;
        mCapsInitialized = true;
    
        ASSERT(mCurrentQueueFamilyIndex < mQueueFamilyProperties.size());
        const VkQueueFamilyProperties &queueFamilyProperties =
            mQueueFamilyProperties[mCurrentQueueFamilyIndex];
        const VkPhysicalDeviceLimits &limitsVk = mPhysicalDeviceProperties.limits;
    
        mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps);
    
        // Enable GL_EXT_buffer_storage
        mNativeExtensions.bufferStorageEXT = true;
    
        // When ETC2/EAC formats are natively supported, enable ANGLE-specific extension string to
        // expose them to WebGL. In other case, mark potentially-available ETC1 extension as emulated.
        if ((mPhysicalDeviceFeatures.textureCompressionETC2 == VK_TRUE) &&
            gl::DetermineCompressedTextureETCSupport(mNativeTextureCaps))
        {
            mNativeExtensions.compressedTextureEtcANGLE = true;
        }
        else
        {
            mNativeLimitations.emulatedEtc1 = true;
        }
    
        // Vulkan doesn't support ASTC 3D block textures, which are required by
        // GL_OES_texture_compression_astc.
        mNativeExtensions.textureCompressionAstcOES = false;
        // Vulkan does not support sliced 3D ASTC textures either.
        mNativeExtensions.textureCompressionAstcSliced3dKHR = false;
    
        // Vulkan doesn't guarantee HDR blocks decoding without VK_EXT_texture_compression_astc_hdr.
        mNativeExtensions.textureCompressionAstcHdrKHR = false;
    
        // Enable EXT_compressed_ETC1_RGB8_sub_texture
        mNativeExtensions.compressedETC1RGB8SubTextureEXT =
            mNativeExtensions.compressedETC1RGB8TextureOES;
    
        // Enable this for simple buffer readback testing, but some functionality is missing.
        // TODO(jmadill): Support full mapBufferRangeEXT extension.
        mNativeExtensions.mapbufferOES                = true;
        mNativeExtensions.mapBufferRangeEXT           = true;
        mNativeExtensions.textureStorageEXT           = true;
        mNativeExtensions.drawBuffersEXT              = true;
        mNativeExtensions.fragDepthEXT                = true;
        mNativeExtensions.framebufferBlitANGLE        = true;
        mNativeExtensions.framebufferBlitNV           = true;
        mNativeExtensions.framebufferMultisampleANGLE = true;
        mNativeExtensions.multisampledRenderToTextureEXT =
            getFeatures().enableMultisampledRenderToTexture.enabled;
        mNativeExtensions.multisampledRenderToTexture2EXT =
            getFeatures().enableMultisampledRenderToTexture.enabled;
        mNativeExtensions.textureStorageMultisample2dArrayOES =
            (limitsVk.standardSampleLocations == VK_TRUE);
        mNativeExtensions.copyTextureCHROMIUM           = true;
        mNativeExtensions.copyTexture3dANGLE            = true;
        mNativeExtensions.copyCompressedTextureCHROMIUM = true;
        mNativeExtensions.debugMarkerEXT                = true;
        mNativeExtensions.robustnessEXT =
            !IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) &&
            !IsARM(mPhysicalDeviceProperties.vendorID);
        mNativeExtensions.discardFramebufferEXT = true;
        mNativeExtensions.textureBorderClampOES = getFeatures().supportsCustomBorderColorEXT.enabled;
        mNativeExtensions.textureBorderClampEXT = getFeatures().supportsCustomBorderColorEXT.enabled;
        // Enable EXT_texture_type_2_10_10_10_REV
        mNativeExtensions.textureType2101010REVEXT = true;
    
        // Enable ANGLE_base_vertex_base_instance
        mNativeExtensions.baseVertexBaseInstanceANGLE = true;
    
        // Enable OES/EXT_draw_elements_base_vertex
        mNativeExtensions.drawElementsBaseVertexOES = true;
        mNativeExtensions.drawElementsBaseVertexEXT = true;
    
        // Enable EXT_blend_minmax
        mNativeExtensions.blendMinmaxEXT = true;
    
        // Enable OES/EXT_draw_buffers_indexed
        mNativeExtensions.drawBuffersIndexedOES = mPhysicalDeviceFeatures.independentBlend == VK_TRUE;
        mNativeExtensions.drawBuffersIndexedEXT = mNativeExtensions.drawBuffersIndexedOES;
    
        mNativeExtensions.EGLImageOES                  = true;
        mNativeExtensions.EGLImageExternalOES          = true;
        mNativeExtensions.EGLImageExternalWrapModesEXT = true;
        mNativeExtensions.EGLImageExternalEssl3OES     = true;
        mNativeExtensions.EGLImageArrayEXT             = true;
        mNativeExtensions.memoryObjectEXT              = true;
        mNativeExtensions.memoryObjectFdEXT            = getFeatures().supportsExternalMemoryFd.enabled;
        mNativeExtensions.memoryObjectFlagsANGLE       = true;
        mNativeExtensions.memoryObjectFuchsiaANGLE =
            getFeatures().supportsExternalMemoryFuchsia.enabled;
    
        mNativeExtensions.semaphoreEXT   = true;
        mNativeExtensions.semaphoreFdEXT = getFeatures().supportsExternalSemaphoreFd.enabled;
        mNativeExtensions.semaphoreFuchsiaANGLE =
            getFeatures().supportsExternalSemaphoreFuchsia.enabled;
    
        mNativeExtensions.vertexHalfFloatOES = true;
    
        // Enabled in HW if VK_EXT_vertex_attribute_divisor available, otherwise emulated
        mNativeExtensions.instancedArraysANGLE = true;
        mNativeExtensions.instancedArraysEXT   = true;
    
        // Only expose robust buffer access if the physical device supports it.
        mNativeExtensions.robustBufferAccessBehaviorKHR =
            (mPhysicalDeviceFeatures.robustBufferAccess == VK_TRUE);
    
        mNativeExtensions.EGLSyncOES = true;
    
        mNativeExtensions.vertexType1010102OES = true;
    
        // Occlusion queries are natively supported in Vulkan.  ANGLE only issues this query inside a
        // render pass, so there is no dependency to `inheritedQueries`.
        mNativeExtensions.occlusionQueryBooleanEXT = true;
    
        // From the Vulkan specs:
        // > The number of valid bits in a timestamp value is determined by the
        // > VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is
        // > written. Timestamps are supported on any queue which reports a non-zero value for
        // > timestampValidBits via vkGetPhysicalDeviceQueueFamilyProperties.
        //
        // This query is applicable to render passes, but the `inheritedQueries` feature may not be
        // present.  The extension is not exposed in that case.
        // We use secondary command buffers almost everywhere and they require a feature to be
        // able to execute in the presence of queries.  As a result, we won't support queries
        // unless that feature is available.
        if (vk::CommandBuffer::SupportsQueries(mPhysicalDeviceFeatures))
        {
            mNativeExtensions.disjointTimerQueryEXT = queueFamilyProperties.timestampValidBits > 0;
            mNativeCaps.queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
            mNativeCaps.queryCounterBitsTimestamp   = queueFamilyProperties.timestampValidBits;
        }
    
        mNativeExtensions.textureFilterAnisotropicEXT =
            mPhysicalDeviceFeatures.samplerAnisotropy && limitsVk.maxSamplerAnisotropy > 1.0f;
        mNativeCaps.maxTextureAnisotropy =
            mNativeExtensions.textureFilterAnisotropicEXT ? limitsVk.maxSamplerAnisotropy : 0.0f;
    
        // Vulkan natively supports non power-of-two textures
        mNativeExtensions.textureNpotOES = true;
    
        mNativeExtensions.texture3DOES = true;
    
        // Vulkan natively supports standard derivatives
        mNativeExtensions.standardDerivativesOES = true;
    
        // Vulkan natively supports texture LOD
        mNativeExtensions.shaderTextureLodEXT = true;
    
        // Vulkan natively supports noperspective interpolation
        mNativeExtensions.shaderNoperspectiveInterpolationNV = true;
    
        // Vulkan natively supports 32-bit indices, entry in kIndexTypeMap
        mNativeExtensions.elementIndexUintOES = true;
    
        mNativeExtensions.fboRenderMipmapOES = true;
    
        // We support getting image data for Textures and Renderbuffers.
        mNativeExtensions.getImageANGLE = true;
    
        // Implemented in the translator
        mNativeExtensions.shaderNonConstantGlobalInitializersEXT = true;
    
        // Implemented in the front end
        mNativeExtensions.separateShaderObjectsEXT = true;
    
        // Vulkan has no restrictions of the format of cubemaps, so if the proper formats are supported,
        // creating a cube of any of these formats should be implicitly supported.
        mNativeExtensions.depthTextureCubeMapOES =
            mNativeExtensions.depthTextureOES && mNativeExtensions.packedDepthStencilOES;
    
        // Vulkan natively supports format reinterpretation, but we still require support for all
        // formats we may reinterpret to
        mNativeExtensions.textureFormatSRGBOverrideEXT =
            vk::GetTextureSRGBOverrideSupport(this, mNativeExtensions);
        mNativeExtensions.textureSRGBDecodeEXT = vk::GetTextureSRGBDecodeSupport(this);
    
        // EXT_srgb_write_control requires image_format_list
        mNativeExtensions.sRGBWriteControlEXT = getFeatures().supportsImageFormatList.enabled;
    
        // Vulkan natively supports io interface block.
        mNativeExtensions.shaderIoBlocksOES = true;
        mNativeExtensions.shaderIoBlocksEXT = true;
    
        mNativeExtensions.gpuShader5EXT = vk::CanSupportGPUShader5EXT(mPhysicalDeviceFeatures);
    
        mNativeExtensions.textureFilteringHintCHROMIUM =
            getFeatures().supportsFilteringPrecision.enabled;
    
        // Only expose texture cubemap array if the physical device supports it.
        mNativeExtensions.textureCubeMapArrayOES = getFeatures().supportsImageCubeArray.enabled;
        mNativeExtensions.textureCubeMapArrayEXT = mNativeExtensions.textureCubeMapArrayOES;
    
        mNativeExtensions.shadowSamplersEXT = true;
    
        // Enable EXT_external_buffer on Andoid. External buffers are implemented using Android hadware
        // buffer (struct AHardwareBuffer).
        mNativeExtensions.externalBufferEXT = IsAndroid() && GetAndroidSDKVersion() >= 26;
    
        // From the Vulkan specs:
        // sampleRateShading specifies whether Sample Shading and multisample interpolation are
        // supported. If this feature is not enabled, the sampleShadingEnable member of the
        // VkPipelineMultisampleStateCreateInfo structure must be set to VK_FALSE and the
        // minSampleShading member is ignored. This also specifies whether shader modules can declare
        // the SampleRateShading capability
        bool supportSampleRateShading      = mPhysicalDeviceFeatures.sampleRateShading == VK_TRUE;
        mNativeExtensions.sampleShadingOES = supportSampleRateShading;
    
        // From the SPIR-V spec at 3.21. BuiltIn, SampleId and SamplePosition needs
        // SampleRateShading. https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html
        // To replace non-constant index to constant 0 index, this extension assumes that ANGLE only
        // supports the number of samples less than or equal to 32.
        constexpr unsigned int kNotSupportedSampleCounts = VK_SAMPLE_COUNT_64_BIT;
        mNativeExtensions.sampleVariablesOES =
            supportSampleRateShading && vk_gl::GetMaxSampleCount(kNotSupportedSampleCounts) == 0;
    
        // Enable EXT_unpack_subimage
        mNativeExtensions.unpackSubimageEXT = true;
    
        // Enable NV_pack_subimage
        mNativeExtensions.packSubimageNV = true;
    
        mNativeCaps.minInterpolationOffset          = limitsVk.minInterpolationOffset;
        mNativeCaps.maxInterpolationOffset          = limitsVk.maxInterpolationOffset;
        mNativeCaps.subPixelInterpolationOffsetBits = limitsVk.subPixelInterpolationOffsetBits;
    
        // From the Vulkan spec:
        //
        // > The values minInterpolationOffset and maxInterpolationOffset describe the closed interval
        // > of supported interpolation offsets : [ minInterpolationOffset, maxInterpolationOffset ].
        // > The ULP is determined by subPixelInterpolationOffsetBits. If
        // > subPixelInterpolationOffsetBits is 4, this provides increments of(1 / 2^4) = 0.0625, and
        // > thus the range of supported interpolation offsets would be[-0.5, 0.4375]
        //
        // OES_shader_multisample_interpolation requires a maximum value of -0.5 for
        // MIN_FRAGMENT_INTERPOLATION_OFFSET_OES and minimum 0.5 for
        // MAX_FRAGMENT_INTERPOLATION_OFFSET_OES.  Vulkan has an identical limit for
        // minInterpolationOffset, but its limit for maxInterpolationOffset is 0.5-(1/ULP).
        // OES_shader_multisample_interpolation is therefore only supported if
        // maxInterpolationOffset is at least 0.5.
        //
        // It's suspected that the GL spec is not as precise as Vulkan's in this regard and that the
        // requirements really meant to match.  The extension is exposed as non-conformant either way to
        // increase testing coverage).  Discussion with the GL working group ongoing at
        // https://gitlab.khronos.org/opengl/API/-/issues/149
        mNativeExtensions.shaderMultisampleInterpolationOES =
            mNativeExtensions.sampleVariablesOES &&
            (mNativeCaps.maxInterpolationOffset >= 0.5 ||
             mFeatures.exposeNonConformantExtensionsAndVersions.enabled);
    
        // https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
        mNativeCaps.maxElementIndex  = std::numeric_limits<GLuint>::max() - 1;
        mNativeCaps.max3DTextureSize = LimitToInt(limitsVk.maxImageDimension3D);
        mNativeCaps.max2DTextureSize =
            std::min(limitsVk.maxFramebufferWidth, limitsVk.maxImageDimension2D);
        mNativeCaps.maxArrayTextureLayers = LimitToInt(limitsVk.maxImageArrayLayers);
        mNativeCaps.maxLODBias            = limitsVk.maxSamplerLodBias;
        mNativeCaps.maxCubeMapTextureSize = LimitToInt(limitsVk.maxImageDimensionCube);
        mNativeCaps.maxRenderbufferSize =
            std::min({limitsVk.maxImageDimension2D, limitsVk.maxFramebufferWidth,
                      limitsVk.maxFramebufferHeight});
        mNativeCaps.minAliasedPointSize = std::max(1.0f, limitsVk.pointSizeRange[0]);
        mNativeCaps.maxAliasedPointSize = limitsVk.pointSizeRange[1];
    
        mNativeCaps.minAliasedLineWidth = 1.0f;
        mNativeCaps.maxAliasedLineWidth = 1.0f;
    
        mNativeCaps.maxDrawBuffers =
            std::min(limitsVk.maxColorAttachments, limitsVk.maxFragmentOutputAttachments);
        mNativeCaps.maxFramebufferWidth  = LimitToInt(limitsVk.maxFramebufferWidth);
        mNativeCaps.maxFramebufferHeight = LimitToInt(limitsVk.maxFramebufferHeight);
        mNativeCaps.maxColorAttachments  = LimitToInt(limitsVk.maxColorAttachments);
        mNativeCaps.maxViewportWidth     = LimitToInt(limitsVk.maxViewportDimensions[0]);
        mNativeCaps.maxViewportHeight    = LimitToInt(limitsVk.maxViewportDimensions[1]);
        mNativeCaps.maxSampleMaskWords   = LimitToInt(limitsVk.maxSampleMaskWords);
        mNativeCaps.maxColorTextureSamples =
            limitsVk.sampledImageColorSampleCounts & vk_gl::kSupportedSampleCounts;
        mNativeCaps.maxDepthTextureSamples =
            limitsVk.sampledImageDepthSampleCounts & vk_gl::kSupportedSampleCounts;
        mNativeCaps.maxIntegerSamples =
            limitsVk.sampledImageIntegerSampleCounts & vk_gl::kSupportedSampleCounts;
    
        mNativeCaps.maxVertexAttributes     = LimitToInt(limitsVk.maxVertexInputAttributes);
        mNativeCaps.maxVertexAttribBindings = LimitToInt(limitsVk.maxVertexInputBindings);
        // Offset and stride are stored as uint16_t in PackedAttribDesc.
        mNativeCaps.maxVertexAttribRelativeOffset =
            std::min((1u << kAttributeOffsetMaxBits) - 1, limitsVk.maxVertexInputAttributeOffset);
        mNativeCaps.maxVertexAttribStride =
            std::min(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()),
                     limitsVk.maxVertexInputBindingStride);
    
        mNativeCaps.maxElementsIndices  = std::numeric_limits<GLint>::max();
        mNativeCaps.maxElementsVertices = std::numeric_limits<GLint>::max();
    
        // Looks like all floats are IEEE according to the docs here:
        // https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#spirvenv-precision-operation
        mNativeCaps.vertexHighpFloat.setIEEEFloat();
        mNativeCaps.vertexMediumpFloat.setIEEEFloat();
        mNativeCaps.vertexLowpFloat.setIEEEFloat();
        mNativeCaps.fragmentHighpFloat.setIEEEFloat();
        mNativeCaps.fragmentMediumpFloat.setIEEEFloat();
        mNativeCaps.fragmentLowpFloat.setIEEEFloat();
    
        // Can't find documentation on the int precision in Vulkan.
        mNativeCaps.vertexHighpInt.setTwosComplementInt(32);
        mNativeCaps.vertexMediumpInt.setTwosComplementInt(32);
        mNativeCaps.vertexLowpInt.setTwosComplementInt(32);
        mNativeCaps.fragmentHighpInt.setTwosComplementInt(32);
        mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32);
        mNativeCaps.fragmentLowpInt.setTwosComplementInt(32);
    
        // Compute shader limits.
        mNativeCaps.maxComputeWorkGroupCount[0] = LimitToInt(limitsVk.maxComputeWorkGroupCount[0]);
        mNativeCaps.maxComputeWorkGroupCount[1] = LimitToInt(limitsVk.maxComputeWorkGroupCount[1]);
        mNativeCaps.maxComputeWorkGroupCount[2] = LimitToInt(limitsVk.maxComputeWorkGroupCount[2]);
        mNativeCaps.maxComputeWorkGroupSize[0]  = LimitToInt(limitsVk.maxComputeWorkGroupSize[0]);
        mNativeCaps.maxComputeWorkGroupSize[1]  = LimitToInt(limitsVk.maxComputeWorkGroupSize[1]);
        mNativeCaps.maxComputeWorkGroupSize[2]  = LimitToInt(limitsVk.maxComputeWorkGroupSize[2]);
        mNativeCaps.maxComputeWorkGroupInvocations =
            LimitToInt(limitsVk.maxComputeWorkGroupInvocations);
        mNativeCaps.maxComputeSharedMemorySize = LimitToInt(limitsVk.maxComputeSharedMemorySize);
    
        GLuint maxUniformBlockSize = limitsVk.maxUniformBufferRange;
    
        // Clamp the maxUniformBlockSize to 64KB (majority of devices support up to this size
        // currently), on AMD the maxUniformBufferRange is near uint32_t max.
        maxUniformBlockSize = std::min(0x10000u, maxUniformBlockSize);
    
        const GLuint maxUniformVectors = maxUniformBlockSize / (sizeof(GLfloat) * kComponentsPerVector);
        const GLuint maxUniformComponents = maxUniformVectors * kComponentsPerVector;
    
        // Uniforms are implemented using a uniform buffer, so the max number of uniforms we can
        // support is the max buffer range divided by the size of a single uniform (4X float).
        mNativeCaps.maxVertexUniformVectors   = maxUniformVectors;
        mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
        for (gl::ShaderType shaderType : gl::AllShaderTypes())
        {
            mNativeCaps.maxShaderUniformComponents[shaderType] = maxUniformComponents;
        }
        mNativeCaps.maxUniformLocations = maxUniformVectors;
    
        // Every stage has 1 reserved uniform buffer for the default uniforms, and 1 for the driver
        // uniforms.
        constexpr uint32_t kTotalReservedPerStageUniformBuffers =
            kReservedDriverUniformBindingCount + kReservedPerStageDefaultUniformBindingCount;
        constexpr uint32_t kTotalReservedUniformBuffers =
            kReservedDriverUniformBindingCount + kReservedDefaultUniformBindingCount;
    
        const int32_t maxPerStageUniformBuffers = LimitToInt(
            limitsVk.maxPerStageDescriptorUniformBuffers - kTotalReservedPerStageUniformBuffers);
        const int32_t maxCombinedUniformBuffers =
            LimitToInt(limitsVk.maxDescriptorSetUniformBuffers - kTotalReservedUniformBuffers);
        for (gl::ShaderType shaderType : gl::AllShaderTypes())
        {
            mNativeCaps.maxShaderUniformBlocks[shaderType] = maxPerStageUniformBuffers;
        }
        mNativeCaps.maxCombinedUniformBlocks = maxCombinedUniformBuffers;
    
        mNativeCaps.maxUniformBufferBindings = maxCombinedUniformBuffers;
        mNativeCaps.maxUniformBlockSize      = maxUniformBlockSize;
        mNativeCaps.uniformBufferOffsetAlignment =
            static_cast<GLint>(limitsVk.minUniformBufferOffsetAlignment);
    
        // Note that Vulkan currently implements textures as combined image+samplers, so the limit is
        // the minimum of supported samplers and sampled images.
        const uint32_t maxPerStageTextures = std::min(limitsVk.maxPerStageDescriptorSamplers,
                                                      limitsVk.maxPerStageDescriptorSampledImages);
        const uint32_t maxCombinedTextures =
            std::min(limitsVk.maxDescriptorSetSamplers, limitsVk.maxDescriptorSetSampledImages);
        for (gl::ShaderType shaderType : gl::AllShaderTypes())
        {
            mNativeCaps.maxShaderTextureImageUnits[shaderType] = LimitToInt(maxPerStageTextures);
        }
        mNativeCaps.maxCombinedTextureImageUnits = LimitToInt(maxCombinedTextures);
    
        uint32_t maxPerStageStorageBuffers    = limitsVk.maxPerStageDescriptorStorageBuffers;
        uint32_t maxVertexStageStorageBuffers = maxPerStageStorageBuffers;
        uint32_t maxCombinedStorageBuffers    = limitsVk.maxDescriptorSetStorageBuffers;
    
        // A number of storage buffer slots are used in the vertex shader to emulate transform feedback.
        // Note that Vulkan requires maxPerStageDescriptorStorageBuffers to be at least 4 (i.e. the same
        // as gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS).
        // TODO(syoussefi): This should be conditioned to transform feedback extension not being
        // present.  http://anglebug.com/3206.
        // TODO(syoussefi): If geometry shader is supported, emulation will be done at that stage, and
        // so the reserved storage buffers should be accounted in that stage.  http://anglebug.com/3606
        static_assert(
            gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS == 4,
            "Limit to ES2.0 if supported SSBO count < supporting transform feedback buffer count");
        if (mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics)
        {
            ASSERT(maxVertexStageStorageBuffers >= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS);
            maxVertexStageStorageBuffers -= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
            maxCombinedStorageBuffers -= gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS;
    
            // Cap the per-stage limit of the other stages to the combined limit, in case the combined
            // limit is now lower than that.
            maxPerStageStorageBuffers = std::min(maxPerStageStorageBuffers, maxCombinedStorageBuffers);
        }
    
        // Reserve up to IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS storage buffers in the fragment and
        // compute stages for atomic counters.  This is only possible if the number of per-stage storage
        // buffers is greater than 4, which is the required GLES minimum for compute.
        //
        // For each stage, we'll either not support atomic counter buffers, or support exactly
        // IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS.  This is due to restrictions in the shader
        // translator where we can't know how many atomic counter buffers we would really need after
        // linking so we can't create a packed buffer array.
        //
        // For the vertex stage, we could support atomic counters without storage buffers, but that's
        // likely not very useful, so we use the same limit (4 + MAX_ATOMIC_COUNTER_BUFFERS) for the
        // vertex stage to determine if we would want to add support for atomic counter buffers.
        constexpr uint32_t kMinimumStorageBuffersForAtomicCounterBufferSupport =
            gl::limits::kMinimumComputeStorageBuffers +
            gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS;
        uint32_t maxVertexStageAtomicCounterBuffers = 0;
        uint32_t maxPerStageAtomicCounterBuffers    = 0;
        uint32_t maxCombinedAtomicCounterBuffers    = 0;
    
        if (maxPerStageStorageBuffers >= kMinimumStorageBuffersForAtomicCounterBufferSupport)
        {
            maxPerStageAtomicCounterBuffers = gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS;
            maxCombinedAtomicCounterBuffers = gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS;
        }
    
        if (maxVertexStageStorageBuffers >= kMinimumStorageBuffersForAtomicCounterBufferSupport)
        {
            maxVertexStageAtomicCounterBuffers = gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS;
        }
    
        maxVertexStageStorageBuffers -= maxVertexStageAtomicCounterBuffers;
        maxPerStageStorageBuffers -= maxPerStageAtomicCounterBuffers;
        maxCombinedStorageBuffers -= maxCombinedAtomicCounterBuffers;
    
        mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] =
            mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics
                ? LimitToInt(maxVertexStageStorageBuffers)
                : 0;
        mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] =
            mPhysicalDeviceFeatures.fragmentStoresAndAtomics ? LimitToInt(maxPerStageStorageBuffers)
                                                             : 0;
        mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Compute] =
            LimitToInt(maxPerStageStorageBuffers);
        mNativeCaps.maxCombinedShaderStorageBlocks = LimitToInt(maxCombinedStorageBuffers);
    
        mNativeCaps.maxShaderStorageBufferBindings = LimitToInt(maxCombinedStorageBuffers);
        mNativeCaps.maxShaderStorageBlockSize      = limitsVk.maxStorageBufferRange;
        mNativeCaps.shaderStorageBufferOffsetAlignment =
            LimitToInt(static_cast<uint32_t>(limitsVk.minStorageBufferOffsetAlignment));
    
        mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::Vertex] =
            mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics
                ? LimitToInt(maxVertexStageAtomicCounterBuffers)
                : 0;
        mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::Fragment] =
            mPhysicalDeviceFeatures.fragmentStoresAndAtomics
                ? LimitToInt(maxPerStageAtomicCounterBuffers)
                : 0;
        mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::Compute] =
            LimitToInt(maxPerStageAtomicCounterBuffers);
        mNativeCaps.maxCombinedAtomicCounterBuffers = LimitToInt(maxCombinedAtomicCounterBuffers);
    
        mNativeCaps.maxAtomicCounterBufferBindings = LimitToInt(maxCombinedAtomicCounterBuffers);
        // Emulated as storage buffers, atomic counter buffers have the same size limit.  However, the
        // limit is a signed integer and values above int max will end up as a negative size.
        mNativeCaps.maxAtomicCounterBufferSize = LimitToInt(limitsVk.maxStorageBufferRange);
    
        // There is no particular limit to how many atomic counters there can be, other than the size of
        // a storage buffer.  We nevertheless limit this to something reasonable (4096 arbitrarily).
        const int32_t maxAtomicCounters =
            std::min<int32_t>(4096, limitsVk.maxStorageBufferRange / sizeof(uint32_t));
        for (gl::ShaderType shaderType : gl::AllShaderTypes())
        {
            mNativeCaps.maxShaderAtomicCounters[shaderType] = maxAtomicCounters;
        }
    
        // Set maxShaderAtomicCounters to zero if atomic is not supported.
        if (!mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics)
        {
            mNativeCaps.maxShaderAtomicCounters[gl::ShaderType::Vertex] = 0;
        }
        if (!mPhysicalDeviceFeatures.fragmentStoresAndAtomics)
        {
            mNativeCaps.maxShaderAtomicCounters[gl::ShaderType::Fragment] = 0;
        }
    
        mNativeCaps.maxCombinedAtomicCounters = maxAtomicCounters;
    
        // GL Images correspond to Vulkan Storage Images.
        const int32_t maxPerStageImages = LimitToInt(limitsVk.maxPerStageDescriptorStorageImages);
        const int32_t maxCombinedImages = LimitToInt(limitsVk.maxDescriptorSetStorageImages);
        const int32_t maxVertexPipelineImages =
            mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics ? maxPerStageImages : 0;
    
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Vertex]         = maxVertexPipelineImages;
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::TessControl]    = maxVertexPipelineImages;
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::TessEvaluation] = maxVertexPipelineImages;
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Geometry]       = maxVertexPipelineImages;
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Fragment] =
            mPhysicalDeviceFeatures.fragmentStoresAndAtomics ? maxPerStageImages : 0;
        mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Compute] = maxPerStageImages;
    
        mNativeCaps.maxCombinedImageUniforms = maxCombinedImages;
        mNativeCaps.maxImageUnits            = maxCombinedImages;
    
        mNativeCaps.minProgramTexelOffset         = limitsVk.minTexelOffset;
        mNativeCaps.maxProgramTexelOffset         = limitsVk.maxTexelOffset;
        mNativeCaps.minProgramTextureGatherOffset = limitsVk.minTexelGatherOffset;
        mNativeCaps.maxProgramTextureGatherOffset = limitsVk.maxTexelGatherOffset;
    
        // There is no additional limit to the combined number of components.  We can have up to a
        // maximum number of uniform buffers, each having the maximum number of components.  Note that
        // this limit includes both components in and out of uniform buffers.
        //
        // This value is limited to INT_MAX to avoid overflow when queried from glGetIntegerv().
        const uint64_t maxCombinedUniformComponents =
            std::min<uint64_t>(static_cast<uint64_t>(maxPerStageUniformBuffers +
                                                     kReservedPerStageDefaultUniformBindingCount) *
                                   maxUniformComponents,
                               std::numeric_limits<GLint>::max());
        for (gl::ShaderType shaderType : gl::AllShaderTypes())
        {
            mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents;
        }
    
        // Total number of resources available to the user are as many as Vulkan allows minus everything
        // that ANGLE uses internally.  That is, one dynamic uniform buffer used per stage for default
        // uniforms and a single dynamic uniform buffer for driver uniforms.  Additionally, Vulkan uses
        // up to IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS + 1 buffers for transform feedback (Note:
        // +1 is for the "counter" buffer of transform feedback, which will be necessary for transform
        // feedback extension and ES3.2 transform feedback emulation, but is not yet present).
        constexpr uint32_t kReservedPerStageUniformBufferCount = 1;
        constexpr uint32_t kReservedPerStageBindingCount =
            kReservedDriverUniformBindingCount + kReservedPerStageUniformBufferCount +
            gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS + 1;
    
        // Note: maxPerStageResources is required to be at least the sum of per stage UBOs, SSBOs etc
        // which total a minimum of 44 resources, so no underflow is possible here.  Limit the total
        // number of resources reported by Vulkan to 2 billion though to avoid seeing negative numbers
        // in applications that take the value as signed int (including dEQP).
        const uint32_t maxPerStageResources = limitsVk.maxPerStageResources;
        mNativeCaps.maxCombinedShaderOutputResources =
            LimitToInt(maxPerStageResources - kReservedPerStageBindingCount);
    
        // Reserve 1 extra varying for ANGLEPosition when GLLineRasterization is enabled
        constexpr GLint kReservedVaryingComponentsForGLLineRasterization = 4;
        // Reserve 1 extra varying for transform feedback capture of gl_Position.
        constexpr GLint kReservedVaryingComponentsForTransformFeedbackExtension = 4;
    
        GLint reservedVaryingComponentCount = 0;
    
        if (getFeatures().basicGLLineRasterization.enabled)
        {
            reservedVaryingComponentCount += kReservedVaryingComponentsForGLLineRasterization;
        }
        if (getFeatures().supportsTransformFeedbackExtension.enabled)
        {
            reservedVaryingComponentCount += kReservedVaryingComponentsForTransformFeedbackExtension;
        }
    
        // The max varying vectors should not include gl_Position.
        // The gles2.0 section 2.10 states that "gl_Position is not a varying variable and does
        // not count against this limit.", but the Vulkan spec has no such mention in its Built-in
        // vars section. It is implicit that we need to actually reserve it for Vulkan in that case.
        //
        // Note that this exception for gl_Position does not apply to MAX_VERTEX_OUTPUT_COMPONENTS and
        // similar limits.
        const GLint reservedVaryingVectorCount = reservedVaryingComponentCount / 4 + 1;
    
        const GLint maxVaryingCount =
            std::min(limitsVk.maxVertexOutputComponents, limitsVk.maxFragmentInputComponents);
        mNativeCaps.maxVaryingVectors =
            LimitToInt((maxVaryingCount / kComponentsPerVector) - reservedVaryingVectorCount);
        mNativeCaps.maxVertexOutputComponents =
            LimitToInt(limitsVk.maxVertexOutputComponents) - reservedVaryingComponentCount;
        mNativeCaps.maxFragmentInputComponents =
            LimitToInt(limitsVk.maxFragmentInputComponents) - reservedVaryingComponentCount;
    
        mNativeCaps.maxTransformFeedbackInterleavedComponents =
            gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS;
        mNativeCaps.maxTransformFeedbackSeparateAttributes =
            gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS;
        mNativeCaps.maxTransformFeedbackSeparateComponents =
            gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS;
    
        mNativeCaps.minProgramTexelOffset = limitsVk.minTexelOffset;
        mNativeCaps.maxProgramTexelOffset = LimitToInt(limitsVk.maxTexelOffset);
    
        const uint32_t sampleCounts =
            limitsVk.framebufferColorSampleCounts & limitsVk.framebufferDepthSampleCounts &
            limitsVk.framebufferStencilSampleCounts & vk_gl::kSupportedSampleCounts;
    
        mNativeCaps.maxSamples            = LimitToInt(vk_gl::GetMaxSampleCount(sampleCounts));
        mNativeCaps.maxFramebufferSamples = mNativeCaps.maxSamples;
    
        mNativeCaps.subPixelBits = limitsVk.subPixelPrecisionBits;
    
        // Important games are not checking supported extensions properly, and are confusing the
        // GL_EXT_shader_framebuffer_fetch_non_coherent as the GL_EXT_shader_framebuffer_fetch
        // extension.  Therefore, don't enable the extension on Arm and Qualcomm.
        // https://issuetracker.google.com/issues/186643966
        if (!(IsARM(mPhysicalDeviceProperties.vendorID) ||
              IsQualcomm(mPhysicalDeviceProperties.vendorID)))
        {
            // Enable GL_EXT_shader_framebuffer_fetch_non_coherent
            // For supporting this extension, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS is used.
            mNativeExtensions.shaderFramebufferFetchNonCoherentEXT =
                mNativeCaps.maxDrawBuffers >= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
        }
    
        // Enable Program Binary extension.
        mNativeExtensions.getProgramBinaryOES = true;
        mNativeCaps.programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE);
    
        // Enable GL_NV_pixel_buffer_object extension.
        mNativeExtensions.pixelBufferObjectNV = true;
    
        // Enable GL_NV_fence extension.
        mNativeExtensions.fenceNV = true;
    
        // Enable GL_EXT_copy_image
        mNativeExtensions.copyImageEXT = true;
    
        // GL_EXT_clip_control
        mNativeExtensions.clipControlEXT = true;
    
        // Enable GL_EXT_texture_buffer and OES variant.  Nearly all formats required for this extension
        // are also required to have the UNIFORM_TEXEL_BUFFER feature bit in Vulkan, except for
        // R32G32B32_SFLOAT/UINT/SINT which are optional.  For many formats, the STORAGE_TEXEL_BUFFER
        // feature is optional though.  This extension is exposed only if the formats specified in
        // EXT_texture_buffer support the necessary feature bits.
        if (vk::HasTextureBufferSupport(this))
        {
            mNativeExtensions.textureBufferOES = true;
            mNativeExtensions.textureBufferEXT = true;
            mNativeCaps.maxTextureBufferSize   = LimitToInt(limitsVk.maxTexelBufferElements);
            mNativeCaps.textureBufferOffsetAlignment =
                LimitToInt(limitsVk.minTexelBufferOffsetAlignment);
        }
    
        // Atomic image operations in the vertex and fragment shaders require the
        // vertexPipelineStoresAndAtomics and fragmentStoresAndAtomics Vulkan features respectively.
        // If either of these features is not present, the number of image uniforms for that stage is
        // advertized as zero, so image atomic operations support can be agnostic of shader stages.
        //
        // GL_OES_shader_image_atomic requires that image atomic functions have support for r32i and
        // r32ui formats.  These formats have mandatory support for STORAGE_IMAGE_ATOMIC and
        // STORAGE_TEXEL_BUFFER_ATOMIC features in Vulkan.  Additionally, it requires that
        // imageAtomicExchange supports r32f, which is emulated in ANGLE transforming the shader to
        // expect r32ui instead.
        mNativeExtensions.shaderImageAtomicOES = true;
    
        // Geometry shaders are required for ES 3.2.
        // We don't support GS when we are emulating line raster due to the tricky position varying.
        if (mPhysicalDeviceFeatures.geometryShader && !mFeatures.basicGLLineRasterization.enabled)
        {
            // TODO: geometry shader support is incomplete.  http://anglebug.com/3571
            bool geometryShader = mFeatures.supportsTransformFeedbackExtension.enabled &&
                                  mFeatures.exposeNonConformantExtensionsAndVersions.enabled;
            mNativeExtensions.geometryShaderEXT = geometryShader;
            mNativeExtensions.geometryShaderOES = geometryShader;
            mNativeCaps.maxFramebufferLayers    = LimitToInt(limitsVk.maxFramebufferLayers);
    
            // If the provoking vertex feature is enabled, angle specifies to use
            // the "last" convention in order to match GL behavior. Otherwise, use
            // "first" as vulkan follows this convention for provoking vertex.
            mNativeCaps.layerProvokingVertex = (mFeatures.provokingVertex.enabled)
                                                   ? GL_LAST_VERTEX_CONVENTION_EXT
                                                   : GL_FIRST_VERTEX_CONVENTION_EXT;
    
            mNativeCaps.maxGeometryInputComponents =
                LimitToInt(limitsVk.maxGeometryInputComponents) - reservedVaryingComponentCount;
            mNativeCaps.maxGeometryOutputComponents =
                LimitToInt(limitsVk.maxGeometryOutputComponents) - reservedVaryingComponentCount;
            mNativeCaps.maxGeometryOutputVertices = LimitToInt(limitsVk.maxGeometryOutputVertices);
            mNativeCaps.maxGeometryTotalOutputComponents =
                LimitToInt(limitsVk.maxGeometryTotalOutputComponents);
            mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Geometry] =
                mNativeCaps.maxCombinedShaderOutputResources;
            mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::Geometry] =
                maxCombinedAtomicCounterBuffers;
            mNativeCaps.maxGeometryShaderInvocations =
                LimitToInt(limitsVk.maxGeometryShaderInvocations);
        }
    
        // We don't support TS when we are emulating line raster due to the tricky position varying.
        if (mPhysicalDeviceFeatures.tessellationShader && !mFeatures.basicGLLineRasterization.enabled)
        {
            constexpr uint32_t kReservedTessellationDefaultUniformBindingCount = 2;
    
            // TODO: tessellation shader support is incomplete.  http://anglebug.com/3572
            mNativeExtensions.tessellationShaderEXT =
                mFeatures.supportsTransformFeedbackExtension.enabled &&
                mFeatures.exposeNonConformantExtensionsAndVersions.enabled;
            mNativeCaps.maxPatchVertices = LimitToInt(limitsVk.maxTessellationPatchSize);
            mNativeCaps.maxTessPatchComponents =
                LimitToInt(limitsVk.maxTessellationControlPerPatchOutputComponents);
            mNativeCaps.maxTessGenLevel = LimitToInt(limitsVk.maxTessellationGenerationLevel);
    
            mNativeCaps.maxTessControlInputComponents =
                LimitToInt(limitsVk.maxTessellationControlPerVertexInputComponents);
            mNativeCaps.maxTessControlOutputComponents =
                LimitToInt(limitsVk.maxTessellationControlPerVertexOutputComponents);
            mNativeCaps.maxTessControlTotalOutputComponents =
                LimitToInt(limitsVk.maxTessellationControlTotalOutputComponents);
            mNativeCaps.maxTessEvaluationInputComponents =
                LimitToInt(limitsVk.maxTessellationEvaluationInputComponents);
            mNativeCaps.maxTessEvaluationOutputComponents =
                LimitToInt(limitsVk.maxTessellationEvaluationOutputComponents);
    
            // There is 1 default uniform binding used per tessellation stages.
            mNativeCaps.maxCombinedUniformBlocks = LimitToInt(
                mNativeCaps.maxCombinedUniformBlocks + kReservedTessellationDefaultUniformBindingCount);
            mNativeCaps.maxUniformBufferBindings = LimitToInt(
                mNativeCaps.maxUniformBufferBindings + kReservedTessellationDefaultUniformBindingCount);
    
            mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::TessControl] =
                mNativeCaps.maxCombinedShaderOutputResources;
            mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::TessControl] =
                maxCombinedAtomicCounterBuffers;
    
            mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::TessEvaluation] =
                mNativeCaps.maxCombinedShaderOutputResources;
            mNativeCaps.maxShaderAtomicCounterBuffers[gl::ShaderType::TessEvaluation] =
                maxCombinedAtomicCounterBuffers;
        }
    
        // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance
        // From the EXT_clip_cull_distance extension spec:
        //
        // > Modify Section 7.2, "Built-In Constants" (p. 126)
        // >
        // > const mediump int gl_MaxClipDistances = 8;
        // > const mediump int gl_MaxCullDistances = 8;
        // > const mediump int gl_MaxCombinedClipAndCullDistances = 8;
        constexpr uint32_t kMaxClipDistancePerSpec                = 8;
        constexpr uint32_t kMaxCullDistancePerSpec                = 8;
        constexpr uint32_t kMaxCombinedClipAndCullDistancePerSpec = 8;
    
        // TODO: http://anglebug.com/5466
        // After implementing EXT_geometry_shader, EXT_clip_cull_distance should be additionally
        // implemented to support the geometry shader. Until then, EXT_clip_cull_distance is enabled
        // only in the experimental cases.
        if (mPhysicalDeviceFeatures.shaderClipDistance &&
            limitsVk.maxClipDistances >= kMaxClipDistancePerSpec)
        {
            mNativeExtensions.clipDistanceAPPLE = true;
            mNativeCaps.maxClipDistances =
                std::min<GLuint>(limitsVk.maxClipDistances, gl::IMPLEMENTATION_MAX_CLIP_DISTANCES);
    
            if (mPhysicalDeviceFeatures.shaderCullDistance &&
                limitsVk.maxCullDistances >= kMaxCullDistancePerSpec &&
                limitsVk.maxCombinedClipAndCullDistances >= kMaxCombinedClipAndCullDistancePerSpec)
            {
                mNativeExtensions.clipCullDistanceEXT       = true;
                mNativeCaps.maxCullDistances                = limitsVk.maxCullDistances;
                mNativeCaps.maxCombinedClipAndCullDistances = limitsVk.maxCombinedClipAndCullDistances;
            }
        }
    
        // GL_EXT_blend_func_extended
        mNativeExtensions.blendFuncExtendedEXT = (mPhysicalDeviceFeatures.dualSrcBlend == VK_TRUE);
        mNativeCaps.maxDualSourceDrawBuffers   = LimitToInt(limitsVk.maxFragmentDualSrcAttachments);
    
        // GL_ANGLE_relaxed_vertex_attribute_type
        mNativeExtensions.relaxedVertexAttributeTypeANGLE = true;
    
        // GL_OVR_multiview*.  Bresenham line emulation does not work with multiview.  There's no
        // limitation in Vulkan to restrict an application to multiview 1.
        mNativeExtensions.multiviewOVR =
            mMultiviewFeatures.multiview && mFeatures.bresenhamLineRasterization.enabled;
        mNativeExtensions.multiview2OVR = mNativeExtensions.multiviewOVR;
        mNativeCaps.maxViews            = mMultiviewProperties.maxMultiviewViewCount;
    
        // GL_ANGLE_yuv_internal_format
        mNativeExtensions.yuvInternalFormatANGLE =
            getFeatures().supportsYUVSamplerConversion.enabled && vk::CanSupportYuvInternalFormat(this);
    
        // GL_EXT_primitive_bounding_box
        mNativeExtensions.primitiveBoundingBoxEXT = true;
    
        // GL_EXT_protected_textures
        mNativeExtensions.protectedTexturesEXT = mFeatures.supportsProtectedMemory.enabled;
    }
    
    namespace vk
    {
    
    bool CanSupportGPUShader5EXT(const VkPhysicalDeviceFeatures &features)
    {
        // We use the following Vulkan features to implement EXT_gpu_shader5:
        // - shaderImageGatherExtended: textureGatherOffset with non-constant offset and
        //   textureGatherOffsets family of functions.
        // - shaderSampledImageArrayDynamicIndexing and shaderUniformBufferArrayDynamicIndexing:
        //   dynamically uniform indices for samplers and uniform buffers.
        return features.shaderImageGatherExtended && features.shaderSampledImageArrayDynamicIndexing &&
               features.shaderUniformBufferArrayDynamicIndexing;
    }
    
    }  // namespace vk
    
    namespace egl_vk
    {
    
    namespace
    {
    
    EGLint ComputeMaximumPBufferPixels(const VkPhysicalDeviceProperties &physicalDeviceProperties)
    {
        // EGLints are signed 32-bit integers, it's fairly easy to overflow them, especially since
        // Vulkan's minimum guaranteed VkImageFormatProperties::maxResourceSize is 2^31 bytes.
        constexpr uint64_t kMaxValueForEGLint =
            static_cast<uint64_t>(std::numeric_limits<EGLint>::max());
    
        // TODO(geofflang): Compute the maximum size of a pbuffer by using the maxResourceSize result
        // from vkGetPhysicalDeviceImageFormatProperties for both the color and depth stencil format and
        // the exact image creation parameters that would be used to create the pbuffer. Because it is
        // always safe to return out-of-memory errors on pbuffer allocation, it's fine to simply return
        // the number of pixels in a max width by max height pbuffer for now. http://anglebug.com/2622
    
        // Storing the result of squaring a 32-bit unsigned int in a 64-bit unsigned int is safe.
        static_assert(std::is_same<decltype(physicalDeviceProperties.limits.maxImageDimension2D),
                                   uint32_t>::value,
                      "physicalDeviceProperties.limits.maxImageDimension2D expected to be a uint32_t.");
        const uint64_t maxDimensionsSquared =
            static_cast<uint64_t>(physicalDeviceProperties.limits.maxImageDimension2D) *
            static_cast<uint64_t>(physicalDeviceProperties.limits.maxImageDimension2D);
    
        return static_cast<EGLint>(std::min(maxDimensionsSquared, kMaxValueForEGLint));
    }
    
    // Generates a basic config for a combination of color format, depth stencil format and sample
    // count.
    egl::Config GenerateDefaultConfig(DisplayVk *display,
                                      const gl::InternalFormat &colorFormat,
                                      const gl::InternalFormat &depthStencilFormat,
                                      EGLint sampleCount)
    {
        const RendererVk *renderer = display->getRenderer();
    
        const VkPhysicalDeviceProperties &physicalDeviceProperties =
            renderer->getPhysicalDeviceProperties();
        gl::Version maxSupportedESVersion = renderer->getMaxSupportedESVersion();
    
        // ES3 features are required to emulate ES1
        EGLint es1Support = (maxSupportedESVersion.major >= 3 ? EGL_OPENGL_ES_BIT : 0);
        EGLint es2Support = (maxSupportedESVersion.major >= 2 ? EGL_OPENGL_ES2_BIT : 0);
        EGLint es3Support = (maxSupportedESVersion.major >= 3 ? EGL_OPENGL_ES3_BIT : 0);
    
        egl::Config config;
    
        config.renderTargetFormat = colorFormat.internalFormat;
        config.depthStencilFormat = depthStencilFormat.internalFormat;
        config.bufferSize         = colorFormat.pixelBytes * 8;
        config.redSize            = colorFormat.redBits;
        config.greenSize          = colorFormat.greenBits;
        config.blueSize           = colorFormat.blueBits;
        config.alphaSize          = colorFormat.alphaBits;
        config.alphaMaskSize      = 0;
        config.bindToTextureRGB   = colorFormat.format == GL_RGB;
        config.bindToTextureRGBA  = colorFormat.format == GL_RGBA || colorFormat.format == GL_BGRA_EXT;
        config.colorBufferType    = EGL_RGB_BUFFER;
        config.configCaveat       = GetConfigCaveat(colorFormat.internalFormat);
        config.conformant         = es1Support | es2Support | es3Support;
        config.depthSize          = depthStencilFormat.depthBits;
        config.stencilSize        = depthStencilFormat.stencilBits;
        config.level              = 0;
        config.matchNativePixmap  = EGL_NONE;
        config.maxPBufferWidth    = physicalDeviceProperties.limits.maxImageDimension2D;
        config.maxPBufferHeight   = physicalDeviceProperties.limits.maxImageDimension2D;
        config.maxPBufferPixels   = ComputeMaximumPBufferPixels(physicalDeviceProperties);
        config.maxSwapInterval    = 1;
        config.minSwapInterval    = 0;
        config.nativeRenderable   = EGL_TRUE;
        config.nativeVisualID     = static_cast<EGLint>(GetNativeVisualID(colorFormat));
        config.nativeVisualType   = EGL_NONE;
        config.renderableType     = es1Support | es2Support | es3Support;
        config.sampleBuffers      = (sampleCount > 0) ? 1 : 0;
        config.samples            = sampleCount;
        config.surfaceType        = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
        // Vulkan surfaces use a different origin than OpenGL, always prefer to be flipped vertically if
        // possible.
        config.optimalOrientation    = EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE;
        config.transparentType       = EGL_NONE;
        config.transparentRedValue   = 0;
        config.transparentGreenValue = 0;
        config.transparentBlueValue  = 0;
        config.colorComponentType =
            gl_egl::GLComponentTypeToEGLColorComponentType(colorFormat.componentType);
    
        // Vulkan always supports off-screen rendering.  Check the config with display to see if it can
        // also have window support.  If not, the following call should automatically remove
        // EGL_WINDOW_BIT.
        display->checkConfigSupport(&config);
    
        return config;
    }
    
    }  // anonymous namespace
    
    egl::ConfigSet GenerateConfigs(const GLenum *colorFormats,
                                   size_t colorFormatsCount,
                                   const GLenum *depthStencilFormats,
                                   size_t depthStencilFormatCount,
                                   DisplayVk *display)
    {
        ASSERT(colorFormatsCount > 0);
        ASSERT(display != nullptr);
    
        gl::SupportedSampleSet colorSampleCounts;
        gl::SupportedSampleSet depthStencilSampleCounts;
        gl::SupportedSampleSet sampleCounts;
    
        const VkPhysicalDeviceLimits &limits =
            display->getRenderer()->getPhysicalDeviceProperties().limits;
        const uint32_t depthStencilSampleCountsLimit = limits.framebufferDepthSampleCounts &
                                                       limits.framebufferStencilSampleCounts &
                                                       vk_gl::kSupportedSampleCounts;
    
        vk_gl::AddSampleCounts(limits.framebufferColorSampleCounts & vk_gl::kSupportedSampleCounts,
                               &colorSampleCounts);
        vk_gl::AddSampleCounts(depthStencilSampleCountsLimit, &depthStencilSampleCounts);
    
        // Always support 0 samples
        colorSampleCounts.insert(0);
        depthStencilSampleCounts.insert(0);
    
        std::set_intersection(colorSampleCounts.begin(), colorSampleCounts.end(),
                              depthStencilSampleCounts.begin(), depthStencilSampleCounts.end(),
                              std::inserter(sampleCounts, sampleCounts.begin()));
    
        egl::ConfigSet configSet;
    
        for (size_t colorFormatIdx = 0; colorFormatIdx < colorFormatsCount; colorFormatIdx++)
        {
            const gl::InternalFormat &colorFormatInfo =
                gl::GetSizedInternalFormatInfo(colorFormats[colorFormatIdx]);
            ASSERT(colorFormatInfo.sized);
    
            for (size_t depthStencilFormatIdx = 0; depthStencilFormatIdx < depthStencilFormatCount;
                 depthStencilFormatIdx++)
            {
                const gl::InternalFormat &depthStencilFormatInfo =
                    gl::GetSizedInternalFormatInfo(depthStencilFormats[depthStencilFormatIdx]);
                ASSERT(depthStencilFormats[depthStencilFormatIdx] == GL_NONE ||
                       depthStencilFormatInfo.sized);
    
                const gl::SupportedSampleSet *configSampleCounts = &sampleCounts;
                // If there is no depth/stencil buffer, use the color samples set.
                if (depthStencilFormats[depthStencilFormatIdx] == GL_NONE)
                {
                    configSampleCounts = &colorSampleCounts;
                }
                // If there is no color buffer, use the depth/stencil samples set.
                else if (colorFormats[colorFormatIdx] == GL_NONE)
                {
                    configSampleCounts = &depthStencilSampleCounts;
                }
    
                for (EGLint sampleCount : *configSampleCounts)
                {
                    egl::Config config = GenerateDefaultConfig(display, colorFormatInfo,
                                                               depthStencilFormatInfo, sampleCount);
                    configSet.add(config);
                }
            }
        }
    
        return configSet;
    }
    
    }  // namespace egl_vk
    
    }  // namespace rx