blob: bd7b5e93b336faa7421b75142d024bc33422ba3a [file] [log] [blame]
//
// 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