| // |
| // 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/system_utils.h" |
| #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 HasTextureBufferSupport(const RendererVk *rendererVk) |
| { |
| // glTexBuffer page 187 table 8.18. |
| // glBindImageTexture page 216 table 8.24. |
| // https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf. |
| // https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/chap43.html#features-required-format-support |
| // required image and texture access for texture buffer formats are |
| // texture access image access |
| // 8-bit components, all required by vulkan. |
| // |
| // GL_R8 Y N |
| // GL_R8I Y N |
| // GL_R8UI Y N |
| // GL_RG8 Y N |
| // GL_RG8I Y N |
| // GL_RG8UI Y N |
| // GL_RGBA8 Y Y |
| // GL_RGBA8I Y Y |
| // GL_RGBA8UI Y Y |
| // GL_RGBA8_SNORM N Y |
| // |
| // 16-bit components, all required by vulkan. |
| // |
| // GL_R16F Y N |
| // GL_R16I Y N |
| // GL_R16UI Y N |
| // GL_RG16F Y N |
| // GL_RG16I Y N |
| // GL_RG16UI Y N |
| // GL_RGBA16F Y Y |
| // GL_RGBA16I Y Y |
| // GL_RGBA16UI Y Y |
| // |
| // 32-bit components, except RGB32 all others required by vulkan. |
| // |
| // GL_R32F Y Y |
| // GL_R32I Y Y |
| // GL_R32UI Y Y |
| // GL_RG32F Y N |
| // GL_RG32I Y N |
| // GL_RG32UI Y N |
| // GL_RGB32F Y N |
| // GL_RGB32I Y N |
| // GL_RGB32UI Y N |
| // GL_RGBA32F Y Y |
| // GL_RGBA32I Y Y |
| // GL_RGBA32UI Y Y |
| |
| // TODO: some platform may not support RGB32 formats as UNIFORM_TEXEL_BUFFER |
| // Despite this limitation, we expose EXT_texture_buffer. http://anglebug.com/3573 |
| if (rendererVk->getFeatures().exposeNonConformantExtensionsAndVersions.enabled) |
| { |
| return true; |
| } |
| |
| const std::array<GLenum, 3> &optionalFormats = { |
| GL_RGB32F, |
| GL_RGB32I, |
| GL_RGB32UI, |
| }; |
| |
| for (GLenum formatGL : optionalFormats) |
| { |
| const Format &formatVk = rendererVk->getFormat(formatGL); |
| if (!rendererVk->hasBufferFormatFeatureBits(formatVk.getActualBufferFormat(false).id, |
| VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)) |
| 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.textureMultisampleANGLE = 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 = !IsARM(mPhysicalDeviceProperties.vendorID); |
| mNativeExtensions.discardFramebufferEXT = true; |
| mNativeExtensions.textureBorderClampOES = getFeatures().supportsCustomBorderColor.enabled; |
| mNativeExtensions.textureBorderClampEXT = getFeatures().supportsCustomBorderColor.enabled; |
| // Enable EXT_texture_type_2_10_10_10_REV |
| mNativeExtensions.textureType2101010REVEXT = true; |
| |
| // Enable EXT_multi_draw_indirect |
| mNativeExtensions.multiDrawIndirectEXT = true; |
| |
| // Enable ANGLE_base_vertex_base_instance |
| mNativeExtensions.baseVertexBaseInstanceANGLE = true; |
| mNativeExtensions.baseVertexBaseInstanceShaderBuiltinANGLE = 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.EGLImageStorageEXT = 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 timestamp queries |
| // unless that feature is available. |
| if (vk::OutsideRenderPassCommandBuffer::SupportsQueries(mPhysicalDeviceFeatures) && |
| vk::RenderPassCommandBuffer::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; |
| |
| // GL_KHR_blend_equation_advanced. According to the spec, only color attachment zero can be |
| // used with advanced blend: |
| // |
| // > Advanced blending equations are supported only when rendering to a single |
| // > color buffer using fragment color zero. |
| // |
| // Vulkan requires advancedBlendMaxColorAttachments to be at least one, so we can support |
| // advanced blend as long as the Vulkan extension is supported. Otherwise, the extension is |
| // emulated where possible. |
| mNativeExtensions.blendEquationAdvancedKHR = mFeatures.supportsBlendOperationAdvanced.enabled || |
| mFeatures.emulateAdvancedBlendEquations.enabled; |
| |
| // 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; |
| |
| // Enable GL_ANGLE_robust_fragment_shader_output |
| mNativeExtensions.robustFragmentShaderOutputANGLE = true; |
| |
| // 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. |
| // |
| // The GL spec is not as precise as Vulkan's in this regard and that the requirements really |
| // meant to match. This is rectified in the GL spec. |
| // https://gitlab.khronos.org/opengl/API/-/issues/149 |
| mNativeExtensions.shaderMultisampleInterpolationOES = mNativeExtensions.sampleVariablesOES; |
| |
| // Always enable ANGLE_rgbx_internal_format to expose GL_RGBX8_ANGLE. |
| mNativeExtensions.rgbxInternalFormatANGLE = true; |
| |
| // 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]; |
| |
| if (mPhysicalDeviceFeatures.wideLines && mFeatures.bresenhamLineRasterization.enabled) |
| { |
| mNativeCaps.minAliasedLineWidth = std::max(1.0f, limitsVk.lineWidthRange[0]); |
| mNativeCaps.maxAliasedLineWidth = limitsVk.lineWidthRange[1]; |
| } |
| else |
| { |
| 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 = |
| vk_gl::GetMaxSampleCount(limitsVk.sampledImageColorSampleCounts); |
| mNativeCaps.maxDepthTextureSamples = |
| vk_gl::GetMaxSampleCount(limitsVk.sampledImageDepthSampleCounts); |
| mNativeCaps.maxIntegerSamples = |
| vk_gl::GetMaxSampleCount(limitsVk.sampledImageIntegerSampleCounts); |
| |
| 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.setIEEEHalfFloat(); |
| mNativeCaps.vertexLowpFloat.setIEEEHalfFloat(); |
| mNativeCaps.fragmentHighpFloat.setIEEEFloat(); |
| mNativeCaps.fragmentMediumpFloat.setIEEEHalfFloat(); |
| mNativeCaps.fragmentLowpFloat.setIEEEHalfFloat(); |
| |
| // Vulkan doesn't provide such information. We provide the spec-required minimum here. |
| mNativeCaps.vertexHighpInt.setTwosComplementInt(32); |
| mNativeCaps.vertexMediumpInt.setTwosComplementInt(16); |
| mNativeCaps.vertexLowpInt.setTwosComplementInt(16); |
| mNativeCaps.fragmentHighpInt.setTwosComplementInt(32); |
| mNativeCaps.fragmentMediumpInt.setTwosComplementInt(16); |
| mNativeCaps.fragmentLowpInt.setTwosComplementInt(16); |
| |
| // 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; |
| mNativeCaps.maxShaderAtomicCounters[gl::ShaderType::Geometry] = 0; |
| mNativeCaps.maxShaderAtomicCounters[gl::ShaderType::TessControl] = 0; |
| mNativeCaps.maxShaderAtomicCounters[gl::ShaderType::TessEvaluation] = 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 && |
| (!getFeatures().supportsDepthClipControl.enabled || |
| getFeatures().enablePreRotateSurfaces.enabled || |
| getFeatures().emulatedPrerotation90.enabled || |
| getFeatures().emulatedPrerotation180.enabled || |
| getFeatures().emulatedPrerotation270.enabled || |
| !getFeatures().supportsNegativeViewport.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; |
| |
| if (getFeatures().supportsShaderFramebufferFetch.enabled) |
| { |
| // Enable GL_EXT_shader_framebuffer_fetch |
| // gl::IMPLEMENTATION_MAX_DRAW_BUFFERS is used to support the extension. |
| mNativeExtensions.shaderFramebufferFetchEXT = |
| mNativeCaps.maxDrawBuffers >= gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; |
| } |
| |
| if (getFeatures().supportsShaderFramebufferFetchNonCoherent.enabled) |
| { |
| // 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; |
| |
| // GL_ANGLE_read_only_depth_stencil_feedback_loops |
| mNativeExtensions.readOnlyDepthStencilFeedbackLoopsANGLE = 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); |
| |
| // Use "undefined" which means APP would have to set gl_Layer identically. |
| mNativeCaps.layerProvokingVertex = GL_UNDEFINED_VERTEX_EXT; |
| |
| mNativeCaps.maxGeometryInputComponents = |
| LimitToInt(limitsVk.maxGeometryInputComponents) - reservedVaryingComponentCount; |
| mNativeCaps.maxGeometryOutputComponents = |
| LimitToInt(limitsVk.maxGeometryOutputComponents) - reservedVaryingComponentCount; |
| mNativeCaps.maxGeometryOutputVertices = LimitToInt(limitsVk.maxGeometryOutputVertices); |
| mNativeCaps.maxGeometryTotalOutputComponents = |
| LimitToInt(limitsVk.maxGeometryTotalOutputComponents); |
| if (mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics) |
| { |
| 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); |
| |
| if (mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics) |
| { |
| 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 = limitsVk.maxClipDistances; |
| |
| 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_OES_primitive_bounding_box |
| mNativeExtensions.primitiveBoundingBoxOES = true; |
| |
| // GL_EXT_protected_textures |
| mNativeExtensions.protectedTexturesEXT = mFeatures.supportsProtectedMemory.enabled; |
| |
| // GL_ANGLE_vulkan_image |
| mNativeExtensions.vulkanImageANGLE = true; |
| |
| // GL_ANGLE_texture_usage |
| mNativeExtensions.textureUsageANGLE = true; |
| |
| // GL_KHR_parallel_shader_compile |
| mNativeExtensions.parallelShaderCompileKHR = false; |
| |
| // GL_QCOM_shading_rate |
| mNativeExtensions.shadingRateQCOM = mFeatures.supportsFragmentShadingRate.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)); |
| } |
| |
| EGLint GetMatchFormat(GLenum internalFormat) |
| { |
| // Lock Surface match format |
| switch (internalFormat) |
| { |
| case GL_RGBA8: |
| return EGL_FORMAT_RGBA_8888_KHR; |
| case GL_BGRA8_EXT: |
| return EGL_FORMAT_RGBA_8888_EXACT_KHR; |
| case GL_RGB565: |
| return EGL_FORMAT_RGB_565_EXACT_KHR; |
| default: |
| return EGL_NONE; |
| } |
| } |
| |
| // 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; |
| if (display->getExtensions().mutableRenderBufferKHR) |
| { |
| config.surfaceType |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR; |
| } |
| // 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); |
| // LockSurface matching |
| config.matchFormat = GetMatchFormat(colorFormat.internalFormat); |
| if (config.matchFormat != EGL_NONE) |
| { |
| config.surfaceType |= EGL_LOCK_SURFACE_BIT_KHR; |
| } |
| |
| // 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 |