2590 lines
100 KiB
C++
2590 lines
100 KiB
C++
//
|
|
// Copyright 2016 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.
|
|
//
|
|
// RendererVk.cpp:
|
|
// Implements the class methods for RendererVk.
|
|
//
|
|
|
|
#include "libANGLE/renderer/vulkan/RendererVk.h"
|
|
|
|
// Placing this first seems to solve an intellisense bug.
|
|
#include "libANGLE/renderer/vulkan/vk_utils.h"
|
|
|
|
#include <EGL/eglext.h>
|
|
|
|
#include "common/debug.h"
|
|
#include "common/platform.h"
|
|
#include "common/system_utils.h"
|
|
#include "common/vulkan/vk_google_filtering_precision.h"
|
|
#include "common/vulkan/vulkan_icd.h"
|
|
#include "gpu_info_util/SystemInfo.h"
|
|
#include "libANGLE/Context.h"
|
|
#include "libANGLE/Display.h"
|
|
#include "libANGLE/renderer/driver_utils.h"
|
|
#include "libANGLE/renderer/glslang_wrapper_utils.h"
|
|
#include "libANGLE/renderer/vulkan/CompilerVk.h"
|
|
#include "libANGLE/renderer/vulkan/ContextVk.h"
|
|
#include "libANGLE/renderer/vulkan/DisplayVk.h"
|
|
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
|
|
#include "libANGLE/renderer/vulkan/ProgramVk.h"
|
|
#include "libANGLE/renderer/vulkan/ResourceVk.h"
|
|
#include "libANGLE/renderer/vulkan/VertexArrayVk.h"
|
|
#include "libANGLE/renderer/vulkan/vk_caps_utils.h"
|
|
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
|
|
#include "libANGLE/trace.h"
|
|
#include "platform/PlatformMethods.h"
|
|
|
|
// Consts
|
|
namespace
|
|
{
|
|
constexpr VkFormatFeatureFlags kInvalidFormatFeatureFlags = static_cast<VkFormatFeatureFlags>(-1);
|
|
} // anonymous namespace
|
|
|
|
namespace rx
|
|
{
|
|
|
|
namespace
|
|
{
|
|
constexpr uint32_t kMinDefaultUniformBufferSize = 16 * 1024u;
|
|
// This size is picked based on experience. Majority of devices support 64K
|
|
// maxUniformBufferSize. Since this is per context buffer, a bigger buffer size reduces the
|
|
// number of descriptor set allocations, so we picked the maxUniformBufferSize that most
|
|
// devices supports. It may needs further tuning based on specific device needs and balance
|
|
// between performance and memory usage.
|
|
constexpr uint32_t kPreferredDefaultUniformBufferSize = 64 * 1024u;
|
|
|
|
// Update the pipeline cache every this many swaps.
|
|
constexpr uint32_t kPipelineCacheVkUpdatePeriod = 60;
|
|
// Per the Vulkan specification, as long as Vulkan 1.1+ is returned by vkEnumerateInstanceVersion,
|
|
// ANGLE must indicate the highest version of Vulkan functionality that it uses. The Vulkan
|
|
// validation layers will issue messages for any core functionality that requires a higher version.
|
|
// This value must be increased whenever ANGLE starts using functionality from a newer core
|
|
// version of Vulkan.
|
|
constexpr uint32_t kPreferredVulkanAPIVersion = VK_API_VERSION_1_1;
|
|
|
|
constexpr bool kOutputVmaStatsString = false;
|
|
|
|
angle::vk::ICD ChooseICDFromAttribs(const egl::AttributeMap &attribs)
|
|
{
|
|
#if !defined(ANGLE_PLATFORM_ANDROID)
|
|
// Mock ICD does not currently run on Android
|
|
EGLAttrib deviceType = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
|
|
EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE);
|
|
|
|
switch (deviceType)
|
|
{
|
|
case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE:
|
|
break;
|
|
case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
|
|
return angle::vk::ICD::Mock;
|
|
case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
|
|
return angle::vk::ICD::SwiftShader;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
#endif // !defined(ANGLE_PLATFORM_ANDROID)
|
|
|
|
return angle::vk::ICD::Default;
|
|
}
|
|
|
|
bool StrLess(const char *a, const char *b)
|
|
{
|
|
return strcmp(a, b) < 0;
|
|
}
|
|
|
|
bool ExtensionFound(const char *needle, const vk::ExtensionNameList &haystack)
|
|
{
|
|
// NOTE: The list must be sorted.
|
|
return std::binary_search(haystack.begin(), haystack.end(), needle, StrLess);
|
|
}
|
|
|
|
VkResult VerifyExtensionsPresent(const vk::ExtensionNameList &haystack,
|
|
const vk::ExtensionNameList &needles)
|
|
{
|
|
// NOTE: The lists must be sorted.
|
|
if (std::includes(haystack.begin(), haystack.end(), needles.begin(), needles.end(), StrLess))
|
|
{
|
|
return VK_SUCCESS;
|
|
}
|
|
for (const char *needle : needles)
|
|
{
|
|
if (!ExtensionFound(needle, haystack))
|
|
{
|
|
ERR() << "Extension not supported: " << needle;
|
|
}
|
|
}
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
|
|
// Array of Validation error/warning messages that will be ignored, should include bugID
|
|
constexpr const char *kSkippedMessages[] = {
|
|
// http://anglebug.com/2866
|
|
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
|
// http://anglebug.com/4883
|
|
"UNASSIGNED-CoreValidation-Shader-InputNotProduced",
|
|
// http://anglebug.com/2796
|
|
"UNASSIGNED-CoreValidation-Shader-PointSizeMissing",
|
|
// http://anglebug.com/3832
|
|
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428",
|
|
// http://anglebug.com/4063
|
|
"VUID-VkDeviceCreateInfo-pNext-pNext",
|
|
"VUID-VkPipelineRasterizationStateCreateInfo-pNext-pNext", "VUID_Undefined",
|
|
// https://issuetracker.google.com/issues/159493191
|
|
"VUID-vkCmdDraw-None-02690", "VUID-vkCmdDrawIndexed-None-02690",
|
|
// http://anglebug.com/4975
|
|
"VUID-vkCmdDraw-None-02687", "VUID-vkCmdDrawIndexed-None-02687",
|
|
// Best Practices Skips https://issuetracker.google.com/issues/166641492
|
|
// https://issuetracker.google.com/issues/166793850
|
|
"UNASSIGNED-BestPractices-vkCreateCommandPool-command-buffer-reset",
|
|
"UNASSIGNED-BestPractices-pipeline-stage-flags", "UNASSIGNED-BestPractices-Error-Result",
|
|
"UNASSIGNED-BestPractices-vkAllocateMemory-small-allocation",
|
|
"UNASSIGNED-BestPractices-vkBindMemory-small-dedicated-allocation",
|
|
"UNASSIGNED-BestPractices-vkAllocateMemory-too-many-objects",
|
|
"UNASSIGNED-BestPractices-vkCreateDevice-deprecated-extension",
|
|
"UNASSIGNED-BestPractices-vkCreateRenderPass-image-requires-memory",
|
|
"UNASSIGNED-BestPractices-vkCreateGraphicsPipelines-too-many-instanced-vertex-buffers",
|
|
"UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw",
|
|
"UNASSIGNED-BestPractices-vkCmdClearAttachments-clear-after-load",
|
|
// http://anglebug.com/4928
|
|
"VUID-vkMapMemory-memory-00683",
|
|
// http://anglebug.com/5027
|
|
"UNASSIGNED-CoreValidation-Shader-PushConstantOutOfRange",
|
|
// http://anglebug.com/5304
|
|
"VUID-vkCmdDraw-magFilter-04553", "VUID-vkCmdDrawIndexed-magFilter-04553",
|
|
// http://anglebug.com/5309
|
|
"VUID-VkImageViewCreateInfo-usage-02652",
|
|
// http://anglebug.com/5336
|
|
"UNASSIGNED-BestPractices-vkCreateDevice-specialuse-extension"};
|
|
|
|
// Suppress validation errors that are known
|
|
// return "true" if given code/prefix/message is known, else return "false"
|
|
bool IsIgnoredDebugMessage(const char *message)
|
|
{
|
|
if (!message)
|
|
{
|
|
return false;
|
|
}
|
|
for (const char *msg : kSkippedMessages)
|
|
{
|
|
if (strstr(message, msg) != nullptr)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *GetVkObjectTypeName(VkObjectType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case VK_OBJECT_TYPE_UNKNOWN:
|
|
return "Unknown";
|
|
case VK_OBJECT_TYPE_INSTANCE:
|
|
return "Instance";
|
|
case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
|
|
return "Physical Device";
|
|
case VK_OBJECT_TYPE_DEVICE:
|
|
return "Device";
|
|
case VK_OBJECT_TYPE_QUEUE:
|
|
return "Queue";
|
|
case VK_OBJECT_TYPE_SEMAPHORE:
|
|
return "Semaphore";
|
|
case VK_OBJECT_TYPE_COMMAND_BUFFER:
|
|
return "Command Buffer";
|
|
case VK_OBJECT_TYPE_FENCE:
|
|
return "Fence";
|
|
case VK_OBJECT_TYPE_DEVICE_MEMORY:
|
|
return "Device Memory";
|
|
case VK_OBJECT_TYPE_BUFFER:
|
|
return "Buffer";
|
|
case VK_OBJECT_TYPE_IMAGE:
|
|
return "Image";
|
|
case VK_OBJECT_TYPE_EVENT:
|
|
return "Event";
|
|
case VK_OBJECT_TYPE_QUERY_POOL:
|
|
return "Query Pool";
|
|
case VK_OBJECT_TYPE_BUFFER_VIEW:
|
|
return "Buffer View";
|
|
case VK_OBJECT_TYPE_IMAGE_VIEW:
|
|
return "Image View";
|
|
case VK_OBJECT_TYPE_SHADER_MODULE:
|
|
return "Shader Module";
|
|
case VK_OBJECT_TYPE_PIPELINE_CACHE:
|
|
return "Pipeline Cache";
|
|
case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
|
|
return "Pipeline Layout";
|
|
case VK_OBJECT_TYPE_RENDER_PASS:
|
|
return "Render Pass";
|
|
case VK_OBJECT_TYPE_PIPELINE:
|
|
return "Pipeline";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:
|
|
return "Descriptor Set Layout";
|
|
case VK_OBJECT_TYPE_SAMPLER:
|
|
return "Sampler";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_POOL:
|
|
return "Descriptor Pool";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_SET:
|
|
return "Descriptor Set";
|
|
case VK_OBJECT_TYPE_FRAMEBUFFER:
|
|
return "Framebuffer";
|
|
case VK_OBJECT_TYPE_COMMAND_POOL:
|
|
return "Command Pool";
|
|
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
|
|
return "Sampler YCbCr Conversion";
|
|
case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:
|
|
return "Descriptor Update Template";
|
|
case VK_OBJECT_TYPE_SURFACE_KHR:
|
|
return "Surface";
|
|
case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
|
|
return "Swapchain";
|
|
case VK_OBJECT_TYPE_DISPLAY_KHR:
|
|
return "Display";
|
|
case VK_OBJECT_TYPE_DISPLAY_MODE_KHR:
|
|
return "Display Mode";
|
|
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
|
|
return "Debug Report Callback";
|
|
case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV:
|
|
return "Indirect Commands Layout";
|
|
case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:
|
|
return "Debug Utils Messenger";
|
|
case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:
|
|
return "Validation Cache";
|
|
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:
|
|
return "Acceleration Structure";
|
|
default:
|
|
return "<Unrecognized>";
|
|
}
|
|
}
|
|
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL
|
|
DebugUtilsMessenger(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
const VkDebugUtilsMessengerCallbackDataEXT *callbackData,
|
|
void *userData)
|
|
{
|
|
// See if it's an issue we are aware of and don't want to be spammed about.
|
|
if (IsIgnoredDebugMessage(callbackData->pMessageIdName))
|
|
{
|
|
return VK_FALSE;
|
|
}
|
|
|
|
std::ostringstream log;
|
|
if (callbackData->pMessageIdName)
|
|
{
|
|
log << "[ " << callbackData->pMessageIdName << " ] ";
|
|
}
|
|
log << callbackData->pMessage << std::endl;
|
|
|
|
// Aesthetic value based on length of the function name, line number, etc.
|
|
constexpr size_t kStartIndent = 28;
|
|
|
|
// Output the debug marker hierarchy under which this error has occured.
|
|
size_t indent = kStartIndent;
|
|
if (callbackData->queueLabelCount > 0)
|
|
{
|
|
log << std::string(indent++, ' ') << "<Queue Label Hierarchy:>" << std::endl;
|
|
for (uint32_t i = 0; i < callbackData->queueLabelCount; ++i)
|
|
{
|
|
log << std::string(indent++, ' ') << callbackData->pQueueLabels[i].pLabelName
|
|
<< std::endl;
|
|
}
|
|
}
|
|
if (callbackData->cmdBufLabelCount > 0)
|
|
{
|
|
log << std::string(indent++, ' ') << "<Command Buffer Label Hierarchy:>" << std::endl;
|
|
for (uint32_t i = 0; i < callbackData->cmdBufLabelCount; ++i)
|
|
{
|
|
log << std::string(indent++, ' ') << callbackData->pCmdBufLabels[i].pLabelName
|
|
<< std::endl;
|
|
}
|
|
}
|
|
// Output the objects involved in this error message.
|
|
if (callbackData->objectCount > 0)
|
|
{
|
|
for (uint32_t i = 0; i < callbackData->objectCount; ++i)
|
|
{
|
|
const char *objectName = callbackData->pObjects[i].pObjectName;
|
|
const char *objectType = GetVkObjectTypeName(callbackData->pObjects[i].objectType);
|
|
uint64_t objectHandle = callbackData->pObjects[i].objectHandle;
|
|
log << std::string(indent, ' ') << "Object: ";
|
|
if (objectHandle == 0)
|
|
{
|
|
log << "VK_NULL_HANDLE";
|
|
}
|
|
else
|
|
{
|
|
log << "0x" << std::hex << objectHandle << std::dec;
|
|
}
|
|
log << " (type = " << objectType << "(" << callbackData->pObjects[i].objectType << "))";
|
|
if (objectName)
|
|
{
|
|
log << " [" << objectName << "]";
|
|
}
|
|
log << std::endl;
|
|
}
|
|
}
|
|
|
|
bool isError = (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0;
|
|
std::string msg = log.str();
|
|
|
|
RendererVk *rendererVk = static_cast<RendererVk *>(userData);
|
|
rendererVk->onNewValidationMessage(msg);
|
|
|
|
if (isError)
|
|
{
|
|
ERR() << msg;
|
|
}
|
|
else
|
|
{
|
|
WARN() << msg;
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags,
|
|
VkDebugReportObjectTypeEXT objectType,
|
|
uint64_t object,
|
|
size_t location,
|
|
int32_t messageCode,
|
|
const char *layerPrefix,
|
|
const char *message,
|
|
void *userData)
|
|
{
|
|
if (IsIgnoredDebugMessage(message))
|
|
{
|
|
return VK_FALSE;
|
|
}
|
|
if ((flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) != 0)
|
|
{
|
|
ERR() << message;
|
|
#if !defined(NDEBUG)
|
|
// Abort the call in Debug builds.
|
|
return VK_TRUE;
|
|
#endif
|
|
}
|
|
else if ((flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) != 0)
|
|
{
|
|
WARN() << message;
|
|
}
|
|
else
|
|
{
|
|
// Uncomment this if you want Vulkan spam.
|
|
// WARN() << message;
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
|
|
bool ShouldUseValidationLayers(const egl::AttributeMap &attribs)
|
|
{
|
|
#if defined(ANGLE_ENABLE_VULKAN_VALIDATION_LAYERS_BY_DEFAULT)
|
|
return ShouldUseDebugLayers(attribs);
|
|
#else
|
|
EGLAttrib debugSetting =
|
|
attribs.get(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, EGL_DONT_CARE);
|
|
return debugSetting == EGL_TRUE;
|
|
#endif // defined(ANGLE_ENABLE_VULKAN_VALIDATION_LAYERS_BY_DEFAULT)
|
|
}
|
|
|
|
gl::Version LimitVersionTo(const gl::Version ¤t, const gl::Version &lower)
|
|
{
|
|
return std::min(current, lower);
|
|
}
|
|
|
|
ANGLE_MAYBE_UNUSED bool FencePropertiesCompatibleWithAndroid(
|
|
const VkExternalFenceProperties &externalFenceProperties)
|
|
{
|
|
// handleType here is the external fence type -
|
|
// we want type compatible with creating and export/dup() Android FD
|
|
|
|
// Imported handleType that can be exported - need for vkGetFenceFdKHR()
|
|
if ((externalFenceProperties.exportFromImportedHandleTypes &
|
|
VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// HandleTypes which can be specified at creating a fence
|
|
if ((externalFenceProperties.compatibleHandleTypes &
|
|
VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
constexpr VkExternalFenceFeatureFlags kFeatureFlags =
|
|
(VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR |
|
|
VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR);
|
|
if ((externalFenceProperties.externalFenceFeatures & kFeatureFlags) != kFeatureFlags)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ANGLE_MAYBE_UNUSED bool SemaphorePropertiesCompatibleWithAndroid(
|
|
const VkExternalSemaphoreProperties &externalSemaphoreProperties)
|
|
{
|
|
// handleType here is the external semaphore type -
|
|
// we want type compatible with importing an Android FD
|
|
|
|
constexpr VkExternalSemaphoreFeatureFlags kFeatureFlags =
|
|
(VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR);
|
|
if ((externalSemaphoreProperties.externalSemaphoreFeatures & kFeatureFlags) != kFeatureFlags)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Environment variable (and associated Android property) to enable Vulkan debug-utils markers
|
|
constexpr char kEnableDebugMarkersVarName[] = "ANGLE_ENABLE_DEBUG_MARKERS";
|
|
constexpr char kEnableDebugMarkersPropertyName[] = "debug.angle.markers";
|
|
} // namespace
|
|
|
|
// RendererVk implementation.
|
|
RendererVk::RendererVk()
|
|
: mDisplay(nullptr),
|
|
mCapsInitialized(false),
|
|
mInstance(VK_NULL_HANDLE),
|
|
mEnableValidationLayers(false),
|
|
mEnableDebugUtils(false),
|
|
mEnabledICD(angle::vk::ICD::Default),
|
|
mDebugUtilsMessenger(VK_NULL_HANDLE),
|
|
mDebugReportCallback(VK_NULL_HANDLE),
|
|
mPhysicalDevice(VK_NULL_HANDLE),
|
|
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
|
|
mMaxVertexAttribDivisor(1),
|
|
mMaxVertexAttribStride(0),
|
|
mMinImportedHostPointerAlignment(1),
|
|
mDefaultUniformBufferSize(kPreferredDefaultUniformBufferSize),
|
|
mDevice(VK_NULL_HANDLE),
|
|
mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
|
|
mCurrentQueueSerial(mQueueSerialFactory.generate()),
|
|
mDeviceLost(false),
|
|
mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod),
|
|
mPipelineCacheDirty(false),
|
|
mPipelineCacheInitialized(false),
|
|
mCommandProcessor(this),
|
|
mGlslangInitialized(false)
|
|
{
|
|
VkFormatProperties invalid = {0, 0, kInvalidFormatFeatureFlags};
|
|
mFormatProperties.fill(invalid);
|
|
|
|
// We currently don't have any big-endian devices in the list of supported platforms. There are
|
|
// a number of places in the Vulkan backend that make this assumption. This assertion is made
|
|
// early to fail immediately on big-endian platforms.
|
|
ASSERT(IsLittleEndian());
|
|
}
|
|
|
|
RendererVk::~RendererVk()
|
|
{
|
|
mAllocator.release();
|
|
mPipelineCache.release();
|
|
ASSERT(!hasSharedGarbage());
|
|
}
|
|
|
|
bool RendererVk::hasSharedGarbage()
|
|
{
|
|
std::lock_guard<std::mutex> lock(mGarbageMutex);
|
|
return !mSharedGarbage.empty();
|
|
}
|
|
|
|
void RendererVk::releaseSharedResources(vk::ResourceUseList *resourceList)
|
|
{
|
|
// resource list may access same resources referenced by garbage collection so need to protect
|
|
// that access with a lock.
|
|
std::lock_guard<std::mutex> lock(mGarbageMutex);
|
|
resourceList->releaseResourceUses();
|
|
}
|
|
|
|
void RendererVk::onDestroy()
|
|
{
|
|
if (getFeatures().commandProcessor.enabled)
|
|
{
|
|
// Shutdown worker thread
|
|
mCommandProcessor.shutdown(&mCommandProcessorThread);
|
|
}
|
|
|
|
// Force all commands to finish by flushing all queues.
|
|
for (VkQueue queue : mQueues)
|
|
{
|
|
if (queue != VK_NULL_HANDLE)
|
|
{
|
|
vkQueueWaitIdle(queue);
|
|
}
|
|
}
|
|
|
|
// Then assign an infinite "last completed" serial to force garbage to delete.
|
|
{
|
|
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
|
|
mLastCompletedQueueSerial = Serial::Infinite();
|
|
}
|
|
|
|
(void)cleanupGarbage(true);
|
|
ASSERT(!hasSharedGarbage());
|
|
|
|
for (PendingOneOffCommands &pending : mPendingOneOffCommands)
|
|
{
|
|
pending.commandBuffer.releaseHandle();
|
|
}
|
|
|
|
mOneOffCommandPool.destroy(mDevice);
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(mFenceRecyclerMutex);
|
|
mFenceRecycler.destroy(mDevice);
|
|
}
|
|
{
|
|
std::lock_guard<decltype(mNextSubmitFenceMutex)> lock(mNextSubmitFenceMutex);
|
|
mNextSubmitFence.reset(mDevice);
|
|
}
|
|
|
|
mPipelineCache.destroy(mDevice);
|
|
mSamplerCache.destroy(this);
|
|
mYuvConversionCache.destroy(this);
|
|
|
|
mAllocator.destroy();
|
|
|
|
if (mGlslangInitialized)
|
|
{
|
|
GlslangRelease();
|
|
mGlslangInitialized = false;
|
|
}
|
|
|
|
if (mDevice)
|
|
{
|
|
vkDestroyDevice(mDevice, nullptr);
|
|
mDevice = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (mDebugUtilsMessenger)
|
|
{
|
|
vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugUtilsMessenger, nullptr);
|
|
|
|
ASSERT(mDebugReportCallback == VK_NULL_HANDLE);
|
|
}
|
|
else if (mDebugReportCallback)
|
|
{
|
|
vkDestroyDebugReportCallbackEXT(mInstance, mDebugReportCallback, nullptr);
|
|
}
|
|
|
|
if (mInstance)
|
|
{
|
|
vkDestroyInstance(mInstance, nullptr);
|
|
mInstance = VK_NULL_HANDLE;
|
|
}
|
|
|
|
mMemoryProperties.destroy();
|
|
mPhysicalDevice = VK_NULL_HANDLE;
|
|
}
|
|
|
|
void RendererVk::notifyDeviceLost()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
|
|
mLastCompletedQueueSerial = getLastSubmittedQueueSerial();
|
|
}
|
|
mDeviceLost = true;
|
|
mDisplay->notifyDeviceLost();
|
|
}
|
|
|
|
bool RendererVk::isDeviceLost() const
|
|
{
|
|
return mDeviceLost;
|
|
}
|
|
|
|
angle::Result RendererVk::initialize(DisplayVk *displayVk,
|
|
egl::Display *display,
|
|
const char *wsiExtension,
|
|
const char *wsiLayer)
|
|
{
|
|
#if defined(ANGLE_SHARED_LIBVULKAN)
|
|
// Set all vk* function ptrs
|
|
ANGLE_VK_TRY(displayVk, volkInitialize());
|
|
#endif // defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
mDisplay = display;
|
|
const egl::AttributeMap &attribs = mDisplay->getAttributeMap();
|
|
angle::vk::ScopedVkLoaderEnvironment scopedEnvironment(ShouldUseValidationLayers(attribs),
|
|
ChooseICDFromAttribs(attribs));
|
|
mEnableValidationLayers = scopedEnvironment.canEnableValidationLayers();
|
|
mEnabledICD = scopedEnvironment.getEnabledICD();
|
|
|
|
// Gather global layer properties.
|
|
uint32_t instanceLayerCount = 0;
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
|
|
|
|
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerCount);
|
|
if (instanceLayerCount > 0)
|
|
{
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount,
|
|
instanceLayerProps.data()));
|
|
}
|
|
|
|
VulkanLayerVector enabledInstanceLayerNames;
|
|
if (mEnableValidationLayers)
|
|
{
|
|
bool layersRequested =
|
|
(attribs.get(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, EGL_DONT_CARE) == EGL_TRUE);
|
|
mEnableValidationLayers = GetAvailableValidationLayers(instanceLayerProps, layersRequested,
|
|
&enabledInstanceLayerNames);
|
|
}
|
|
|
|
if (wsiLayer)
|
|
{
|
|
enabledInstanceLayerNames.push_back(wsiLayer);
|
|
}
|
|
|
|
// Enumerate instance extensions that are provided by the vulkan
|
|
// implementation and implicit layers.
|
|
uint32_t instanceExtensionCount = 0;
|
|
ANGLE_VK_TRY(displayVk,
|
|
vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr));
|
|
|
|
std::vector<VkExtensionProperties> instanceExtensionProps(instanceExtensionCount);
|
|
if (instanceExtensionCount > 0)
|
|
{
|
|
ANGLE_VK_TRY(displayVk,
|
|
vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount,
|
|
instanceExtensionProps.data()));
|
|
}
|
|
|
|
// Enumerate instance extensions that are provided by explicit layers.
|
|
for (const char *layerName : enabledInstanceLayerNames)
|
|
{
|
|
uint32_t previousExtensionCount = static_cast<uint32_t>(instanceExtensionProps.size());
|
|
uint32_t instanceLayerExtensionCount = 0;
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceExtensionProperties(
|
|
layerName, &instanceLayerExtensionCount, nullptr));
|
|
instanceExtensionProps.resize(previousExtensionCount + instanceLayerExtensionCount);
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceExtensionProperties(
|
|
layerName, &instanceLayerExtensionCount,
|
|
instanceExtensionProps.data() + previousExtensionCount));
|
|
}
|
|
|
|
vk::ExtensionNameList instanceExtensionNames;
|
|
if (!instanceExtensionProps.empty())
|
|
{
|
|
for (const VkExtensionProperties &i : instanceExtensionProps)
|
|
{
|
|
instanceExtensionNames.push_back(i.extensionName);
|
|
}
|
|
std::sort(instanceExtensionNames.begin(), instanceExtensionNames.end(), StrLess);
|
|
}
|
|
|
|
vk::ExtensionNameList enabledInstanceExtensions;
|
|
enabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
|
enabledInstanceExtensions.push_back(wsiExtension);
|
|
mEnableDebugUtils = mEnableValidationLayers &&
|
|
ExtensionFound(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instanceExtensionNames);
|
|
|
|
bool enableDebugReport =
|
|
mEnableValidationLayers && !mEnableDebugUtils &&
|
|
ExtensionFound(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instanceExtensionNames);
|
|
|
|
if (mEnableDebugUtils)
|
|
{
|
|
enabledInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
}
|
|
else if (enableDebugReport)
|
|
{
|
|
enabledInstanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
|
}
|
|
|
|
if (ExtensionFound(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, instanceExtensionNames))
|
|
{
|
|
enabledInstanceExtensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsSwapchainColorspace, true);
|
|
}
|
|
|
|
// Verify the required extensions are in the extension names set. Fail if not.
|
|
std::sort(enabledInstanceExtensions.begin(), enabledInstanceExtensions.end(), StrLess);
|
|
ANGLE_VK_TRY(displayVk,
|
|
VerifyExtensionsPresent(instanceExtensionNames, enabledInstanceExtensions));
|
|
|
|
// Enable VK_KHR_get_physical_device_properties_2 if available.
|
|
if (ExtensionFound(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
|
instanceExtensionNames))
|
|
{
|
|
enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
|
|
VkApplicationInfo applicationInfo = {};
|
|
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
std::string appName = angle::GetExecutableName();
|
|
applicationInfo.pApplicationName = appName.c_str();
|
|
applicationInfo.applicationVersion = 1;
|
|
applicationInfo.pEngineName = "ANGLE";
|
|
applicationInfo.engineVersion = 1;
|
|
|
|
auto enumerateInstanceVersion = reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
|
|
vkGetInstanceProcAddr(mInstance, "vkEnumerateInstanceVersion"));
|
|
if (!enumerateInstanceVersion)
|
|
{
|
|
applicationInfo.apiVersion = VK_API_VERSION_1_0;
|
|
}
|
|
else
|
|
{
|
|
uint32_t apiVersion = VK_API_VERSION_1_0;
|
|
ANGLE_VK_TRY(displayVk, enumerateInstanceVersion(&apiVersion));
|
|
if ((VK_VERSION_MAJOR(apiVersion) > 1) || (VK_VERSION_MINOR(apiVersion) >= 1))
|
|
{
|
|
// This is the highest version of core Vulkan functionality that ANGLE uses.
|
|
applicationInfo.apiVersion = kPreferredVulkanAPIVersion;
|
|
}
|
|
else
|
|
{
|
|
// Since only 1.0 instance-level functionality is available, this must set to 1.0.
|
|
applicationInfo.apiVersion = VK_API_VERSION_1_0;
|
|
}
|
|
}
|
|
|
|
VkInstanceCreateInfo instanceInfo = {};
|
|
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
instanceInfo.flags = 0;
|
|
instanceInfo.pApplicationInfo = &applicationInfo;
|
|
|
|
// Enable requested layers and extensions.
|
|
instanceInfo.enabledExtensionCount = static_cast<uint32_t>(enabledInstanceExtensions.size());
|
|
instanceInfo.ppEnabledExtensionNames =
|
|
enabledInstanceExtensions.empty() ? nullptr : enabledInstanceExtensions.data();
|
|
instanceInfo.enabledLayerCount = static_cast<uint32_t>(enabledInstanceLayerNames.size());
|
|
instanceInfo.ppEnabledLayerNames = enabledInstanceLayerNames.data();
|
|
|
|
VkValidationFeatureEnableEXT enabledFeatures[] = {
|
|
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT};
|
|
VkValidationFeaturesEXT validationFeatures = {};
|
|
validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
|
validationFeatures.enabledValidationFeatureCount = 1;
|
|
validationFeatures.pEnabledValidationFeatures = enabledFeatures;
|
|
|
|
if (mEnableValidationLayers)
|
|
{
|
|
// Enable best practices output which includes perfdoc layer
|
|
vk::AddToPNextChain(&instanceInfo, &validationFeatures);
|
|
}
|
|
|
|
ANGLE_VK_TRY(displayVk, vkCreateInstance(&instanceInfo, nullptr, &mInstance));
|
|
#if defined(ANGLE_SHARED_LIBVULKAN)
|
|
// Load volk if we are linking dynamically
|
|
volkLoadInstance(mInstance);
|
|
#endif // defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
if (mEnableDebugUtils)
|
|
{
|
|
// Use the newer EXT_debug_utils if it exists.
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitDebugUtilsEXTFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
// Create the messenger callback.
|
|
VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {};
|
|
|
|
constexpr VkDebugUtilsMessageSeverityFlagsEXT kSeveritiesToLog =
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
|
|
|
|
constexpr VkDebugUtilsMessageTypeFlagsEXT kMessagesToLog =
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
|
|
|
messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
|
messengerInfo.messageSeverity = kSeveritiesToLog;
|
|
messengerInfo.messageType = kMessagesToLog;
|
|
messengerInfo.pfnUserCallback = &DebugUtilsMessenger;
|
|
messengerInfo.pUserData = this;
|
|
|
|
ANGLE_VK_TRY(displayVk, vkCreateDebugUtilsMessengerEXT(mInstance, &messengerInfo, nullptr,
|
|
&mDebugUtilsMessenger));
|
|
}
|
|
else if (enableDebugReport)
|
|
{
|
|
// Fallback to EXT_debug_report.
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitDebugReportEXTFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
VkDebugReportCallbackCreateInfoEXT debugReportInfo = {};
|
|
|
|
debugReportInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
|
|
debugReportInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
|
|
debugReportInfo.pfnCallback = &DebugReportCallback;
|
|
debugReportInfo.pUserData = this;
|
|
|
|
ANGLE_VK_TRY(displayVk, vkCreateDebugReportCallbackEXT(mInstance, &debugReportInfo, nullptr,
|
|
&mDebugReportCallback));
|
|
}
|
|
|
|
if (std::find(enabledInstanceExtensions.begin(), enabledInstanceExtensions.end(),
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) !=
|
|
enabledInstanceExtensions.end())
|
|
{
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitGetPhysicalDeviceProperties2KHRFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
ASSERT(vkGetPhysicalDeviceProperties2KHR);
|
|
}
|
|
|
|
uint32_t physicalDeviceCount = 0;
|
|
ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));
|
|
ANGLE_VK_CHECK(displayVk, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED);
|
|
|
|
// TODO(jmadill): Handle multiple physical devices. For now, use the first device.
|
|
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
|
|
ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount,
|
|
physicalDevices.data()));
|
|
ChoosePhysicalDevice(physicalDevices, mEnabledICD, &mPhysicalDevice,
|
|
&mPhysicalDeviceProperties);
|
|
|
|
mGarbageCollectionFlushThreshold =
|
|
static_cast<uint32_t>(mPhysicalDeviceProperties.limits.maxMemoryAllocationCount *
|
|
kPercentMaxMemoryAllocationCount);
|
|
|
|
vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysicalDeviceFeatures);
|
|
|
|
// Ensure we can find a graphics queue family.
|
|
uint32_t queueCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
|
|
|
|
ANGLE_VK_CHECK(displayVk, queueCount > 0, VK_ERROR_INITIALIZATION_FAILED);
|
|
|
|
mQueueFamilyProperties.resize(queueCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount,
|
|
mQueueFamilyProperties.data());
|
|
|
|
size_t graphicsQueueFamilyCount = false;
|
|
uint32_t firstGraphicsQueueFamily = 0;
|
|
constexpr VkQueueFlags kGraphicsAndCompute = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
|
|
for (uint32_t familyIndex = 0; familyIndex < queueCount; ++familyIndex)
|
|
{
|
|
const auto &queueInfo = mQueueFamilyProperties[familyIndex];
|
|
if ((queueInfo.queueFlags & kGraphicsAndCompute) == kGraphicsAndCompute)
|
|
{
|
|
ASSERT(queueInfo.queueCount > 0);
|
|
graphicsQueueFamilyCount++;
|
|
if (firstGraphicsQueueFamily == 0)
|
|
{
|
|
firstGraphicsQueueFamily = familyIndex;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ANGLE_VK_CHECK(displayVk, graphicsQueueFamilyCount > 0, VK_ERROR_INITIALIZATION_FAILED);
|
|
|
|
// If only one queue family, go ahead and initialize the device. If there is more than one
|
|
// queue, we'll have to wait until we see a WindowSurface to know which supports present.
|
|
if (graphicsQueueFamilyCount == 1)
|
|
{
|
|
ANGLE_TRY(initializeDevice(displayVk, firstGraphicsQueueFamily));
|
|
}
|
|
|
|
VkDeviceSize preferredLargeHeapBlockSize = 0;
|
|
if (mFeatures.preferredLargeHeapBlockSize4MB.enabled)
|
|
{
|
|
// This number matches Chromium and was picked by looking at memory usage of
|
|
// Android apps. The allocator will start making blocks at 1/8 the max size
|
|
// and builds up block size as needed before capping at the max set here.
|
|
preferredLargeHeapBlockSize = 4 * 1024 * 1024;
|
|
}
|
|
|
|
// Create VMA allocator
|
|
ANGLE_VK_TRY(displayVk,
|
|
mAllocator.init(mPhysicalDevice, mDevice, mInstance, applicationInfo.apiVersion,
|
|
preferredLargeHeapBlockSize));
|
|
|
|
// Store the physical device memory properties so we can find the right memory pools.
|
|
mMemoryProperties.init(mPhysicalDevice);
|
|
|
|
if (!mGlslangInitialized)
|
|
{
|
|
GlslangInitialize();
|
|
mGlslangInitialized = true;
|
|
}
|
|
|
|
// Initialize the format table.
|
|
mFormatTable.initialize(this, &mNativeTextureCaps, &mNativeCaps.compressedTextureFormats);
|
|
|
|
setGlobalDebugAnnotator();
|
|
|
|
if (getFeatures().commandProcessor.enabled)
|
|
{
|
|
if (getFeatures().asynchronousCommandProcessing.enabled)
|
|
{
|
|
ASSERT(getFeatures().commandProcessor.enabled);
|
|
mCommandProcessorThread =
|
|
std::thread(&vk::CommandProcessor::processTasks, &mCommandProcessor);
|
|
waitForCommandProcessorIdle(displayVk);
|
|
}
|
|
else
|
|
{
|
|
ANGLE_TRY(mCommandProcessor.initTaskProcessor(displayVk));
|
|
}
|
|
}
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
void RendererVk::queryDeviceExtensionFeatures(const vk::ExtensionNameList &deviceExtensionNames)
|
|
{
|
|
// Default initialize all extension features to false.
|
|
mLineRasterizationFeatures = {};
|
|
mLineRasterizationFeatures.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;
|
|
|
|
mProvokingVertexFeatures = {};
|
|
mProvokingVertexFeatures.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
|
|
|
|
mVertexAttributeDivisorFeatures = {};
|
|
mVertexAttributeDivisorFeatures.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
|
|
|
|
mVertexAttributeDivisorProperties = {};
|
|
mVertexAttributeDivisorProperties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
|
|
|
|
mTransformFeedbackFeatures = {};
|
|
mTransformFeedbackFeatures.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
|
|
|
|
mIndexTypeUint8Features = {};
|
|
mIndexTypeUint8Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT;
|
|
|
|
mSubgroupProperties = {};
|
|
mSubgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
|
|
|
|
mExternalMemoryHostProperties = {};
|
|
mExternalMemoryHostProperties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
|
|
|
|
mShaderFloat16Int8Features = {};
|
|
mShaderFloat16Int8Features.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
|
|
|
|
mShaderAtomicFloatFeature = {};
|
|
mShaderAtomicFloatFeature.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT;
|
|
|
|
mDepthStencilResolveProperties = {};
|
|
mDepthStencilResolveProperties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
|
|
|
|
mExternalFenceProperties = {};
|
|
mExternalFenceProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES;
|
|
|
|
mExternalSemaphoreProperties = {};
|
|
mExternalSemaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES;
|
|
|
|
mSamplerYcbcrConversionFeatures = {};
|
|
mSamplerYcbcrConversionFeatures.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
|
|
|
|
if (!vkGetPhysicalDeviceProperties2KHR || !vkGetPhysicalDeviceFeatures2KHR)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Query features and properties.
|
|
VkPhysicalDeviceFeatures2KHR deviceFeatures = {};
|
|
deviceFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
|
|
VkPhysicalDeviceProperties2 deviceProperties = {};
|
|
deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
|
|
// Query line rasterization features
|
|
if (ExtensionFound(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mLineRasterizationFeatures);
|
|
}
|
|
|
|
// Query provoking vertex features
|
|
if (ExtensionFound(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mProvokingVertexFeatures);
|
|
}
|
|
|
|
// Query attribute divisor features and properties
|
|
if (ExtensionFound(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mVertexAttributeDivisorFeatures);
|
|
vk::AddToPNextChain(&deviceProperties, &mVertexAttributeDivisorProperties);
|
|
}
|
|
|
|
// Query transform feedback features
|
|
if (ExtensionFound(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mTransformFeedbackFeatures);
|
|
}
|
|
|
|
// Query uint8 index type features
|
|
if (ExtensionFound(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mIndexTypeUint8Features);
|
|
}
|
|
|
|
// Query external memory host properties
|
|
if (ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceProperties, &mExternalMemoryHostProperties);
|
|
}
|
|
|
|
// Query Ycbcr conversion properties
|
|
if (ExtensionFound(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mSamplerYcbcrConversionFeatures);
|
|
}
|
|
|
|
// Query float16/int8 features
|
|
if (ExtensionFound(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mShaderFloat16Int8Features);
|
|
}
|
|
|
|
// Query shader atomic float features
|
|
if (ExtensionFound(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceFeatures, &mShaderAtomicFloatFeature);
|
|
}
|
|
|
|
// Query depth/stencil resolve properties
|
|
if (ExtensionFound(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
vk::AddToPNextChain(&deviceProperties, &mDepthStencilResolveProperties);
|
|
}
|
|
|
|
// Query subgroup properties
|
|
vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties);
|
|
|
|
vkGetPhysicalDeviceFeatures2KHR(mPhysicalDevice, &deviceFeatures);
|
|
vkGetPhysicalDeviceProperties2KHR(mPhysicalDevice, &deviceProperties);
|
|
|
|
// Fence properties
|
|
if (mFeatures.supportsExternalFenceCapabilities.enabled)
|
|
{
|
|
VkPhysicalDeviceExternalFenceInfo externalFenceInfo = {};
|
|
externalFenceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO;
|
|
externalFenceInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
|
|
|
|
vkGetPhysicalDeviceExternalFencePropertiesKHR(mPhysicalDevice, &externalFenceInfo,
|
|
&mExternalFenceProperties);
|
|
}
|
|
|
|
// Semaphore properties
|
|
if (mFeatures.supportsExternalSemaphoreCapabilities.enabled)
|
|
{
|
|
VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {};
|
|
externalSemaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO;
|
|
externalSemaphoreInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
|
|
|
|
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
|
|
&mExternalSemaphoreProperties);
|
|
}
|
|
|
|
// Clean up pNext chains
|
|
mLineRasterizationFeatures.pNext = nullptr;
|
|
mProvokingVertexFeatures.pNext = nullptr;
|
|
mVertexAttributeDivisorFeatures.pNext = nullptr;
|
|
mVertexAttributeDivisorProperties.pNext = nullptr;
|
|
mTransformFeedbackFeatures.pNext = nullptr;
|
|
mIndexTypeUint8Features.pNext = nullptr;
|
|
mSubgroupProperties.pNext = nullptr;
|
|
mExternalMemoryHostProperties.pNext = nullptr;
|
|
mShaderFloat16Int8Features.pNext = nullptr;
|
|
mShaderAtomicFloatFeature.pNext = nullptr;
|
|
mDepthStencilResolveProperties.pNext = nullptr;
|
|
mSamplerYcbcrConversionFeatures.pNext = nullptr;
|
|
}
|
|
|
|
angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex)
|
|
{
|
|
uint32_t deviceLayerCount = 0;
|
|
ANGLE_VK_TRY(displayVk,
|
|
vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, nullptr));
|
|
|
|
std::vector<VkLayerProperties> deviceLayerProps(deviceLayerCount);
|
|
if (deviceLayerCount > 0)
|
|
{
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount,
|
|
deviceLayerProps.data()));
|
|
}
|
|
|
|
VulkanLayerVector enabledDeviceLayerNames;
|
|
if (mEnableValidationLayers)
|
|
{
|
|
mEnableValidationLayers =
|
|
GetAvailableValidationLayers(deviceLayerProps, false, &enabledDeviceLayerNames);
|
|
}
|
|
|
|
const char *wsiLayer = displayVk->getWSILayer();
|
|
if (wsiLayer)
|
|
{
|
|
enabledDeviceLayerNames.push_back(wsiLayer);
|
|
}
|
|
|
|
// Enumerate device extensions that are provided by the vulkan
|
|
// implementation and implicit layers.
|
|
uint32_t deviceExtensionCount = 0;
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
|
|
&deviceExtensionCount, nullptr));
|
|
|
|
std::vector<VkExtensionProperties> deviceExtensionProps(deviceExtensionCount);
|
|
if (deviceExtensionCount > 0)
|
|
{
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr,
|
|
&deviceExtensionCount,
|
|
deviceExtensionProps.data()));
|
|
}
|
|
|
|
// Enumerate device extensions that are provided by explicit layers.
|
|
for (const char *layerName : enabledDeviceLayerNames)
|
|
{
|
|
uint32_t previousExtensionCount = static_cast<uint32_t>(deviceExtensionProps.size());
|
|
uint32_t deviceLayerExtensionCount = 0;
|
|
ANGLE_VK_TRY(displayVk,
|
|
vkEnumerateDeviceExtensionProperties(mPhysicalDevice, layerName,
|
|
&deviceLayerExtensionCount, nullptr));
|
|
deviceExtensionProps.resize(previousExtensionCount + deviceLayerExtensionCount);
|
|
ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(
|
|
mPhysicalDevice, layerName, &deviceLayerExtensionCount,
|
|
deviceExtensionProps.data() + previousExtensionCount));
|
|
}
|
|
|
|
vk::ExtensionNameList deviceExtensionNames;
|
|
if (!deviceExtensionProps.empty())
|
|
{
|
|
ASSERT(deviceExtensionNames.size() <= deviceExtensionProps.size());
|
|
for (const VkExtensionProperties &prop : deviceExtensionProps)
|
|
{
|
|
deviceExtensionNames.push_back(prop.extensionName);
|
|
}
|
|
std::sort(deviceExtensionNames.begin(), deviceExtensionNames.end(), StrLess);
|
|
}
|
|
|
|
vk::ExtensionNameList enabledDeviceExtensions;
|
|
enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
|
|
|
// Queues: map low, med, high priority to whatever is supported up to 3 queues
|
|
uint32_t queueCount = std::min(mQueueFamilyProperties[queueFamilyIndex].queueCount,
|
|
static_cast<uint32_t>(egl::ContextPriority::EnumCount));
|
|
|
|
constexpr float kVulkanQueuePriorityLow = 0.0;
|
|
constexpr float kVulkanQueuePriorityMedium = 0.4;
|
|
constexpr float kVulkanQueuePriorityHigh = 1.0;
|
|
|
|
// Index order: Low, High, Medium - so no need to rearrange according to count:
|
|
// If we have 1 queue - all same, if 2 - Low and High, if 3 Low, High and Medium.
|
|
constexpr uint32_t kQueueIndexLow = 0;
|
|
constexpr uint32_t kQueueIndexHigh = 1;
|
|
constexpr uint32_t kQueueIndexMedium = 2;
|
|
|
|
constexpr float queuePriorities[static_cast<uint32_t>(egl::ContextPriority::EnumCount)] = {
|
|
kVulkanQueuePriorityMedium, kVulkanQueuePriorityHigh, kVulkanQueuePriorityLow};
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfo = {};
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.flags = 0;
|
|
queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
|
|
queueCreateInfo.queueCount = queueCount;
|
|
queueCreateInfo.pQueuePriorities = queuePriorities;
|
|
|
|
// Query extensions and their features.
|
|
queryDeviceExtensionFeatures(deviceExtensionNames);
|
|
|
|
// Initialize features and workarounds.
|
|
initFeatures(displayVk, deviceExtensionNames);
|
|
|
|
// Enable VK_KHR_get_memory_requirements2, if supported
|
|
if (ExtensionFound(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
}
|
|
|
|
// Enable VK_KHR_bind_memory2, if supported
|
|
if (ExtensionFound(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, deviceExtensionNames))
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
}
|
|
|
|
// Enable KHR_MAINTENANCE1 to support viewport flipping.
|
|
if (mPhysicalDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0))
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsRenderpass2.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsIncrementalPresent.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);
|
|
}
|
|
|
|
#if defined(ANGLE_PLATFORM_ANDROID)
|
|
if (getFeatures().supportsAndroidHardwareBuffer.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
|
|
enabledDeviceExtensions.push_back(
|
|
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
|
|
# if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalMemoryHardwareBufferANDROIDFunctions(mInstance);
|
|
# endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
#else
|
|
ASSERT(!getFeatures().supportsAndroidHardwareBuffer.enabled);
|
|
#endif
|
|
|
|
#if defined(ANGLE_PLATFORM_GGP)
|
|
if (getFeatures().supportsGGPFrameToken.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_GGP_FRAME_TOKEN_EXTENSION_NAME);
|
|
}
|
|
ANGLE_VK_CHECK(displayVk, getFeatures().supportsGGPFrameToken.enabled,
|
|
VK_ERROR_EXTENSION_NOT_PRESENT);
|
|
#else
|
|
ASSERT(!getFeatures().supportsGGPFrameToken.enabled);
|
|
#endif
|
|
|
|
if (getFeatures().supportsAndroidHardwareBuffer.enabled ||
|
|
getFeatures().supportsExternalMemoryFd.enabled ||
|
|
getFeatures().supportsExternalMemoryFuchsia.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalMemoryFd.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalMemoryFuchsia.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalSemaphoreFd.enabled ||
|
|
getFeatures().supportsExternalSemaphoreFuchsia.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalSemaphoreFdFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
if (getFeatures().supportsExternalSemaphoreCapabilities.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalFenceCapabilities.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalSemaphoreFd.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalSemaphoreCapabilities.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalSemaphoreCapabilitiesFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
if (getFeatures().supportsExternalFenceFd.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME);
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalFenceFdFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
if (getFeatures().supportsExternalFenceCapabilities.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalFenceCapabilitiesFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
if (getFeatures().supportsExternalSemaphoreFuchsia.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsShaderStencilExport.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsRenderPassStoreOpNoneQCOM.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_QCOM_render_pass_store_ops_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsImageFormatList.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
|
|
}
|
|
|
|
std::sort(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end(), StrLess);
|
|
ANGLE_VK_TRY(displayVk, VerifyExtensionsPresent(deviceExtensionNames, enabledDeviceExtensions));
|
|
|
|
// Select additional features to be enabled.
|
|
VkPhysicalDeviceFeatures2KHR enabledFeatures = {};
|
|
enabledFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
// Used to support cubemap array:
|
|
enabledFeatures.features.imageCubeArray = getFeatures().supportsImageCubeArray.enabled;
|
|
// Used to support framebuffers with multiple attachments:
|
|
enabledFeatures.features.independentBlend = mPhysicalDeviceFeatures.independentBlend;
|
|
// Used to support robust buffer access:
|
|
enabledFeatures.features.robustBufferAccess = mPhysicalDeviceFeatures.robustBufferAccess;
|
|
// Used to support Anisotropic filtering:
|
|
enabledFeatures.features.samplerAnisotropy = mPhysicalDeviceFeatures.samplerAnisotropy;
|
|
// Used to support wide lines:
|
|
enabledFeatures.features.wideLines = mPhysicalDeviceFeatures.wideLines;
|
|
// Used to emulate transform feedback:
|
|
enabledFeatures.features.vertexPipelineStoresAndAtomics =
|
|
mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics;
|
|
// Used to implement storage buffers and images in the fragment shader:
|
|
enabledFeatures.features.fragmentStoresAndAtomics =
|
|
mPhysicalDeviceFeatures.fragmentStoresAndAtomics;
|
|
// Used to support geometry shaders:
|
|
enabledFeatures.features.geometryShader = mPhysicalDeviceFeatures.geometryShader;
|
|
// Used to support EXT_gpu_shader5:
|
|
enabledFeatures.features.shaderImageGatherExtended =
|
|
mPhysicalDeviceFeatures.shaderImageGatherExtended;
|
|
// Used to support EXT_gpu_shader5:
|
|
enabledFeatures.features.shaderUniformBufferArrayDynamicIndexing =
|
|
mPhysicalDeviceFeatures.shaderUniformBufferArrayDynamicIndexing;
|
|
// Used to support EXT_gpu_shader5 and sampler array of array emulation:
|
|
enabledFeatures.features.shaderSampledImageArrayDynamicIndexing =
|
|
mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing;
|
|
// Used to support atomic counter emulation:
|
|
enabledFeatures.features.shaderStorageBufferArrayDynamicIndexing =
|
|
mPhysicalDeviceFeatures.shaderStorageBufferArrayDynamicIndexing;
|
|
// Used to support APPLE_clip_distance
|
|
enabledFeatures.features.shaderClipDistance = mPhysicalDeviceFeatures.shaderClipDistance;
|
|
// Used to support OES_sample_shading
|
|
enabledFeatures.features.sampleRateShading = mPhysicalDeviceFeatures.sampleRateShading;
|
|
// Used to support depth clears through draw calls.
|
|
enabledFeatures.features.depthClamp = mPhysicalDeviceFeatures.depthClamp;
|
|
|
|
if (!vk::CommandBuffer::ExecutesInline())
|
|
{
|
|
enabledFeatures.features.inheritedQueries = mPhysicalDeviceFeatures.inheritedQueries;
|
|
}
|
|
|
|
// Setup device initialization struct
|
|
VkDeviceCreateInfo createInfo = {};
|
|
|
|
// Based on available extension features, decide on which extensions and features to enable.
|
|
|
|
if (mLineRasterizationFeatures.bresenhamLines)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mLineRasterizationFeatures);
|
|
}
|
|
|
|
if (mProvokingVertexFeatures.provokingVertexLast)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mProvokingVertexFeatures);
|
|
}
|
|
|
|
if (mVertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mVertexAttributeDivisorFeatures);
|
|
|
|
// We only store 8 bit divisor in GraphicsPipelineDesc so capping value & we emulate if
|
|
// exceeded
|
|
mMaxVertexAttribDivisor =
|
|
std::min(mVertexAttributeDivisorProperties.maxVertexAttribDivisor,
|
|
static_cast<uint32_t>(std::numeric_limits<uint8_t>::max()));
|
|
}
|
|
|
|
if (getFeatures().supportsTransformFeedbackExtension.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mTransformFeedbackFeatures);
|
|
}
|
|
|
|
if (getFeatures().supportsIndexTypeUint8.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mIndexTypeUint8Features);
|
|
}
|
|
|
|
if (getFeatures().supportsDepthStencilResolve.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
|
|
}
|
|
|
|
if (getFeatures().supportsExternalMemoryHost.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
|
|
mMinImportedHostPointerAlignment =
|
|
mExternalMemoryHostProperties.minImportedHostPointerAlignment;
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
InitExternalMemoryHostFunctions(mInstance);
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
if (getFeatures().supportsYUVSamplerConversion.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mSamplerYcbcrConversionFeatures);
|
|
}
|
|
|
|
if (getFeatures().supportsShaderFloat16.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mShaderFloat16Int8Features);
|
|
}
|
|
|
|
if (getFeatures().supportsShaderImageFloat32Atomics.enabled)
|
|
{
|
|
enabledDeviceExtensions.push_back(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME);
|
|
vk::AddToPNextChain(&createInfo, &mShaderAtomicFloatFeature);
|
|
}
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
createInfo.flags = 0;
|
|
createInfo.queueCreateInfoCount = 1;
|
|
createInfo.pQueueCreateInfos = &queueCreateInfo;
|
|
createInfo.enabledLayerCount = static_cast<uint32_t>(enabledDeviceLayerNames.size());
|
|
createInfo.ppEnabledLayerNames = enabledDeviceLayerNames.data();
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size());
|
|
createInfo.ppEnabledExtensionNames =
|
|
enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data();
|
|
// Enable core features without assuming VkPhysicalDeviceFeatures2KHR is accepted in the pNext
|
|
// chain of VkDeviceCreateInfo.
|
|
createInfo.pEnabledFeatures = &enabledFeatures.features;
|
|
|
|
ANGLE_VK_TRY(displayVk, vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice));
|
|
#if defined(ANGLE_SHARED_LIBVULKAN)
|
|
// Load volk if we are loading dynamically
|
|
volkLoadDevice(mDevice);
|
|
#endif // defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
mCurrentQueueFamilyIndex = queueFamilyIndex;
|
|
|
|
// When only 1 Queue, use same for all, Low index. Identify as Medium, since it's default.
|
|
VkQueue queue;
|
|
vkGetDeviceQueue(mDevice, mCurrentQueueFamilyIndex, kQueueIndexLow, &queue);
|
|
mQueues[egl::ContextPriority::Low] = queue;
|
|
mQueues[egl::ContextPriority::Medium] = queue;
|
|
mQueues[egl::ContextPriority::High] = queue;
|
|
mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Medium;
|
|
mPriorities[egl::ContextPriority::Medium] = egl::ContextPriority::Medium;
|
|
mPriorities[egl::ContextPriority::High] = egl::ContextPriority::Medium;
|
|
|
|
// If at least 2 queues, High has its own queue
|
|
if (queueCount > 1)
|
|
{
|
|
vkGetDeviceQueue(mDevice, mCurrentQueueFamilyIndex, kQueueIndexHigh,
|
|
&mQueues[egl::ContextPriority::High]);
|
|
mPriorities[egl::ContextPriority::High] = egl::ContextPriority::High;
|
|
}
|
|
// If at least 3 queues, Medium has its own queue. Adjust Low priority.
|
|
if (queueCount > 2)
|
|
{
|
|
vkGetDeviceQueue(mDevice, mCurrentQueueFamilyIndex, kQueueIndexMedium,
|
|
&mQueues[egl::ContextPriority::Medium]);
|
|
mPriorities[egl::ContextPriority::Low] = egl::ContextPriority::Low;
|
|
}
|
|
|
|
#if !defined(ANGLE_SHARED_LIBVULKAN)
|
|
if (getFeatures().supportsTransformFeedbackExtension.enabled)
|
|
{
|
|
InitTransformFeedbackEXTFunctions(mDevice);
|
|
}
|
|
if (getFeatures().supportsYUVSamplerConversion.enabled)
|
|
{
|
|
InitSamplerYcbcrKHRFunctions(mDevice);
|
|
}
|
|
if (getFeatures().supportsRenderpass2.enabled)
|
|
{
|
|
InitRenderPass2KHRFunctions(mDevice);
|
|
}
|
|
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
|
|
|
|
if (getFeatures().forceMaxUniformBufferSize16KB.enabled)
|
|
{
|
|
mDefaultUniformBufferSize = kMinDefaultUniformBufferSize;
|
|
}
|
|
// Cap it with the driver limit
|
|
mDefaultUniformBufferSize = std::min(
|
|
mDefaultUniformBufferSize, getPhysicalDeviceProperties().limits.maxUniformBufferRange);
|
|
|
|
// Initialize the vulkan pipeline cache.
|
|
bool success = false;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPipelineCacheMutex);
|
|
ANGLE_TRY(initPipelineCache(displayVk, &mPipelineCache, &success));
|
|
}
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::selectPresentQueueForSurface(DisplayVk *displayVk,
|
|
VkSurfaceKHR surface,
|
|
uint32_t *presentQueueOut)
|
|
{
|
|
// We've already initialized a device, and can't re-create it unless it's never been used.
|
|
// TODO(jmadill): Handle the re-creation case if necessary.
|
|
if (mDevice != VK_NULL_HANDLE)
|
|
{
|
|
ASSERT(mCurrentQueueFamilyIndex != std::numeric_limits<uint32_t>::max());
|
|
|
|
// Check if the current device supports present on this surface.
|
|
VkBool32 supportsPresent = VK_FALSE;
|
|
ANGLE_VK_TRY(displayVk,
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mCurrentQueueFamilyIndex,
|
|
surface, &supportsPresent));
|
|
|
|
if (supportsPresent == VK_TRUE)
|
|
{
|
|
*presentQueueOut = mCurrentQueueFamilyIndex;
|
|
return angle::Result::Continue;
|
|
}
|
|
}
|
|
|
|
// Find a graphics and present queue.
|
|
Optional<uint32_t> newPresentQueue;
|
|
uint32_t queueCount = static_cast<uint32_t>(mQueueFamilyProperties.size());
|
|
constexpr VkQueueFlags kGraphicsAndCompute = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
|
|
for (uint32_t queueIndex = 0; queueIndex < queueCount; ++queueIndex)
|
|
{
|
|
const auto &queueInfo = mQueueFamilyProperties[queueIndex];
|
|
if ((queueInfo.queueFlags & kGraphicsAndCompute) == kGraphicsAndCompute)
|
|
{
|
|
VkBool32 supportsPresent = VK_FALSE;
|
|
ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceSupportKHR(
|
|
mPhysicalDevice, queueIndex, surface, &supportsPresent));
|
|
|
|
if (supportsPresent == VK_TRUE)
|
|
{
|
|
newPresentQueue = queueIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ANGLE_VK_CHECK(displayVk, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED);
|
|
ANGLE_TRY(initializeDevice(displayVk, newPresentQueue.value()));
|
|
|
|
*presentQueueOut = newPresentQueue.value();
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
std::string RendererVk::getVendorString() const
|
|
{
|
|
return GetVendorString(mPhysicalDeviceProperties.vendorID);
|
|
}
|
|
|
|
std::string RendererVk::getRendererDescription() const
|
|
{
|
|
std::stringstream strstr;
|
|
|
|
uint32_t apiVersion = mPhysicalDeviceProperties.apiVersion;
|
|
|
|
strstr << "Vulkan ";
|
|
strstr << VK_VERSION_MAJOR(apiVersion) << ".";
|
|
strstr << VK_VERSION_MINOR(apiVersion) << ".";
|
|
strstr << VK_VERSION_PATCH(apiVersion);
|
|
|
|
strstr << "(";
|
|
|
|
// In the case of NVIDIA, deviceName does not necessarily contain "NVIDIA". Add "NVIDIA" so that
|
|
// Vulkan end2end tests can be selectively disabled on NVIDIA. TODO(jmadill): should not be
|
|
// needed after http://anglebug.com/1874 is fixed and end2end_tests use more sophisticated
|
|
// driver detection.
|
|
if (mPhysicalDeviceProperties.vendorID == VENDOR_ID_NVIDIA)
|
|
{
|
|
strstr << GetVendorString(mPhysicalDeviceProperties.vendorID) << " ";
|
|
}
|
|
|
|
strstr << mPhysicalDeviceProperties.deviceName;
|
|
strstr << " (" << gl::FmtHex(mPhysicalDeviceProperties.deviceID) << ")";
|
|
|
|
strstr << ")";
|
|
|
|
return strstr.str();
|
|
}
|
|
|
|
gl::Version RendererVk::getMaxSupportedESVersion() const
|
|
{
|
|
// Current highest supported version
|
|
gl::Version maxVersion = gl::Version(3, 1);
|
|
|
|
// Early out without downgrading ES version if mock ICD enabled.
|
|
// Mock ICD doesn't expose sufficient capabilities yet.
|
|
// https://github.com/KhronosGroup/Vulkan-Tools/issues/84
|
|
if (isMockICDEnabled())
|
|
{
|
|
return maxVersion;
|
|
}
|
|
|
|
// Limit to ES3.1 if there are any blockers for 3.2.
|
|
if (!vk::CanSupportGPUShader5EXT(mPhysicalDeviceFeatures))
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {3, 1});
|
|
}
|
|
|
|
// Limit to ES3.0 if there are any blockers for 3.1.
|
|
|
|
// ES3.1 requires at least one atomic counter buffer and four storage buffers in compute.
|
|
// Atomic counter buffers are emulated with storage buffers. For simplicity, we always support
|
|
// either none or IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS atomic counter buffers. So if
|
|
// Vulkan doesn't support at least that many storage buffers in compute, we don't support 3.1.
|
|
const uint32_t kMinimumStorageBuffersForES31 =
|
|
gl::limits::kMinimumComputeStorageBuffers + gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS;
|
|
if (mPhysicalDeviceProperties.limits.maxPerStageDescriptorStorageBuffers <
|
|
kMinimumStorageBuffersForES31)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {3, 0});
|
|
}
|
|
|
|
// ES3.1 requires at least a maximum offset of at least 2047.
|
|
// If the Vulkan implementation can't support that, we cannot support 3.1.
|
|
if (mPhysicalDeviceProperties.limits.maxVertexInputAttributeOffset < 2047)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {3, 0});
|
|
}
|
|
|
|
// Limit to ES2.0 if there are any blockers for 3.0.
|
|
// TODO: http://anglebug.com/3972 Limit to GLES 2.0 if flat shading can't be emulated
|
|
|
|
// Multisample textures (ES3.1) and multisample renderbuffers (ES3.0) require the Vulkan driver
|
|
// to support the standard sample locations (in order to pass dEQP tests that check these
|
|
// locations). If the Vulkan implementation can't support that, we cannot support 3.0/3.1.
|
|
if (mPhysicalDeviceProperties.limits.standardSampleLocations != VK_TRUE)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
|
|
// If the command buffer doesn't support queries, we can't support ES3.
|
|
if (!vk::CommandBuffer::SupportsQueries(mPhysicalDeviceFeatures))
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
|
|
// If independentBlend is not supported, we can't have a mix of has-alpha and emulated-alpha
|
|
// render targets in a framebuffer. We also cannot perform masked clears of multiple render
|
|
// targets.
|
|
if (!mPhysicalDeviceFeatures.independentBlend)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
|
|
// If the Vulkan transform feedback extension is not present, we use an emulation path that
|
|
// requires the vertexPipelineStoresAndAtomics feature. Without the extension or this feature,
|
|
// we can't currently support transform feedback.
|
|
if (!mFeatures.supportsTransformFeedbackExtension.enabled &&
|
|
!mFeatures.emulateTransformFeedback.enabled)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
|
|
// Limit to GLES 2.0 if maxPerStageDescriptorUniformBuffers is too low.
|
|
// Table 6.31 MAX_VERTEX_UNIFORM_BLOCKS minimum value = 12
|
|
// Table 6.32 MAX_FRAGMENT_UNIFORM_BLOCKS minimum value = 12
|
|
// NOTE: We reserve some uniform buffers for emulation, so use the NativeCaps which takes this
|
|
// into account, rather than the physical device maxPerStageDescriptorUniformBuffers limits.
|
|
for (gl::ShaderType shaderType : gl::AllShaderTypes())
|
|
{
|
|
if (static_cast<GLuint>(getNativeCaps().maxShaderUniformBlocks[shaderType]) <
|
|
gl::limits::kMinimumShaderUniformBlocks)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
}
|
|
|
|
// Limit to GLES 2.0 if maxVertexOutputComponents is too low.
|
|
// Table 6.31 MAX VERTEX OUTPUT COMPONENTS minimum value = 64
|
|
// NOTE: We reserve some vertex output components for emulation, so use the NativeCaps which
|
|
// takes this into account, rather than the physical device maxVertexOutputComponents limits.
|
|
if (static_cast<GLuint>(getNativeCaps().maxVertexOutputComponents) <
|
|
gl::limits::kMinimumVertexOutputComponents)
|
|
{
|
|
maxVersion = LimitVersionTo(maxVersion, {2, 0});
|
|
}
|
|
|
|
return maxVersion;
|
|
}
|
|
|
|
gl::Version RendererVk::getMaxConformantESVersion() const
|
|
{
|
|
return LimitVersionTo(getMaxSupportedESVersion(), {3, 1});
|
|
}
|
|
|
|
void RendererVk::initFeatures(DisplayVk *displayVk,
|
|
const vk::ExtensionNameList &deviceExtensionNames)
|
|
{
|
|
if (displayVk->getState().featuresAllDisabled)
|
|
{
|
|
ApplyFeatureOverrides(&mFeatures, displayVk->getState());
|
|
return;
|
|
}
|
|
|
|
constexpr uint32_t kPixel2DriverWithRelaxedPrecision = 0x801EA000;
|
|
|
|
bool isAMD = IsAMD(mPhysicalDeviceProperties.vendorID);
|
|
bool isIntel = IsIntel(mPhysicalDeviceProperties.vendorID);
|
|
bool isNvidia = IsNvidia(mPhysicalDeviceProperties.vendorID);
|
|
bool isQualcomm = IsQualcomm(mPhysicalDeviceProperties.vendorID);
|
|
bool isARM = IsARM(mPhysicalDeviceProperties.vendorID);
|
|
bool isPowerVR = IsPowerVR(mPhysicalDeviceProperties.vendorID);
|
|
bool isSwiftShader =
|
|
IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID);
|
|
|
|
if (mLineRasterizationFeatures.bresenhamLines == VK_TRUE)
|
|
{
|
|
ASSERT(mLineRasterizationFeatures.sType ==
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT);
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, bresenhamLineRasterization, true);
|
|
}
|
|
else
|
|
{
|
|
// Use OpenGL line rasterization rules if extension not available by default.
|
|
// TODO(jmadill): Fix Android support. http://anglebug.com/2830
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, basicGLLineRasterization, !IsAndroid() && !isPowerVR);
|
|
}
|
|
|
|
if (mProvokingVertexFeatures.provokingVertexLast == VK_TRUE)
|
|
{
|
|
ASSERT(mProvokingVertexFeatures.sType ==
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT);
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, provokingVertex, true);
|
|
}
|
|
|
|
// http://anglebug.com/2838
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, extraCopyBufferRegion, IsWindows() && isIntel);
|
|
|
|
// http://anglebug.com/3055
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, forceCPUPathForCubeMapCopy, IsWindows() && isIntel);
|
|
|
|
// Work around incorrect NVIDIA point size range clamping.
|
|
// http://anglebug.com/2970#c10
|
|
// Clamp if driver version is:
|
|
// < 430 on Windows
|
|
// < 421 otherwise
|
|
angle::VersionInfo nvidiaVersion;
|
|
if (isNvidia)
|
|
{
|
|
nvidiaVersion =
|
|
angle::ParseNvidiaDriverVersion(this->mPhysicalDeviceProperties.driverVersion);
|
|
}
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, clampPointSize,
|
|
isNvidia && nvidiaVersion.major < uint32_t(IsWindows() ? 430 : 421));
|
|
|
|
// Work around ineffective compute-graphics barriers on Nexus 5X.
|
|
// TODO(syoussefi): Figure out which other vendors and driver versions are affected.
|
|
// http://anglebug.com/3019
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, flushAfterVertexConversion,
|
|
IsAndroid() && IsNexus5X(mPhysicalDeviceProperties.vendorID,
|
|
mPhysicalDeviceProperties.deviceID));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsRenderpass2,
|
|
ExtensionFound(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsIncrementalPresent,
|
|
ExtensionFound(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
#if defined(ANGLE_PLATFORM_ANDROID)
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsAndroidHardwareBuffer,
|
|
IsAndroid() &&
|
|
ExtensionFound(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
|
deviceExtensionNames) &&
|
|
ExtensionFound(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, deviceExtensionNames));
|
|
#endif
|
|
|
|
#if defined(ANGLE_PLATFORM_GGP)
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsGGPFrameToken,
|
|
ExtensionFound(VK_GGP_FRAME_TOKEN_EXTENSION_NAME, deviceExtensionNames));
|
|
#endif
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalMemoryFd,
|
|
ExtensionFound(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalMemoryFuchsia,
|
|
ExtensionFound(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsFilteringPrecision,
|
|
ExtensionFound(VK_GOOGLE_SAMPLER_FILTERING_PRECISION_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalFenceCapabilities,
|
|
ExtensionFound(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalSemaphoreCapabilities,
|
|
ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
|
|
deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalSemaphoreFd,
|
|
ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalSemaphoreFuchsia,
|
|
ExtensionFound(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalFenceFd,
|
|
ExtensionFound(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
#if defined(ANGLE_PLATFORM_ANDROID)
|
|
if (mFeatures.supportsExternalFenceCapabilities.enabled &&
|
|
mFeatures.supportsExternalSemaphoreCapabilities.enabled)
|
|
{
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsAndroidNativeFenceSync,
|
|
(mFeatures.supportsExternalFenceFd.enabled &&
|
|
FencePropertiesCompatibleWithAndroid(mExternalFenceProperties) &&
|
|
mFeatures.supportsExternalSemaphoreFd.enabled &&
|
|
SemaphorePropertiesCompatibleWithAndroid(mExternalSemaphoreProperties)));
|
|
}
|
|
else
|
|
{
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsAndroidNativeFenceSync,
|
|
(mFeatures.supportsExternalFenceFd.enabled &&
|
|
mFeatures.supportsExternalSemaphoreFd.enabled));
|
|
}
|
|
#endif // defined(ANGLE_PLATFORM_ANDROID)
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsShaderStencilExport,
|
|
ExtensionFound(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsRenderPassStoreOpNoneQCOM,
|
|
ExtensionFound(VK_QCOM_render_pass_store_ops_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsTransformFeedbackExtension,
|
|
mTransformFeedbackFeatures.transformFeedback == VK_TRUE);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsIndexTypeUint8,
|
|
mIndexTypeUint8Features.indexTypeUint8 == VK_TRUE);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsDepthStencilResolve,
|
|
mFeatures.supportsRenderpass2.enabled &&
|
|
mDepthStencilResolveProperties.independentResolveNone == VK_TRUE);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, emulateTransformFeedback,
|
|
(!mFeatures.supportsTransformFeedbackExtension.enabled &&
|
|
mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics == VK_TRUE));
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, disableFifoPresentMode, IsLinux() && isIntel);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, bindEmptyForUnusedDescriptorSets,
|
|
IsAndroid() && isQualcomm);
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, forceOldRewriteStructSamplers,
|
|
!mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing || isQualcomm);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, perFrameWindowSizeQuery,
|
|
isIntel || (IsWindows() && isAMD) || IsFuchsia());
|
|
|
|
// Disabled on AMD/windows due to buggy behavior.
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, disallowSeamfulCubeMapEmulation, IsWindows() && isAMD);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, padBuffersToMaxVertexAttribStride, isAMD);
|
|
mMaxVertexAttribStride = std::min(static_cast<uint32_t>(gl::limits::kMaxVertexAttribStride),
|
|
mPhysicalDeviceProperties.limits.maxVertexInputBindingStride);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, forceD16TexFilter, IsAndroid() && isQualcomm);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, disableFlippingBlitWithCommand, IsAndroid() && isQualcomm);
|
|
|
|
// Allocation sanitization disabled by default because of a heaveyweight implementation
|
|
// that can cause OOM and timeouts.
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, allocateNonZeroMemory, false);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, shadowBuffers, true);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, persistentlyMappedBuffers, true);
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsExternalMemoryHost,
|
|
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
|
|
|
|
// Android pre-rotation support can be disabled.
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, enablePreRotateSurfaces, IsAndroid());
|
|
|
|
// Currently disable FramebufferVk cache on Apple: http://anglebug.com/4442
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, enableFramebufferVkCache, !IsApple());
|
|
|
|
// Currently disabled by default: http://anglebug.com/3078
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, enablePrecisionQualifiers,
|
|
!(IsPixel2(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) &&
|
|
(mPhysicalDeviceProperties.driverVersion < kPixel2DriverWithRelaxedPrecision)));
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, preferAggregateBarrierCalls, isNvidia || isAMD || isIntel);
|
|
|
|
// Currently disabled by default: http://anglebug.com/4324
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, commandProcessor, false);
|
|
|
|
// Currently disabled by default: http://anglebug.com/4324
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, asynchronousCommandProcessing, false);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion,
|
|
mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsShaderFloat16,
|
|
mShaderFloat16Int8Features.shaderFloat16 == VK_TRUE);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, supportsShaderImageFloat32Atomics,
|
|
mShaderAtomicFloatFeature.shaderImageFloat32Atomics == VK_TRUE);
|
|
|
|
// The compute shader used to generate mipmaps uses a 256-wide workgroup. This path is only
|
|
// enabled on devices that meet this minimum requirement. Furthermore,
|
|
// VK_IMAGE_USAGE_STORAGE_BIT is detrimental to performance on many platforms, on which this
|
|
// path is not enabled. Platforms that are known to have better performance with this path are:
|
|
//
|
|
// - Nvidia
|
|
// - AMD
|
|
//
|
|
// Additionally, this path is disabled on buggy drivers:
|
|
//
|
|
// - AMD/Windows: Unfortunately the trybots use ancient AMD cards and drivers.
|
|
const uint32_t maxComputeWorkGroupInvocations =
|
|
mPhysicalDeviceProperties.limits.maxComputeWorkGroupInvocations;
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, allowGenerateMipmapWithCompute,
|
|
maxComputeWorkGroupInvocations >= 256 && (isNvidia || (isAMD && !IsWindows())));
|
|
|
|
bool isAdreno540 = mPhysicalDeviceProperties.deviceID == angle::kDeviceID_Adreno540;
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, forceMaxUniformBufferSize16KB, isQualcomm && isAdreno540);
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsImageFormatList,
|
|
(ExtensionFound(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, deviceExtensionNames)) && isAMD);
|
|
|
|
// Feature disabled due to driver bugs:
|
|
//
|
|
// - Swiftshader on mac: http://anglebug.com/4937
|
|
// - Intel on windows: http://anglebug.com/5032
|
|
// - AMD on windows: http://crbug.com/1132366
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, enableMultisampledRenderToTexture,
|
|
!(IsApple() && isSwiftShader) && !(IsWindows() && (isIntel || isAMD)));
|
|
|
|
// Feature disabled due to driver bugs:
|
|
//
|
|
// - Swiftshader: http://anglebug.com/5142
|
|
// - Qualcomm: http://anglebug.com/5143
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, supportsImageCubeArray,
|
|
mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE && !isSwiftShader && !isQualcomm);
|
|
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm);
|
|
|
|
// Defer glFLush call causes manhattan 3.0 perf regression. Let Qualcomm driver opt out from
|
|
// this optimization.
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, deferFlushUntilEndRenderPass, !isQualcomm);
|
|
|
|
// Android mistakenly destroys the old swapchain when creating a new one.
|
|
ANGLE_FEATURE_CONDITION(&mFeatures, waitIdleBeforeSwapchainRecreation, IsAndroid() && isARM);
|
|
|
|
ANGLE_FEATURE_CONDITION(
|
|
&mFeatures, preferDrawClearOverVkCmdClearAttachments,
|
|
IsPixel2(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID));
|
|
|
|
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
|
|
platform->overrideFeaturesVk(platform, &mFeatures);
|
|
|
|
ApplyFeatureOverrides(&mFeatures, displayVk->getState());
|
|
}
|
|
|
|
void RendererVk::initPipelineCacheVkKey()
|
|
{
|
|
std::ostringstream hashStream("ANGLE Pipeline Cache: ", std::ios_base::ate);
|
|
// Add the pipeline cache UUID to make sure the blob cache always gives a compatible pipeline
|
|
// cache. It's not particularly necessary to write it as a hex number as done here, so long as
|
|
// there is no '\0' in the result.
|
|
for (const uint32_t c : mPhysicalDeviceProperties.pipelineCacheUUID)
|
|
{
|
|
hashStream << std::hex << c;
|
|
}
|
|
// Add the vendor and device id too for good measure.
|
|
hashStream << std::hex << mPhysicalDeviceProperties.vendorID;
|
|
hashStream << std::hex << mPhysicalDeviceProperties.deviceID;
|
|
|
|
const std::string &hashString = hashStream.str();
|
|
angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(hashString.c_str()),
|
|
hashString.length(), mPipelineCacheVkBlobKey.data());
|
|
}
|
|
|
|
angle::Result RendererVk::initPipelineCache(DisplayVk *display,
|
|
vk::PipelineCache *pipelineCache,
|
|
bool *success)
|
|
{
|
|
initPipelineCacheVkKey();
|
|
|
|
egl::BlobCache::Value initialData;
|
|
size_t dataSize = 0;
|
|
*success = display->getBlobCache()->get(display->getScratchBuffer(), mPipelineCacheVkBlobKey,
|
|
&initialData, &dataSize);
|
|
|
|
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
|
|
|
|
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
|
pipelineCacheCreateInfo.flags = 0;
|
|
pipelineCacheCreateInfo.initialDataSize = *success ? dataSize : 0;
|
|
pipelineCacheCreateInfo.pInitialData = *success ? initialData.data() : nullptr;
|
|
|
|
ANGLE_VK_TRY(display, pipelineCache->init(mDevice, pipelineCacheCreateInfo));
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::getPipelineCache(vk::PipelineCache **pipelineCache)
|
|
{
|
|
// Note that unless external synchronization is specifically requested the pipeline cache
|
|
// is internally synchronized. See VK_EXT_pipeline_creation_cache_control. We might want
|
|
// to investigate controlling synchronization manually in ANGLE at some point for perf.
|
|
std::lock_guard<std::mutex> lock(mPipelineCacheMutex);
|
|
|
|
if (mPipelineCacheInitialized)
|
|
{
|
|
*pipelineCache = &mPipelineCache;
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
// We should now recreate the pipeline cache with the blob cache pipeline data.
|
|
vk::PipelineCache pCache;
|
|
bool success = false;
|
|
ANGLE_TRY(initPipelineCache(vk::GetImpl(mDisplay), &pCache, &success));
|
|
if (success)
|
|
{
|
|
// Merge the newly created pipeline cache into the existing one.
|
|
mPipelineCache.merge(mDevice, mPipelineCache.getHandle(), 1, pCache.ptr());
|
|
}
|
|
mPipelineCacheInitialized = true;
|
|
pCache.destroy(mDevice);
|
|
|
|
*pipelineCache = &mPipelineCache;
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
const gl::Caps &RendererVk::getNativeCaps() const
|
|
{
|
|
ensureCapsInitialized();
|
|
return mNativeCaps;
|
|
}
|
|
|
|
const gl::TextureCapsMap &RendererVk::getNativeTextureCaps() const
|
|
{
|
|
ensureCapsInitialized();
|
|
return mNativeTextureCaps;
|
|
}
|
|
|
|
const gl::Extensions &RendererVk::getNativeExtensions() const
|
|
{
|
|
ensureCapsInitialized();
|
|
return mNativeExtensions;
|
|
}
|
|
|
|
const gl::Limitations &RendererVk::getNativeLimitations() const
|
|
{
|
|
ensureCapsInitialized();
|
|
return mNativeLimitations;
|
|
}
|
|
|
|
angle::Result RendererVk::getPipelineCacheSize(DisplayVk *displayVk, size_t *pipelineCacheSizeOut)
|
|
{
|
|
VkResult result = mPipelineCache.getCacheData(mDevice, pipelineCacheSizeOut, nullptr);
|
|
ANGLE_VK_TRY(displayVk, result);
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::syncPipelineCacheVk(DisplayVk *displayVk)
|
|
{
|
|
// TODO: Synchronize access to the pipeline/blob caches?
|
|
ASSERT(mPipelineCache.valid());
|
|
|
|
if (--mPipelineCacheVkUpdateTimeout > 0)
|
|
{
|
|
return angle::Result::Continue;
|
|
}
|
|
if (!mPipelineCacheDirty)
|
|
{
|
|
mPipelineCacheVkUpdateTimeout = kPipelineCacheVkUpdatePeriod;
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
mPipelineCacheVkUpdateTimeout = kPipelineCacheVkUpdatePeriod;
|
|
|
|
size_t pipelineCacheSize = 0;
|
|
ANGLE_TRY(getPipelineCacheSize(displayVk, &pipelineCacheSize));
|
|
// Make sure we will receive enough data to hold the pipeline cache header
|
|
// Table 7. Layout for pipeline cache header version VK_PIPELINE_CACHE_HEADER_VERSION_ONE
|
|
const size_t kPipelineCacheHeaderSize = 16 + VK_UUID_SIZE;
|
|
if (pipelineCacheSize < kPipelineCacheHeaderSize)
|
|
{
|
|
// No pipeline cache data to read, so return
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::MemoryBuffer *pipelineCacheData = nullptr;
|
|
ANGLE_VK_CHECK_ALLOC(displayVk,
|
|
displayVk->getScratchBuffer(pipelineCacheSize, &pipelineCacheData));
|
|
|
|
size_t oldPipelineCacheSize = pipelineCacheSize;
|
|
VkResult result =
|
|
mPipelineCache.getCacheData(mDevice, &pipelineCacheSize, pipelineCacheData->data());
|
|
// We don't need all of the cache data, so just make sure we at least got the header
|
|
// Vulkan Spec 9.6. Pipeline Cache
|
|
// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap9.html#pipelines-cache
|
|
// If pDataSize is less than what is necessary to store this header, nothing will be written to
|
|
// pData and zero will be written to pDataSize.
|
|
// Any data written to pData is valid and can be provided as the pInitialData member of the
|
|
// VkPipelineCacheCreateInfo structure passed to vkCreatePipelineCache.
|
|
if (ANGLE_UNLIKELY(pipelineCacheSize < kPipelineCacheHeaderSize))
|
|
{
|
|
WARN() << "Not enough pipeline cache data read.";
|
|
return angle::Result::Continue;
|
|
}
|
|
else if (ANGLE_UNLIKELY(result == VK_INCOMPLETE))
|
|
{
|
|
WARN() << "Received VK_INCOMPLETE: Old: " << oldPipelineCacheSize
|
|
<< ", New: " << pipelineCacheSize;
|
|
}
|
|
else
|
|
{
|
|
ANGLE_VK_TRY(displayVk, result);
|
|
}
|
|
|
|
// If vkGetPipelineCacheData ends up writing fewer bytes than requested, zero out the rest of
|
|
// the buffer to avoid leaking garbage memory.
|
|
ASSERT(pipelineCacheSize <= pipelineCacheData->size());
|
|
if (pipelineCacheSize < pipelineCacheData->size())
|
|
{
|
|
memset(pipelineCacheData->data() + pipelineCacheSize, 0,
|
|
pipelineCacheData->size() - pipelineCacheSize);
|
|
}
|
|
|
|
displayVk->getBlobCache()->putApplication(mPipelineCacheVkBlobKey, *pipelineCacheData);
|
|
mPipelineCacheDirty = false;
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
Serial RendererVk::issueShaderSerial()
|
|
{
|
|
return mShaderSerialFactory.generate();
|
|
}
|
|
|
|
// These functions look at the mandatory format for support, and fallback to querying the device (if
|
|
// necessary) to test the availability of the bits.
|
|
bool RendererVk::hasLinearImageFormatFeatureBits(VkFormat format,
|
|
const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
return hasFormatFeatureBits<&VkFormatProperties::linearTilingFeatures>(format, featureBits);
|
|
}
|
|
|
|
VkFormatFeatureFlags RendererVk::getImageFormatFeatureBits(
|
|
VkFormat format,
|
|
const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
return getFormatFeatureBits<&VkFormatProperties::optimalTilingFeatures>(format, featureBits);
|
|
}
|
|
|
|
bool RendererVk::hasImageFormatFeatureBits(VkFormat format,
|
|
const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
return hasFormatFeatureBits<&VkFormatProperties::optimalTilingFeatures>(format, featureBits);
|
|
}
|
|
|
|
bool RendererVk::hasBufferFormatFeatureBits(VkFormat format,
|
|
const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
return hasFormatFeatureBits<&VkFormatProperties::bufferFeatures>(format, featureBits);
|
|
}
|
|
|
|
void RendererVk::outputVmaStatString()
|
|
{
|
|
// Output the VMA stats string
|
|
// This JSON string can be passed to VmaDumpVis.py to generate a visualization of the
|
|
// allocations the VMA has performed.
|
|
char *statsString;
|
|
mAllocator.buildStatsString(&statsString, true);
|
|
INFO() << std::endl << statsString << std::endl;
|
|
mAllocator.freeStatsString(statsString);
|
|
}
|
|
|
|
angle::Result RendererVk::queueSubmit(vk::Context *context,
|
|
egl::ContextPriority priority,
|
|
const VkSubmitInfo &submitInfo,
|
|
vk::ResourceUseList *resourceList,
|
|
const vk::Fence *fence,
|
|
Serial *serialOut)
|
|
{
|
|
if (kOutputVmaStatsString)
|
|
{
|
|
outputVmaStatString();
|
|
}
|
|
|
|
ASSERT(!getFeatures().commandProcessor.enabled);
|
|
|
|
{
|
|
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
|
|
std::lock_guard<std::mutex> serialLock(mQueueSerialMutex);
|
|
VkFence handle = fence ? fence->getHandle() : VK_NULL_HANDLE;
|
|
ANGLE_VK_TRY(context, vkQueueSubmit(mQueues[priority], 1, &submitInfo, handle));
|
|
|
|
if (resourceList)
|
|
{
|
|
resourceList->releaseResourceUsesAndUpdateSerials(mCurrentQueueSerial);
|
|
}
|
|
*serialOut = mCurrentQueueSerial;
|
|
mLastSubmittedQueueSerial = mCurrentQueueSerial;
|
|
mCurrentQueueSerial = mQueueSerialFactory.generate();
|
|
}
|
|
|
|
ANGLE_TRY(cleanupGarbage(false));
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::queueSubmitOneOff(vk::Context *context,
|
|
vk::PrimaryCommandBuffer &&primary,
|
|
egl::ContextPriority priority,
|
|
const vk::Fence *fence,
|
|
Serial *serialOut)
|
|
{
|
|
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queueSubmitOneOff");
|
|
|
|
if (getFeatures().commandProcessor.enabled)
|
|
{
|
|
vk::CommandProcessorTask oneOffQueueSubmit;
|
|
oneOffQueueSubmit.initOneOffQueueSubmit(primary.getHandle(), priority, fence);
|
|
queueCommand(context, &oneOffQueueSubmit);
|
|
// TODO: https://issuetracker.google.com/170312581 - should go away with improved fence
|
|
// management
|
|
if (getFeatures().asynchronousCommandProcessing.enabled)
|
|
{
|
|
waitForCommandProcessorIdle(context);
|
|
}
|
|
*serialOut = getLastSubmittedQueueSerial();
|
|
}
|
|
else
|
|
{
|
|
|
|
VkSubmitInfo submitInfo = {};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = primary.ptr();
|
|
|
|
ANGLE_TRY(queueSubmit(context, priority, submitInfo, nullptr, fence, serialOut));
|
|
}
|
|
|
|
mPendingOneOffCommands.push_back({*serialOut, std::move(primary)});
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::queueWaitIdle(vk::Context *context, egl::ContextPriority priority)
|
|
{
|
|
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queueWaitIdle");
|
|
if (getFeatures().asynchronousCommandProcessing.enabled)
|
|
{
|
|
// Wait for all pending commands to get sent before issuing vkQueueWaitIdle
|
|
waitForCommandProcessorIdle(context);
|
|
}
|
|
{
|
|
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
|
|
ANGLE_VK_TRY(context, vkQueueWaitIdle(mQueues[priority]));
|
|
}
|
|
|
|
ANGLE_TRY(cleanupGarbage(false));
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
angle::Result RendererVk::deviceWaitIdle(vk::Context *context)
|
|
{
|
|
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::deviceWaitIdle");
|
|
|
|
if (getFeatures().asynchronousCommandProcessing.enabled)
|
|
{
|
|
// Wait for all pending commands to get sent before issuing vkQueueWaitIdle
|
|
waitForCommandProcessorIdle(context);
|
|
}
|
|
{
|
|
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
|
|
ANGLE_VK_TRY(context, vkDeviceWaitIdle(mDevice));
|
|
}
|
|
|
|
ANGLE_TRY(cleanupGarbage(false));
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
VkResult RendererVk::queuePresent(egl::ContextPriority priority,
|
|
const VkPresentInfoKHR &presentInfo)
|
|
{
|
|
ANGLE_TRACE_EVENT0("gpu.angle", "RendererVk::queuePresent");
|
|
|
|
ASSERT(!getFeatures().commandProcessor.enabled);
|
|
|
|
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
|
|
|
|
{
|
|
ANGLE_TRACE_EVENT0("gpu.angle", "vkQueuePresentKHR");
|
|
return vkQueuePresentKHR(mQueues[priority], &presentInfo);
|
|
}
|
|
}
|
|
|
|
angle::Result RendererVk::newSharedFence(vk::Context *context,
|
|
vk::Shared<vk::Fence> *sharedFenceOut)
|
|
{
|
|
bool gotRecycledFence = false;
|
|
vk::Fence fence;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mFenceRecyclerMutex);
|
|
if (!mFenceRecycler.empty())
|
|
{
|
|
mFenceRecycler.fetch(&fence);
|
|
gotRecycledFence = true;
|
|
}
|
|
}
|
|
if (gotRecycledFence)
|
|
{
|
|
ANGLE_VK_TRY(context, fence.reset(mDevice));
|
|
}
|
|
else
|
|
{
|
|
VkFenceCreateInfo fenceCreateInfo = {};
|
|
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
fenceCreateInfo.flags = 0;
|
|
ANGLE_VK_TRY(context, fence.init(mDevice, fenceCreateInfo));
|
|
}
|
|
sharedFenceOut->assign(mDevice, std::move(fence));
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
// Return a shared fence to be used for the next submit
|
|
// Fence may be shared with a Sync object.
|
|
// reset indicates that nextSubmitFence should be reset before returning. This ensures that the next
|
|
// request for a submit fence gets a fresh fence.
|
|
// TODO: https://issuetracker.google.com/issues/170312581 - move to CommandProcessor as part of
|
|
// fence ownership follow-up task.
|
|
angle::Result RendererVk::getNextSubmitFence(vk::Shared<vk::Fence> *sharedFenceOut, bool reset)
|
|
{
|
|
std::lock_guard<decltype(mNextSubmitFenceMutex)> lock(mNextSubmitFenceMutex);
|
|
if (!mNextSubmitFence.isReferenced())
|
|
{
|
|
ANGLE_TRY(newSharedFence(&mCommandProcessor, &mNextSubmitFence));
|
|
}
|
|
|
|
ASSERT(!sharedFenceOut->isReferenced());
|
|
sharedFenceOut->copy(getDevice(), mNextSubmitFence);
|
|
|
|
if (reset)
|
|
{
|
|
resetSharedFence(&mNextSubmitFence);
|
|
}
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
template <VkFormatFeatureFlags VkFormatProperties::*features>
|
|
VkFormatFeatureFlags RendererVk::getFormatFeatureBits(VkFormat format,
|
|
const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
ASSERT(static_cast<uint32_t>(format) < vk::kNumVkFormats);
|
|
VkFormatProperties &deviceProperties = mFormatProperties[format];
|
|
|
|
if (deviceProperties.bufferFeatures == kInvalidFormatFeatureFlags)
|
|
{
|
|
// If we don't have the actual device features, see if the requested features are mandatory.
|
|
// If so, there's no need to query the device.
|
|
const VkFormatProperties &mandatoryProperties = vk::GetMandatoryFormatSupport(format);
|
|
if (IsMaskFlagSet(mandatoryProperties.*features, featureBits))
|
|
{
|
|
return featureBits;
|
|
}
|
|
|
|
// Otherwise query the format features and cache it.
|
|
vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &deviceProperties);
|
|
// Workaround for some Android devices that don't indicate filtering
|
|
// support on D16_UNORM and they should.
|
|
if (mFeatures.forceD16TexFilter.enabled && format == VK_FORMAT_D16_UNORM)
|
|
{
|
|
deviceProperties.*features |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
|
}
|
|
}
|
|
|
|
return deviceProperties.*features & featureBits;
|
|
}
|
|
|
|
template <VkFormatFeatureFlags VkFormatProperties::*features>
|
|
bool RendererVk::hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const
|
|
{
|
|
return IsMaskFlagSet(getFormatFeatureBits<features>(format, featureBits), featureBits);
|
|
}
|
|
|
|
angle::Result RendererVk::cleanupGarbage(bool block)
|
|
{
|
|
Serial lastCompletedQueueSerial = getLastCompletedQueueSerial();
|
|
std::lock_guard<std::mutex> lock(mGarbageMutex);
|
|
|
|
for (auto garbageIter = mSharedGarbage.begin(); garbageIter != mSharedGarbage.end();)
|
|
{
|
|
// Possibly 'counter' should be always zero when we add the object to garbage.
|
|
vk::SharedGarbage &garbage = *garbageIter;
|
|
if (garbage.destroyIfComplete(this, lastCompletedQueueSerial))
|
|
{
|
|
garbageIter = mSharedGarbage.erase(garbageIter);
|
|
}
|
|
else
|
|
{
|
|
garbageIter++;
|
|
}
|
|
}
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
|
|
void RendererVk::onNewValidationMessage(const std::string &message)
|
|
{
|
|
mLastValidationMessage = message;
|
|
++mValidationMessageCount;
|
|
}
|
|
|
|
std::string RendererVk::getAndClearLastValidationMessage(uint32_t *countSinceLastClear)
|
|
{
|
|
*countSinceLastClear = mValidationMessageCount;
|
|
mValidationMessageCount = 0;
|
|
|
|
return std::move(mLastValidationMessage);
|
|
}
|
|
|
|
uint64_t RendererVk::getMaxFenceWaitTimeNs() const
|
|
{
|
|
constexpr uint64_t kMaxFenceWaitTimeNs = 120'000'000'000llu;
|
|
|
|
return kMaxFenceWaitTimeNs;
|
|
}
|
|
|
|
void RendererVk::onCompletedSerial(Serial serial)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
|
|
if (serial > mLastCompletedQueueSerial)
|
|
{
|
|
mLastCompletedQueueSerial = serial;
|
|
}
|
|
}
|
|
|
|
void RendererVk::setGlobalDebugAnnotator()
|
|
{
|
|
// If the vkCmd*DebugUtilsLabelEXT functions exist, and if the kEnableDebugMarkersVarName
|
|
// environment variable is set, initialize DebugAnnotatorVk to log the OpenGL ES commands that
|
|
// are used, for debuggers (e.g. AGI). Otherwise, uninitialize the global DebugAnnotator
|
|
// pointer so that applications run full speed.
|
|
bool enableDebugAnnotatorVk = false;
|
|
if (vkCmdBeginDebugUtilsLabelEXT)
|
|
{
|
|
std::string enabled = angle::GetEnvironmentVarOrAndroidProperty(
|
|
kEnableDebugMarkersVarName, kEnableDebugMarkersPropertyName);
|
|
if (!enabled.empty() && enabled.compare("0") != 0)
|
|
{
|
|
enableDebugAnnotatorVk = true;
|
|
}
|
|
}
|
|
#if defined(ANGLE_ENABLE_TRACE_ANDROID_LOGCAT)
|
|
// This will log all API commands to Android's logcat as well as create Vulkan debug markers.
|
|
enableDebugAnnotatorVk = true;
|
|
#endif
|
|
|
|
if (enableDebugAnnotatorVk)
|
|
{
|
|
// Install DebugAnnotatorVk so that GLES API commands will generate Vulkan debug markers
|
|
gl::InitializeDebugAnnotations(&mAnnotator);
|
|
}
|
|
else
|
|
{
|
|
// Install LoggingAnnotator so that other debug functionality will still work (e.g. Vulkan
|
|
// validation errors will cause dEQP tests to fail).
|
|
mDisplay->setGlobalDebugAnnotator();
|
|
}
|
|
}
|
|
|
|
void RendererVk::reloadVolkIfNeeded() const
|
|
{
|
|
#if defined(ANGLE_SHARED_LIBVULKAN)
|
|
if ((mInstance != VK_NULL_HANDLE) && (volkGetLoadedInstance() != mInstance))
|
|
{
|
|
volkLoadInstance(mInstance);
|
|
}
|
|
|
|
if ((mDevice != VK_NULL_HANDLE) && (volkGetLoadedDevice() != mDevice))
|
|
{
|
|
volkLoadDevice(mDevice);
|
|
}
|
|
#endif // defined(ANGLE_SHARED_LIBVULKAN)
|
|
}
|
|
|
|
angle::Result RendererVk::getCommandBufferOneOff(vk::Context *context,
|
|
vk::PrimaryCommandBuffer *commandBufferOut)
|
|
{
|
|
if (!mOneOffCommandPool.valid())
|
|
{
|
|
VkCommandPoolCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
ANGLE_VK_TRY(context, mOneOffCommandPool.init(mDevice, createInfo));
|
|
}
|
|
|
|
if (!mPendingOneOffCommands.empty() &&
|
|
mPendingOneOffCommands.front().serial < getLastCompletedQueueSerial())
|
|
{
|
|
*commandBufferOut = std::move(mPendingOneOffCommands.front().commandBuffer);
|
|
mPendingOneOffCommands.pop_front();
|
|
ANGLE_VK_TRY(context, commandBufferOut->reset());
|
|
}
|
|
else
|
|
{
|
|
VkCommandBufferAllocateInfo allocInfo = {};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
allocInfo.commandBufferCount = 1;
|
|
allocInfo.commandPool = mOneOffCommandPool.getHandle();
|
|
|
|
ANGLE_VK_TRY(context, commandBufferOut->init(context->getDevice(), allocInfo));
|
|
}
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
beginInfo.pInheritanceInfo = nullptr;
|
|
ANGLE_VK_TRY(context, commandBufferOut->begin(beginInfo));
|
|
|
|
return angle::Result::Continue;
|
|
}
|
|
} // namespace rx
|