blob: dbdb9c686d92c68372c6e6eedf3298bc07b65ec2 [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/utilities.h"
#include "libANGLE/Caps.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "vk_format_utils.h"
namespace
{
constexpr unsigned int kComponentsPerVector = 4;
} // anonymous namespace
namespace rx
{
namespace vk
{
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps,
gl::Extensions *outExtensions,
gl::Limitations * /* outLimitations */)
{
outExtensions->setTextureExtensionSupport(textureCaps);
// Enable this for simple buffer readback testing, but some functionality is missing.
// TODO(jmadill): Support full mapBufferRange extension.
outExtensions->mapBuffer = true;
outExtensions->mapBufferRange = true;
outExtensions->textureStorage = true;
outExtensions->framebufferBlit = true;
outExtensions->copyTexture = true;
outExtensions->debugMarker = true;
outExtensions->robustness = true;
outExtensions->textureBorderClamp = false; // not implemented yet
outExtensions->translatedShaderSource = true;
// Only expose robust buffer access if the physical device supports it.
outExtensions->robustBufferAccessBehavior = physicalDeviceFeatures.robustBufferAccess;
outExtensions->eglSync = true;
// We use secondary command buffers almost everywhere and they require a feature to be
// able to execute in the presence of queries. As a result, we won't support queries
// unless that feature is available.
outExtensions->occlusionQueryBoolean = physicalDeviceFeatures.inheritedQueries;
// 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.
outExtensions->disjointTimerQuery = queueFamilyProperties.timestampValidBits > 0;
outExtensions->queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
outExtensions->queryCounterBitsTimestamp = queueFamilyProperties.timestampValidBits;
outExtensions->textureFilterAnisotropic =
physicalDeviceFeatures.samplerAnisotropy &&
physicalDeviceProperties.limits.maxSamplerAnisotropy > 1.0f;
outExtensions->maxTextureAnisotropy = outExtensions->textureFilterAnisotropic
? physicalDeviceProperties.limits.maxSamplerAnisotropy
: 0.0f;
// TODO(lucferron): Eventually remove everything above this line in this function as the caps
// get implemented.
// https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
outCaps->maxElementIndex = std::numeric_limits<GLuint>::max() - 1;
outCaps->max3DTextureSize = physicalDeviceProperties.limits.maxImageDimension3D;
outCaps->max2DTextureSize = physicalDeviceProperties.limits.maxImageDimension2D;
outCaps->maxArrayTextureLayers = physicalDeviceProperties.limits.maxImageArrayLayers;
outCaps->maxLODBias = physicalDeviceProperties.limits.maxSamplerLodBias;
outCaps->maxCubeMapTextureSize = physicalDeviceProperties.limits.maxImageDimensionCube;
outCaps->maxRenderbufferSize = outCaps->max2DTextureSize;
outCaps->minAliasedPointSize =
std::max(1.0f, physicalDeviceProperties.limits.pointSizeRange[0]);
outCaps->maxAliasedPointSize = physicalDeviceProperties.limits.pointSizeRange[1];
outCaps->minAliasedLineWidth = 1.0f;
outCaps->maxAliasedLineWidth = 1.0f;
outCaps->maxDrawBuffers =
std::min<uint32_t>(physicalDeviceProperties.limits.maxColorAttachments,
physicalDeviceProperties.limits.maxFragmentOutputAttachments);
outCaps->maxFramebufferWidth = physicalDeviceProperties.limits.maxFramebufferWidth;
outCaps->maxFramebufferHeight = physicalDeviceProperties.limits.maxFramebufferHeight;
outCaps->maxColorAttachments = physicalDeviceProperties.limits.maxColorAttachments;
outCaps->maxViewportWidth = physicalDeviceProperties.limits.maxViewportDimensions[0];
outCaps->maxViewportHeight = physicalDeviceProperties.limits.maxViewportDimensions[1];
outCaps->maxSampleMaskWords = physicalDeviceProperties.limits.maxSampleMaskWords;
outCaps->maxColorTextureSamples = physicalDeviceProperties.limits.sampledImageColorSampleCounts;
outCaps->maxDepthTextureSamples = physicalDeviceProperties.limits.sampledImageDepthSampleCounts;
outCaps->maxIntegerSamples = physicalDeviceProperties.limits.sampledImageIntegerSampleCounts;
outCaps->maxVertexAttributes = physicalDeviceProperties.limits.maxVertexInputAttributes;
outCaps->maxVertexAttribBindings = physicalDeviceProperties.limits.maxVertexInputBindings;
outCaps->maxVertexAttribRelativeOffset =
physicalDeviceProperties.limits.maxVertexInputAttributeOffset;
outCaps->maxVertexAttribStride = physicalDeviceProperties.limits.maxVertexInputBindingStride;
outCaps->maxElementsIndices = std::numeric_limits<GLuint>::max();
outCaps->maxElementsVertices = std::numeric_limits<GLuint>::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
outCaps->vertexHighpFloat.setIEEEFloat();
outCaps->vertexMediumpFloat.setIEEEFloat();
outCaps->vertexLowpFloat.setIEEEFloat();
outCaps->fragmentHighpFloat.setIEEEFloat();
outCaps->fragmentMediumpFloat.setIEEEFloat();
outCaps->fragmentLowpFloat.setIEEEFloat();
// Can't find documentation on the int precision in Vulkan.
outCaps->vertexHighpInt.setTwosComplementInt(32);
outCaps->vertexMediumpInt.setTwosComplementInt(32);
outCaps->vertexLowpInt.setTwosComplementInt(32);
outCaps->fragmentHighpInt.setTwosComplementInt(32);
outCaps->fragmentMediumpInt.setTwosComplementInt(32);
outCaps->fragmentLowpInt.setTwosComplementInt(32);
// TODO(lucferron): This is something we'll need to implement custom in the back-end.
// Vulkan doesn't do any waiting for you, our back-end code is going to manage sync objects,
// and we'll have to check that we've exceeded the max wait timeout. Also, this is ES 3.0 so
// we'll defer the implementation until we tackle the next version.
// outCaps->maxServerWaitTimeout
GLuint maxUniformVectors = physicalDeviceProperties.limits.maxUniformBufferRange /
(sizeof(GLfloat) * kComponentsPerVector);
// Clamp the maxUniformVectors to 1024u, on AMD the maxUniformBufferRange is way too high.
maxUniformVectors = std::min(1024u, maxUniformVectors);
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).
outCaps->maxVertexUniformVectors = maxUniformVectors;
outCaps->maxShaderUniformComponents[gl::ShaderType::Vertex] = maxUniformComponents;
outCaps->maxFragmentUniformVectors = maxUniformVectors;
outCaps->maxShaderUniformComponents[gl::ShaderType::Fragment] = maxUniformComponents;
// TODO(jmadill): this is an ES 3.0 property and we can skip implementing it for now.
// This is maxDescriptorSetUniformBuffers minus the number of uniform buffers we
// reserve for internal variables. We reserve one per shader stage for default uniforms
// and likely one per shader stage for ANGLE internal variables.
// outCaps->maxShaderUniformBlocks[gl::ShaderType::Vertex] = ...
// we use the same bindings on each stage, so the limitation is the same combined or not.
outCaps->maxCombinedTextureImageUnits =
physicalDeviceProperties.limits.maxPerStageDescriptorSamplers;
outCaps->maxShaderTextureImageUnits[gl::ShaderType::Fragment] =
physicalDeviceProperties.limits.maxPerStageDescriptorSamplers;
outCaps->maxShaderTextureImageUnits[gl::ShaderType::Vertex] =
physicalDeviceProperties.limits.maxPerStageDescriptorSamplers;
// The max vertex output components 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.
// TODO(lucferron): AMD has a weird behavior when we edge toward the maximum number of varyings
// and can often crash. Reserving an additional varying just for them bringing the total to 2.
// http://anglebug.com/2483
constexpr GLint kReservedVaryingCount = 2;
outCaps->maxVaryingVectors =
(physicalDeviceProperties.limits.maxVertexOutputComponents / 4) - kReservedVaryingCount;
outCaps->maxVertexOutputComponents = outCaps->maxVaryingVectors * 4;
}
} // namespace vk
namespace egl_vk
{
namespace
{
EGLint ComputeMaximumPBufferPixels(const VkPhysicalDeviceProperties &physicalDeviceProperties)
{
// EGLints are signed 32-bit integers, it's fairly easy to overflow them, especially since
// Vulkan's minimum guaranteed VkImageFormatProperties::maxResourceSize is 2^31 bytes.
constexpr uint64_t kMaxValueForEGLint =
static_cast<uint64_t>(std::numeric_limits<EGLint>::max());
// TODO(geofflang): Compute the maximum size of a pbuffer by using the maxResourceSize result
// from vkGetPhysicalDeviceImageFormatProperties for both the color and depth stencil format and
// the exact image creation parameters that would be used to create the pbuffer. Because it is
// always safe to return out-of-memory errors on pbuffer allocation, it's fine to simply return
// the number of pixels in a max width by max height pbuffer for now. http://anglebug.com/2622
// Storing the result of squaring a 32-bit unsigned int in a 64-bit unsigned int is safe.
static_assert(std::is_same<decltype(physicalDeviceProperties.limits.maxImageDimension2D),
uint32_t>::value,
"physicalDeviceProperties.limits.maxImageDimension2D expected to be a uint32_t.");
const uint64_t maxDimensionsSquared =
static_cast<uint64_t>(physicalDeviceProperties.limits.maxImageDimension2D) *
static_cast<uint64_t>(physicalDeviceProperties.limits.maxImageDimension2D);
return static_cast<EGLint>(std::min(maxDimensionsSquared, kMaxValueForEGLint));
}
// Generates a basic config for a combination of color format, depth stencil format and sample
// count.
egl::Config GenerateDefaultConfig(const RendererVk *renderer,
const gl::InternalFormat &colorFormat,
const gl::InternalFormat &depthStencilFormat,
EGLint sampleCount)
{
const VkPhysicalDeviceProperties &physicalDeviceProperties =
renderer->getPhysicalDeviceProperties();
gl::Version maxSupportedESVersion = renderer->getMaxSupportedESVersion();
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 = EGL_FALSE;
config.bindToTextureRGBA = EGL_FALSE;
config.colorBufferType = EGL_RGB_BUFFER;
config.configCaveat = EGL_NONE;
config.conformant = 0;
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 = 1;
config.nativeRenderable = EGL_TRUE;
config.nativeVisualID = 0;
config.nativeVisualType = EGL_NONE;
config.renderableType = es2Support | es3Support;
config.sampleBuffers = (sampleCount > 0) ? 1 : 0;
config.samples = sampleCount;
config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
// Vulkan surfaces use a different origin than OpenGL, always prefer to be flipped vertically if
// possible.
config.optimalOrientation = EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE;
config.transparentType = EGL_NONE;
config.transparentRedValue = 0;
config.transparentGreenValue = 0;
config.transparentBlueValue = 0;
config.colorComponentType =
gl_egl::GLComponentTypeToEGLColorComponentType(colorFormat.componentType);
return config;
}
} // anonymous namespace
egl::ConfigSet GenerateConfigs(const GLenum *colorFormats,
size_t colorFormatsCount,
const GLenum *depthStencilFormats,
size_t depthStencilFormatCount,
const EGLint *sampleCounts,
size_t sampleCountsCount,
DisplayVk *display)
{
ASSERT(colorFormatsCount > 0);
ASSERT(display != nullptr);
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);
for (size_t sampleCountIndex = 0; sampleCountIndex < sampleCountsCount;
sampleCountIndex++)
{
egl::Config config =
GenerateDefaultConfig(display->getRenderer(), colorFormatInfo,
depthStencilFormatInfo, sampleCounts[sampleCountIndex]);
if (display->checkConfigSupport(&config))
{
configSet.add(config);
}
}
}
}
return configSet;
}
} // namespace egl_vk
} // namespace rx