blob: 30b5b9a011004a1e5d58dd9a0b114fdb34b4abba [file] [log] [blame]
//
// 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/libvulkan_loader.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/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/SyncVk.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);
#if defined(ANGLE_EXPOSE_NON_CONFORMANT_EXTENSIONS_AND_VERSIONS)
constexpr bool kExposeNonConformantExtensionsAndVersions = true;
#else
constexpr bool kExposeNonConformantExtensionsAndVersions = false;
#endif
#if defined(ANGLE_ENABLE_CRC_FOR_PIPELINE_CACHE)
constexpr bool kEnableCRCForPipelineCache = true;
#else
constexpr bool kEnableCRCForPipelineCache = false;
#endif
} // 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;
// Maximum size to use VMA image suballocation. Any allocation greater than or equal to this
// value will use a dedicated VkDeviceMemory.
constexpr size_t kImageSizeThresholdForDedicatedMemoryAllocation = 4 * 1024 * 1024;
// Pipeline cache header version. It should be incremented any time there is an update to the cache
// header or data structure.
constexpr uint16_t kPipelineCacheVersion = 1;
// Update the pipeline cache every this many swaps.
constexpr uint32_t kPipelineCacheVkUpdatePeriod = 60;
// The minimum version of Vulkan that ANGLE requires. If an instance or device below this version
// is encountered, initialization will fail.
constexpr uint32_t kMinimumVulkanAPIVersion = VK_API_VERSION_1_1;
// Per the Vulkan specification, 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.
//
// ANGLE specifically limits its core version to Vulkan 1.1 and relies on availability of
// extensions. While implementations are not required to expose an extension that is promoted to
// later versions, they always do so in practice. Avoiding later core versions helps keep the
// initialization logic simpler.
constexpr uint32_t kPreferredVulkanAPIVersion = VK_API_VERSION_1_1;
bool IsVulkan11(uint32_t apiVersion)
{
return apiVersion >= VK_API_VERSION_1_1;
}
bool IsVenus(uint32_t driverId, const char *deviceName)
{
// Where driver id is available, check against Venus driver id:
if (driverId != 0)
{
return driverId == VK_DRIVER_ID_MESA_VENUS;
}
// Otherwise, look for Venus in the device name.
return strstr(deviceName, "Venus") != nullptr;
}
bool IsQualcommOpenSource(uint32_t vendorId, uint32_t driverId, const char *deviceName)
{
if (!IsQualcomm(vendorId))
{
return false;
}
// Where driver id is available, distinguish by driver id:
if (driverId != 0)
{
return driverId != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
}
// Otherwise, look for Venus or Turnip in the device name.
return strstr(deviceName, "Venus") != nullptr || strstr(deviceName, "Turnip") != nullptr;
}
bool IsPixel()
{
if (!IsAndroid())
{
return false;
}
angle::SystemInfo info;
if (!angle::GetSystemInfo(&info))
{
return false;
}
return strstr(info.machineModelName.c_str(), "Pixel") != nullptr;
}
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/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://issuetracker.google.com/175584609
"VUID-vkCmdDraw-None-04584",
"VUID-vkCmdDrawIndexed-None-04584",
"VUID-vkCmdDrawIndirect-None-04584",
"VUID-vkCmdDrawIndirectCount-None-04584",
"VUID-vkCmdDrawIndexedIndirect-None-04584",
"VUID-vkCmdDrawIndexedIndirectCount-None-04584",
// http://anglebug.com/5912
"VUID-VkImageViewCreateInfo-pNext-01585",
// http://anglebug.com/6514
"vkEnumeratePhysicalDevices: One or more layers modified physical devices",
// When using Vulkan secondary command buffers, the command buffer is begun with the current
// framebuffer specified in pInheritanceInfo::framebuffer. If the framebuffer is multisampled
// and is resolved, an optimization would change the framebuffer to add the resolve target and
// use a subpass resolve operation instead. The following error complains that the framebuffer
// used to start the render pass and the one specified in pInheritanceInfo::framebuffer must be
// equal, which is not true in that case. In practice, this is benign, as the part of the
// framebuffer that's accessed by the command buffer is identically laid out.
// http://anglebug.com/6811
"VUID-vkCmdExecuteCommands-pCommandBuffers-00099",
// http://anglebug.com/7325
"VUID-vkCmdBindVertexBuffers2-pStrides-06209",
// http://anglebug.com/7729
"VUID-vkDestroySemaphore-semaphore-01137",
// http://anglebug.com/7843
"VUID-VkGraphicsPipelineCreateInfo-Vertex-07722",
// http://anglebug.com/7861
"VUID-vkCmdDraw-None-06887",
"VUID-vkCmdDraw-None-06886",
"VUID-vkCmdDrawIndexed-None-06887",
// http://anglebug.com/7865
"VUID-VkDescriptorImageInfo-imageView-06711",
"VUID-VkDescriptorImageInfo-descriptorType-06713",
// http://crbug.com/1412096
"VUID-VkImageCreateInfo-pNext-00990",
// http://crbug.com/1420265
"VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01912",
// http://anglebug.com/8076
"VUID-VkGraphicsPipelineCreateInfo-None-06573",
// http://anglebug.com/8119
"VUID-VkGraphicsPipelineCreateInfo-Input-07904",
"VUID-VkGraphicsPipelineCreateInfo-Input-07905",
"VUID-vkCmdDrawIndexed-None-07835",
"VUID-VkGraphicsPipelineCreateInfo-Input-08733",
// http://anglebug.com/8151
"VUID-vkCmdDraw-None-07844",
"VUID-vkCmdDraw-None-07845",
"VUID-vkCmdDraw-None-07848",
// https://anglebug.com/8128#c3
"VUID-VkBufferViewCreateInfo-buffer-00934",
// https://anglebug.com/8203
"VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870",
// https://anglebug.com/8237
"VUID-VkGraphicsPipelineCreateInfo-topology-08773",
// https://anglebug.com/8242
"VUID-vkCmdDraw-None-08608",
"VUID-vkCmdDrawIndexed-None-08608",
"VUID-vkCmdDraw-None-08753",
"VUID-vkCmdDrawIndexed-None-08753",
"VUID-vkCmdDraw-None-09003",
"VUID-vkCmdDrawIndexed-None-09003",
// https://anglebug.com/8334
"VUID-VkDescriptorImageInfo-imageView-07796",
// https://anglebug.com/8349
"VUID-VkSamplerCreateInfo-pNext-pNext",
};
// Validation messages that should be ignored only when VK_EXT_primitive_topology_list_restart is
// not present.
constexpr const char *kNoListRestartSkippedMessages[] = {
// http://anglebug.com/3832
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428",
};
// Some syncval errors are resolved in the presence of the NONE load or store render pass ops. For
// those, ANGLE makes no further attempt to resolve them and expects vendor support for the
// extensions instead. The list of skipped messages is split based on this support.
constexpr vk::SkippedSyncvalMessage kSkippedSyncvalMessages[] = {
// http://anglebug.com/6416
// http://anglebug.com/6421
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION, "
"write_barriers: 0, command: vkCmdEndRenderPass",
},
// These errors are caused by a feedback loop tests that don't produce correct Vulkan to begin
// with.
// http://anglebug.com/6417
// http://anglebug.com/7070
//
// Occassionally, this is due to VVL's lack of support for some extensions. For example,
// syncval doesn't properly account for VK_EXT_fragment_shader_interlock, which gives
// synchronization guarantees without the need for an image barrier.
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/4387
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"imageLayout: VK_IMAGE_LAYOUT_GENERAL",
"usage: SYNC_FRAGMENT_SHADER_SHADER_",
},
// http://anglebug.com/6551
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_EARLY_FRAGMENT_TESTS_DEPTH_"
"STENCIL_ATTACHMENT_WRITE|SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_LATE_"
"FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE|SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_"
"ATTACHMENT_"
"READ|SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, command: vkCmdEndRenderPass",
},
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ|SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_"
"ATTACHMENT_WRITE, command: vkCmdEndRenderPass",
},
// From: TraceTest.manhattan_31 with SwiftShader and
// VulkanPerformanceCounterTest.NewTextureDoesNotBreakRenderPass for both depth and stencil
// aspect. http://anglebug.com/6701
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"Hazard WRITE_AFTER_WRITE in subpass 0 for attachment 1 aspect ",
"during load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info (usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION",
},
// From various tests. The validation layer does not calculate the exact vertexCounts that's
// being accessed. http://anglebug.com/6725
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"Hazard READ_AFTER_WRITE for vertex",
"usage: SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ",
},
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"Hazard READ_AFTER_WRITE for index",
"usage: SYNC_INDEX_INPUT_INDEX_READ",
},
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"Hazard WRITE_AFTER_READ for",
"Access info (usage: SYNC_VERTEX_SHADER_SHADER_STORAGE_WRITE, prior_usage: "
"SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ",
},
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"Hazard WRITE_AFTER_READ for dstBuffer VkBuffer",
"Access info (usage: SYNC_COPY_TRANSFER_WRITE, prior_usage: "
"SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ",
},
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"Hazard WRITE_AFTER_READ for VkBuffer",
"Access info (usage: SYNC_COMPUTE_SHADER_SHADER_STORAGE_WRITE, prior_usage: "
"SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ",
},
// From: MultisampledRenderToTextureES3Test.TransformFeedbackTest. http://anglebug.com/6725
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"vkCmdBeginRenderPass: Hazard WRITE_AFTER_WRITE in subpass",
"write_barriers: "
"SYNC_TRANSFORM_FEEDBACK_EXT_TRANSFORM_FEEDBACK_COUNTER_READ_EXT|SYNC_TRANSFORM_FEEDBACK_"
"EXT_"
"TRANSFORM_FEEDBACK_COUNTER_WRITE_EXT",
},
// http://anglebug.com/8054 (VkNonDispatchableHandle on x86 bots)
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"Hazard READ_AFTER_WRITE for VkBuffer",
"usage: SYNC_VERTEX_SHADER_SHADER_STORAGE_READ",
},
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"Hazard READ_AFTER_WRITE for VkNonDispatchableHandle",
"usage: SYNC_VERTEX_SHADER_SHADER_STORAGE_READ",
},
// From: TraceTest.manhattan_31 with SwiftShader. These failures appears related to
// dynamic uniform buffers. The failures are gone if I force mUniformBufferDescriptorType to
// VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER. My guess is that syncval is not doing a fine grain enough
// range tracking with dynamic uniform buffers. http://anglebug.com/6725
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"usage: SYNC_VERTEX_SHADER_UNIFORM_READ",
},
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"usage: SYNC_VERTEX_SHADER_UNIFORM_READ",
},
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC",
},
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC",
},
// Coherent framebuffer fetch is enabled on some platforms that are known a priori to have the
// needed behavior, even though this is not specified in the Vulkan spec. These generate
// syncval errors that are benign on those platforms.
// http://anglebug.com/6870
// From: TraceTest.dead_by_daylight
// From: TraceTest.genshin_impact
{"SYNC-HAZARD-READ-AFTER-WRITE",
"vkCmdBeginRenderPass: Hazard READ_AFTER_WRITE in subpass 0 for attachment ",
"aspect color during load with loadOp VK_ATTACHMENT_LOAD_OP_LOAD. Access info (usage: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION, write_barriers: 0, command: vkCmdEndRenderPass",
true},
{"SYNC-HAZARD-WRITE-AFTER-WRITE",
"vkCmdBeginRenderPass: Hazard WRITE_AFTER_WRITE in subpass 0 for attachment ",
"image layout transition (old_layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, new_layout: "
"VK_IMAGE_LAYOUT_GENERAL). Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers:",
true},
// From: TraceTest.special_forces_group_2 http://anglebug.com/5592
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_FRAGMENT_SHADER_SHADER_",
},
// http://anglebug.com/7031
{"SYNC-HAZARD-READ-AFTER-WRITE",
"type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: "
"VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, binding #0, index 0. Access info (usage: "
"SYNC_COMPUTE_SHADER_SHADER_",
"", false},
// http://anglebug.com/7456
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, "
"imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL",
"Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_",
},
// From: TraceTest.life_is_strange http://anglebug.com/7711
{"SYNC-HAZARD-WRITE-AFTER-READ",
"vkCmdEndRenderPass: Hazard WRITE_AFTER_READ in subpass 0 for attachment 1 "
"depth aspect during store with storeOp VK_ATTACHMENT_STORE_OP_DONT_CARE. "
"Access info (usage: SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, "
"prior_usage: SYNC_FRAGMENT_SHADER_SHADER_"},
// From: TraceTest.life_is_strange http://anglebug.com/7711
{"SYNC-HAZARD-READ-AFTER-WRITE",
"type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, "
"imageLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL",
"usage: SYNC_FRAGMENT_SHADER_SHADER_"},
// From: TraceTest.diablo_immortal http://anglebug.com/7837
{"SYNC-HAZARD-WRITE-AFTER-WRITE", "Hazard WRITE_AFTER_WRITE for VkImageView ",
"Subpass #0, and pColorAttachments #0. Access info (usage: "
"SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION, write_barriers: 0, command: vkCmdEndRenderPass"},
// From: TraceTest.diablo_immortal http://anglebug.com/7837
{"SYNC-HAZARD-WRITE-AFTER-READ",
"load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info (usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_FRAGMENT_SHADER_SHADER_"},
// From: TraceTest.catalyst_black http://anglebug.com/7924
{"SYNC-HAZARD-WRITE-AFTER-READ",
"store with storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage: "
"SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_FRAGMENT_SHADER_SHADER_"},
};
// Messages that shouldn't be generated if storeOp=NONE is supported, otherwise they are expected.
constexpr vk::SkippedSyncvalMessage kSkippedSyncvalMessagesWithoutStoreOpNone[] = {
// These errors are generated when simultaneously using a read-only depth/stencil attachment as
// sampler. This is valid Vulkan.
//
// When storeOp=NONE is not present, ANGLE uses storeOp=STORE, but considers the image read-only
// and produces a hazard. ANGLE relies on storeOp=NONE and so this is not expected to be worked
// around.
//
// With storeOp=NONE, there is another bug where a depth/stencil attachment may use storeOp=NONE
// for depth while storeOp=DONT_CARE for stencil, and the latter causes a synchronization error
// (similarly to the previous case as DONT_CARE is also a write operation).
// http://anglebug.com/5962
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"depth aspect during store with storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage: "
"SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE",
"usage: SYNC_FRAGMENT_SHADER_SHADER_",
},
{
"SYNC-HAZARD-WRITE-AFTER-READ",
"stencil aspect during store with stencilStoreOp VK_ATTACHMENT_STORE_OP_STORE. Access info "
"(usage: SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE",
"usage: SYNC_FRAGMENT_SHADER_SHADER_",
},
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"imageLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL",
"usage: SYNC_FRAGMENT_SHADER_SHADER_",
},
// From: TraceTest.antutu_refinery http://anglebug.com/6663
{
"SYNC-HAZARD-READ-AFTER-WRITE",
"imageLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL",
"usage: SYNC_COMPUTE_SHADER_SHADER_SAMPLED_READ",
},
};
// Messages that shouldn't be generated if both loadOp=NONE and storeOp=NONE are supported,
// otherwise they are expected.
constexpr vk::SkippedSyncvalMessage kSkippedSyncvalMessagesWithoutLoadStoreOpNone[] = {
// This error is generated for multiple reasons:
//
// - http://anglebug.com/6411
// - http://anglebug.com/5371: This is resolved with storeOp=NONE
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION, prior_usage: "
"SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, write_barriers: 0, command: "
"vkCmdEndRenderPass",
},
// http://anglebug.com/6411
// http://anglebug.com/6584
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"aspect depth during load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info (usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION",
},
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"aspect stencil during load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info "
"(usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE",
},
// http://anglebug.com/5962
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"aspect stencil during load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info "
"(usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION",
},
{
"SYNC-HAZARD-WRITE-AFTER-WRITE",
"aspect stencil during load with loadOp VK_ATTACHMENT_LOAD_OP_DONT_CARE. Access info "
"(usage: "
"SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_IMAGE_LAYOUT_TRANSITION",
},
};
enum class DebugMessageReport
{
Ignore,
Print,
};
bool IsMessageInSkipList(const char *message,
const char *const skippedList[],
size_t skippedListSize)
{
for (size_t index = 0; index < skippedListSize; ++index)
{
if (strstr(message, skippedList[index]) != nullptr)
{
return true;
}
}
return false;
}
// Suppress validation errors that are known. Returns DebugMessageReport::Ignore in that case.
DebugMessageReport ShouldReportDebugMessage(RendererVk *renderer,
const char *messageId,
const char *message)
{
if (message == nullptr || messageId == nullptr)
{
return DebugMessageReport::Print;
}
// Check with non-syncval messages:
const std::vector<const char *> &skippedMessages = renderer->getSkippedValidationMessages();
if (IsMessageInSkipList(message, skippedMessages.data(), skippedMessages.size()))
{
return DebugMessageReport::Ignore;
}
// Then check with syncval messages:
const bool isFramebufferFetchUsed = renderer->isFramebufferFetchUsed();
for (const vk::SkippedSyncvalMessage &msg : renderer->getSkippedSyncvalMessages())
{
if (strstr(messageId, msg.messageId) == nullptr ||
strstr(message, msg.messageContents1) == nullptr ||
strstr(message, msg.messageContents2) == nullptr)
{
continue;
}
// If the error is due to exposing coherent framebuffer fetch (without
// VK_EXT_rasterization_order_attachment_access), but framebuffer fetch has not been used by
// the application, report it.
//
// Note that currently syncval doesn't support the
// VK_EXT_rasterization_order_attachment_access extension, so the syncval messages would
// continue to be produced despite the extension.
constexpr bool kSyncValSupportsRasterizationOrderExtension = false;
const bool hasRasterizationOrderExtension =
renderer->getFeatures().supportsRasterizationOrderAttachmentAccess.enabled &&
kSyncValSupportsRasterizationOrderExtension;
if (msg.isDueToNonConformantCoherentFramebufferFetch &&
(!isFramebufferFetchUsed || hasRasterizationOrderExtension))
{
return DebugMessageReport::Print;
}
// Otherwise ignore the message
return DebugMessageReport::Ignore;
}
return DebugMessageReport::Print;
}
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_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)
{
RendererVk *rendererVk = static_cast<RendererVk *>(userData);
// See if it's an issue we are aware of and don't want to be spammed about.
if (ShouldReportDebugMessage(rendererVk, callbackData->pMessageIdName,
callbackData->pMessage) == DebugMessageReport::Ignore)
{
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->onNewValidationMessage(msg);
if (isError)
{
ERR() << msg;
}
else
{
WARN() << msg;
}
return VK_FALSE;
}
VKAPI_ATTR void VKAPI_CALL
MemoryReportCallback(const VkDeviceMemoryReportCallbackDataEXT *callbackData, void *userData)
{
RendererVk *rendererVk = static_cast<RendererVk *>(userData);
rendererVk->processMemoryReportCallback(*callbackData);
}
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 &current, const gl::Version &lower)
{
return std::min(current, lower);
}
[[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;
}
[[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;
}
// Exclude memory type indices that include the host-visible bit from VMA image suballocation.
uint32_t GetMemoryTypeBitsExcludingHostVisible(RendererVk *renderer,
VkMemoryPropertyFlags propertyFlags,
uint32_t availableMemoryTypeBits)
{
const vk::MemoryProperties &memoryProperties = renderer->getMemoryProperties();
ASSERT(memoryProperties.getMemoryTypeCount() <= 32);
uint32_t memoryTypeBitsOut = availableMemoryTypeBits;
// For best allocation results, the memory type indices that include the host-visible flag bit
// are removed.
for (size_t memoryIndex : angle::BitSet<32>(availableMemoryTypeBits))
{
VkMemoryPropertyFlags memoryFlags =
memoryProperties.getMemoryType(static_cast<uint32_t>(memoryIndex)).propertyFlags;
if ((memoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
memoryTypeBitsOut &= ~(angle::Bit<uint32_t>(memoryIndex));
continue;
}
// If the protected bit is not required, all memory type indices with this bit should be
// ignored.
if ((memoryFlags & ~propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
{
memoryTypeBitsOut &= ~(angle::Bit<uint32_t>(memoryIndex));
}
}
return memoryTypeBitsOut;
}
// CRC16-CCITT is used for header before the pipeline cache key data.
uint16_t ComputeCRC16(const uint8_t *data, const size_t size)
{
constexpr uint16_t kPolynomialCRC16 = 0x8408;
uint16_t rem = 0;
for (size_t i = 0; i < size; i++)
{
rem ^= data[i];
for (int j = 0; j < 8; j++)
{
rem = (rem & 1) ? kPolynomialCRC16 ^ (rem >> 1) : rem >> 1;
}
}
return rem;
}
// Header data type used for the pipeline cache.
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
class CacheDataHeader
{
public:
void setData(uint16_t compressedDataCRC,
uint32_t cacheDataSize,
uint16_t numChunks,
uint16_t chunkIndex)
{
mVersion = kPipelineCacheVersion;
mCompressedDataCRC = compressedDataCRC;
mCacheDataSize = cacheDataSize;
mNumChunks = numChunks;
mChunkIndex = chunkIndex;
}
void getData(uint16_t *versionOut,
uint16_t *compressedDataCRCOut,
uint32_t *cacheDataSizeOut,
size_t *numChunksOut,
size_t *chunkIndexOut) const
{
*versionOut = mVersion;
*compressedDataCRCOut = mCompressedDataCRC;
*cacheDataSizeOut = mCacheDataSize;
*numChunksOut = static_cast<size_t>(mNumChunks);
*chunkIndexOut = static_cast<size_t>(mChunkIndex);
}
private:
// For pipeline cache, the values stored in key data has the following order:
// {headerVersion, compressedDataCRC, originalCacheSize, numChunks, chunkIndex;
// chunkCompressedData}. The header values are used to validate the data. For example, if the
// original and compressed sizes are 70000 bytes (68k) and 68841 bytes (67k), the compressed
// data will be divided into two chunks: {ver,crc0,70000,2,0;34421 bytes} and
// {ver,crc1,70000,2,1;34420 bytes}.
// The version is used to keep track of the cache format. Please note that kPipelineCacheVersion
// must be incremented by 1 in case of any updates to the cache header or data structure. While
// it is possible to modify the fields in the header, it is recommended to keep the version on
// top and the same size unless absolutely necessary.
uint16_t mVersion;
uint16_t mCompressedDataCRC;
uint32_t mCacheDataSize;
uint16_t mNumChunks;
uint16_t mChunkIndex;
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
// Pack header data for the pipeline cache key data.
void PackHeaderDataForPipelineCache(uint16_t compressedDataCRC,
uint32_t cacheDataSize,
uint16_t numChunks,
uint16_t chunkIndex,
CacheDataHeader *dataOut)
{
dataOut->setData(compressedDataCRC, cacheDataSize, numChunks, chunkIndex);
}
// Unpack header data from the pipeline cache key data.
void UnpackHeaderDataForPipelineCache(CacheDataHeader *data,
uint16_t *versionOut,
uint16_t *compressedDataCRCOut,
uint32_t *cacheDataSizeOut,
size_t *numChunksOut,
size_t *chunkIndexOut)
{
data->getData(versionOut, compressedDataCRCOut, cacheDataSizeOut, numChunksOut, chunkIndexOut);
}
void ComputePipelineCacheVkChunkKey(VkPhysicalDeviceProperties physicalDeviceProperties,
const uint8_t chunkIndex,
egl::BlobCache::Key *hashOut)
{
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 : physicalDeviceProperties.pipelineCacheUUID)
{
hashStream << std::hex << c;
}
// Add the vendor and device id too for good measure.
hashStream << std::hex << physicalDeviceProperties.vendorID;
hashStream << std::hex << physicalDeviceProperties.deviceID;
// Add chunkIndex to generate unique key for chunks.
hashStream << std::hex << static_cast<uint32_t>(chunkIndex);
const std::string &hashString = hashStream.str();
angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(hashString.c_str()),
hashString.length(), hashOut->data());
}
void CompressAndStorePipelineCacheVk(VkPhysicalDeviceProperties physicalDeviceProperties,
DisplayVk *displayVk,
ContextVk *contextVk,
const std::vector<uint8_t> &cacheData,
const size_t maxTotalSize)
{
// Though the pipeline cache will be compressed and divided into several chunks to store in blob
// cache, the largest total size of blob cache is only 2M in android now, so there is no use to
// handle big pipeline cache when android will reject it finally.
if (cacheData.size() >= maxTotalSize)
{
// TODO: handle the big pipeline cache. http://anglebug.com/4722
ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_LOW,
"Skip syncing pipeline cache data when it's larger than maxTotalSize.");
return;
}
// To make it possible to store more pipeline cache data, compress the whole pipelineCache.
angle::MemoryBuffer compressedData;
if (!egl::CompressBlobCacheData(cacheData.size(), cacheData.data(), &compressedData))
{
ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_LOW,
"Skip syncing pipeline cache data as it failed compression.");
return;
}
// If the size of compressedData is larger than (kMaxBlobCacheSize - sizeof(numChunks)),
// the pipelineCache still can't be stored in blob cache. Divide the large compressed
// pipelineCache into several parts to store seperately. There is no function to
// query the limit size in android.
constexpr size_t kMaxBlobCacheSize = 64 * 1024;
size_t compressedOffset = 0;
const size_t numChunks = UnsignedCeilDivide(static_cast<unsigned int>(compressedData.size()),
kMaxBlobCacheSize - sizeof(CacheDataHeader));
ASSERT(numChunks <= UINT16_MAX);
size_t chunkSize = UnsignedCeilDivide(static_cast<unsigned int>(compressedData.size()),
static_cast<unsigned int>(numChunks));
uint16_t compressedDataCRC = 0;
if (kEnableCRCForPipelineCache)
{
compressedDataCRC = ComputeCRC16(compressedData.data(), compressedData.size());
}
for (size_t chunkIndex = 0; chunkIndex < numChunks; ++chunkIndex)
{
if (chunkIndex == numChunks - 1)
{
chunkSize = compressedData.size() - compressedOffset;
}
angle::MemoryBuffer keyData;
if (!keyData.resize(sizeof(CacheDataHeader) + chunkSize))
{
ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_LOW,
"Skip syncing pipeline cache data due to out of memory.");
return;
}
// Add the header data, followed by the compressed data.
ASSERT(cacheData.size() <= UINT32_MAX);
CacheDataHeader headerData = {};
PackHeaderDataForPipelineCache(compressedDataCRC, static_cast<uint32_t>(cacheData.size()),
static_cast<uint16_t>(numChunks),
static_cast<uint16_t>(chunkIndex), &headerData);
memcpy(keyData.data(), &headerData, sizeof(CacheDataHeader));
memcpy(keyData.data() + sizeof(CacheDataHeader), compressedData.data() + compressedOffset,
chunkSize);
compressedOffset += chunkSize;
// Create unique hash key.
egl::BlobCache::Key chunkCacheHash;
ComputePipelineCacheVkChunkKey(physicalDeviceProperties, chunkIndex, &chunkCacheHash);
displayVk->getBlobCache()->putApplication(chunkCacheHash, keyData);
}
}
class CompressAndStorePipelineCacheTask : public angle::Closure
{
public:
CompressAndStorePipelineCacheTask(DisplayVk *displayVk,
ContextVk *contextVk,
std::vector<uint8_t> &&cacheData,
size_t kMaxTotalSize)
: mDisplayVk(displayVk),
mContextVk(contextVk),
mCacheData(std::move(cacheData)),
mMaxTotalSize(kMaxTotalSize)
{}
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "CompressAndStorePipelineCacheVk");
CompressAndStorePipelineCacheVk(mContextVk->getRenderer()->getPhysicalDeviceProperties(),
mDisplayVk, mContextVk, mCacheData, mMaxTotalSize);
}
private:
DisplayVk *mDisplayVk;
ContextVk *mContextVk;
std::vector<uint8_t> mCacheData;
size_t mMaxTotalSize;
};
class WaitableCompressEventImpl : public WaitableCompressEvent
{
public:
WaitableCompressEventImpl(std::shared_ptr<angle::WaitableEvent> waitableEvent,
std::shared_ptr<CompressAndStorePipelineCacheTask> compressTask)
: WaitableCompressEvent(waitableEvent), mCompressTask(compressTask)
{}
private:
std::shared_ptr<CompressAndStorePipelineCacheTask> mCompressTask;
};
angle::Result GetAndDecompressPipelineCacheVk(VkPhysicalDeviceProperties physicalDeviceProperties,
DisplayVk *displayVk,
angle::MemoryBuffer *uncompressedData,
bool *success)
{
// Make sure that the bool output is initialized to false.
*success = false;
// Compute the hash key of chunkIndex 0 and find the first cache data in blob cache.
egl::BlobCache::Key chunkCacheHash;
ComputePipelineCacheVkChunkKey(physicalDeviceProperties, 0, &chunkCacheHash);
egl::BlobCache::Value keyData;
size_t keySize = 0;
if (!displayVk->getBlobCache()->get(displayVk->getScratchBuffer(), chunkCacheHash, &keyData,
&keySize) ||
keyData.size() < sizeof(CacheDataHeader))
{
// Nothing in the cache.
return angle::Result::Continue;
}
// Get the number of chunks and other values from the header for data validation.
uint16_t cacheVersion;
uint16_t compressedDataCRC;
uint32_t uncompressedCacheDataSize;
size_t numChunks;
size_t chunkIndex0;
CacheDataHeader headerData = {};
memcpy(&headerData, keyData.data(), sizeof(CacheDataHeader));
UnpackHeaderDataForPipelineCache(&headerData, &cacheVersion, &compressedDataCRC,
&uncompressedCacheDataSize, &numChunks, &chunkIndex0);
if (cacheVersion == kPipelineCacheVersion)
{
// The data must not contain corruption.
if (chunkIndex0 != 0 || numChunks == 0 || uncompressedCacheDataSize == 0)
{
FATAL() << "Unexpected values while unpacking chunk index 0: "
<< "cacheVersion = " << cacheVersion << ", chunkIndex = " << chunkIndex0
<< ", numChunks = " << numChunks
<< ", uncompressedCacheDataSize = " << uncompressedCacheDataSize;
}
}
else
{
// Either the header structure has been updated, or the header value has been changed.
if (cacheVersion > kPipelineCacheVersion + (1 << 8))
{
// TODO(abdolrashidi): Data corruption in the version should result in a fatal error.
// For now, a warning is shown instead, but it should change when the version field is
// no longer new.
WARN() << "Existing cache version is significantly greater than the new version"
", possibly due to data corruption: "
<< "newVersion = " << kPipelineCacheVersion
<< ", existingVersion = " << cacheVersion;
}
else
{
WARN() << "Change in cache header version detected: "
<< "newVersion = " << kPipelineCacheVersion
<< ", existingVersion = " << cacheVersion;
}
return angle::Result::Continue;
}
size_t chunkSize = keySize - sizeof(CacheDataHeader);
size_t compressedSize = 0;
// Allocate enough memory.
angle::MemoryBuffer compressedData;
ANGLE_VK_CHECK(displayVk, compressedData.resize(chunkSize * numChunks),
VK_ERROR_INITIALIZATION_FAILED);
// To combine the parts of the pipelineCache data.
for (size_t chunkIndex = 0; chunkIndex < numChunks; ++chunkIndex)
{
// Get the unique key by chunkIndex.
ComputePipelineCacheVkChunkKey(physicalDeviceProperties, chunkIndex, &chunkCacheHash);
if (!displayVk->getBlobCache()->get(displayVk->getScratchBuffer(), chunkCacheHash, &keyData,
&keySize) ||
keyData.size() < sizeof(CacheDataHeader))
{
// Can't find every part of the cache data.
WARN() << "Failed to get pipeline cache chunk " << chunkIndex << " of " << numChunks;
return angle::Result::Continue;
}
// Validate the header values and ensure there is enough space to store.
uint16_t checkCacheVersion;
uint16_t checkCompressedDataCRC;
uint32_t checkUncompressedCacheDataSize;
size_t checkNumChunks;
size_t checkChunkIndex;
memcpy(&headerData, keyData.data(), sizeof(CacheDataHeader));
UnpackHeaderDataForPipelineCache(&headerData, &checkCacheVersion, &checkCompressedDataCRC,
&checkUncompressedCacheDataSize, &checkNumChunks,
&checkChunkIndex);
chunkSize = keySize - sizeof(CacheDataHeader);
bool isHeaderDataCorrupted =
(checkCacheVersion != cacheVersion) || (checkNumChunks != numChunks) ||
(checkUncompressedCacheDataSize != uncompressedCacheDataSize) ||
(checkCompressedDataCRC != compressedDataCRC) || (checkChunkIndex != chunkIndex) ||
(compressedData.size() < compressedSize + chunkSize);
if (isHeaderDataCorrupted)
{
WARN() << "Pipeline cache chunk header corrupted: "
<< "checkCacheVersion = " << checkCacheVersion
<< ", cacheVersion = " << cacheVersion << ", checkNumChunks = " << checkNumChunks
<< ", numChunks = " << numChunks
<< ", checkUncompressedCacheDataSize = " << checkUncompressedCacheDataSize
<< ", uncompressedCacheDataSize = " << uncompressedCacheDataSize
<< ", checkCompressedDataCRC = " << checkCompressedDataCRC
<< ", compressedDataCRC = " << compressedDataCRC
<< ", checkChunkIndex = " << checkChunkIndex << ", chunkIndex = " << chunkIndex
<< ", compressedData.size() = " << compressedData.size()
<< ", (compressedSize + chunkSize) = " << (compressedSize + chunkSize);
return angle::Result::Continue;
}
memcpy(compressedData.data() + compressedSize, keyData.data() + sizeof(CacheDataHeader),
chunkSize);
compressedSize += chunkSize;
}
// CRC for compressed data and size for decompressed data should match the values in the header.
if (kEnableCRCForPipelineCache)
{
uint16_t computedCompressedDataCRC = ComputeCRC16(compressedData.data(), compressedSize);
if (computedCompressedDataCRC != compressedDataCRC)
{
if (compressedDataCRC == 0)
{
// This could be due to the cache being populated before kEnableCRCForPipelineCache
// was enabled.
WARN() << "Expected CRC = " << compressedDataCRC
<< ", Actual CRC = " << computedCompressedDataCRC;
return angle::Result::Continue;
}
// If the expected CRC is non-zero and does not match the actual CRC from the data,
// there has been an unexpected data corruption.
ERR() << "Expected CRC = " << compressedDataCRC
<< ", Actual CRC = " << computedCompressedDataCRC;
ERR() << "Data extracted from the cache headers: " << std::hex
<< ", compressedDataCRC = 0x" << compressedDataCRC << "numChunks = 0x"
<< numChunks << ", uncompressedCacheDataSize = 0x" << uncompressedCacheDataSize;
FATAL() << "CRC check failed; possible pipeline cache data corruption.";
return angle::Result::Stop;
}
}
ANGLE_VK_CHECK(
displayVk,
egl::DecompressBlobCacheData(compressedData.data(), compressedSize, uncompressedData),
VK_ERROR_INITIALIZATION_FAILED);
if (uncompressedData->size() != uncompressedCacheDataSize)
{
WARN() << "Expected uncompressed size = " << uncompressedCacheDataSize
<< ", Actual uncompressed size = " << uncompressedData->size();
return angle::Result::Continue;
}
*success = true;
return angle::Result::Continue;
}
// 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";
ANGLE_INLINE gl::ShadingRate GetShadingRateFromVkExtent(const VkExtent2D &extent)
{
if (extent.width == 1 && extent.height == 2)
{
return gl::ShadingRate::_1x2;
}
else if (extent.width == 2 && extent.height == 1)
{
return gl::ShadingRate::_2x1;
}
else if (extent.width == 2 && extent.height == 2)
{
return gl::ShadingRate::_2x2;
}
else if (extent.width == 4 && extent.height == 2)
{
return gl::ShadingRate::_4x2;
}
else if (extent.width == 4 && extent.height == 4)
{
return gl::ShadingRate::_4x4;
}
return gl::ShadingRate::_1x1;
}
} // namespace
// OneOffCommandPool implementation.
OneOffCommandPool::OneOffCommandPool() : mProtectionType(vk::ProtectionType::InvalidEnum) {}
void OneOffCommandPool::init(vk::ProtectionType protectionType)
{
ASSERT(!mCommandPool.valid());
mProtectionType = protectionType;
}
void OneOffCommandPool::destroy(VkDevice device)
{
std::unique_lock<std::mutex> lock(mMutex);
for (PendingOneOffCommands &pending : mPendingCommands)
{
pending.commandBuffer.releaseHandle();
}
mCommandPool.destroy(device);
mProtectionType = vk::ProtectionType::InvalidEnum;
}
angle::Result OneOffCommandPool::getCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut)
{
std::unique_lock<std::mutex> lock(mMutex);
if (!mPendingCommands.empty() &&
context->getRenderer()->hasResourceUseFinished(mPendingCommands.front().use))
{
*commandBufferOut = std::move(mPendingCommands.front().commandBuffer);
mPendingCommands.pop_front();
ANGLE_VK_TRY(context, commandBufferOut->reset());
}
else
{
if (!mCommandPool.valid())
{
VkCommandPoolCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT |
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
ASSERT(mProtectionType == vk::ProtectionType::Unprotected ||
mProtectionType == vk::ProtectionType::Protected);
if (mProtectionType == vk::ProtectionType::Protected)
{
createInfo.flags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
}
ANGLE_VK_TRY(context, mCommandPool.init(context->getDevice(), createInfo));
}
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
allocInfo.commandPool = mCommandPool.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;
}
void OneOffCommandPool::releaseCommandBuffer(const QueueSerial &submitQueueSerial,
vk::PrimaryCommandBuffer &&primary)
{
std::unique_lock<std::mutex> lock(mMutex);
mPendingCommands.push_back({vk::ResourceUse(submitQueueSerial), std::move(primary)});
}
// RendererVk implementation.
RendererVk::RendererVk()
: mDisplay(nullptr),
mLibVulkanLibrary(nullptr),
mCapsInitialized(false),
mInstanceVersion(0),
mDeviceVersion(0),
mInstance(VK_NULL_HANDLE),
mEnableValidationLayers(false),
mEnableDebugUtils(false),
mAngleDebuggerMode(false),
mEnabledICD(angle::vk::ICD::Default),
mDebugUtilsMessenger(VK_NULL_HANDLE),
mPhysicalDevice(VK_NULL_HANDLE),
mMaxVertexAttribDivisor(1),
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mMaxVertexAttribStride(0),
mDefaultUniformBufferSize(kPreferredDefaultUniformBufferSize),
mDevice(VK_NULL_HANDLE),
mDeviceLost(false),
mSuballocationGarbageSizeInBytes(0),
mPendingSuballocationGarbageSizeInBytes(0),
mSuballocationGarbageDestroyed(0),
mSuballocationGarbageSizeInBytesCachedAtomic(0),
mCoherentStagingBufferMemoryTypeIndex(kInvalidMemoryTypeIndex),
mNonCoherentStagingBufferMemoryTypeIndex(kInvalidMemoryTypeIndex),
mStagingBufferAlignment(1),
mHostVisibleVertexConversionBufferMemoryTypeIndex(kInvalidMemoryTypeIndex),
mDeviceLocalVertexConversionBufferMemoryTypeIndex(kInvalidMemoryTypeIndex),
mVertexConversionBufferAlignment(1),
mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod),
mPipelineCacheSizeAtLastSync(0),
mPipelineCacheInitialized(false),
mValidationMessageCount(0),
mCommandProcessor(this, &mCommandQueue),
mSupportedVulkanPipelineStageMask(0),
mSupportedVulkanShaderStageMask(0),
mMemoryAllocationTracker(MemoryAllocationTracker(this))
{
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() {}
bool RendererVk::hasSharedGarbage()
{
std::unique_lock<std::mutex> lock(mGarbageMutex);
return !mSharedGarbage.empty() || !mPendingSubmissionGarbage.empty() ||
!mSuballocationGarbage.empty() || !mPendingSubmissionSuballocationGarbage.empty();
}
void RendererVk::onDestroy(vk::Context *context)
{
if (isDeviceLost())
{
handleDeviceLost();
}
mCommandProcessor.destroy(context);
mCommandQueue.destroy(context);
// mCommandQueue.destroy should already set "last completed" serials to infinite.
cleanupGarbage();
ASSERT(!hasSharedGarbage());
ASSERT(mOrphanedBufferBlocks.empty());
for (OneOffCommandPool &oneOffCommandPool : mOneOffCommandPoolMap)
{
oneOffCommandPool.destroy(mDevice);
}
mPipelineCache.destroy(mDevice);
mSamplerCache.destroy(this);
mYuvConversionCache.destroy(this);
mVkFormatDescriptorCountMap.clear();
mOutsideRenderPassCommandBufferRecycler.onDestroy();
mRenderPassCommandBufferRecycler.onDestroy();
mImageMemorySuballocator.destroy(this);
mAllocator.destroy();
// When the renderer is being destroyed, it is possible to check if all the allocated memory
// throughout the execution has been freed.
mMemoryAllocationTracker.onDestroy();
if (mDevice)
{
vkDestroyDevice(mDevice, nullptr);
mDevice = VK_NULL_HANDLE;
}
if (mDebugUtilsMessenger)
{
vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugUtilsMessenger, nullptr);
}
logCacheStats();
if (mInstance)
{
vkDestroyInstance(mInstance, nullptr);
mInstance = VK_NULL_HANDLE;
}
if (mCompressEvent)
{
mCompressEvent->wait();
mCompressEvent.reset();
}
mMemoryProperties.destroy();
mPhysicalDevice = VK_NULL_HANDLE;
mEnabledInstanceExtensions.clear();
mEnabledDeviceExtensions.clear();
ASSERT(!hasSharedGarbage());
if (mLibVulkanLibrary)
{
angle::CloseSystemLibrary(mLibVulkanLibrary);
mLibVulkanLibrary = nullptr;
}
}
void RendererVk::notifyDeviceLost()
{
mDeviceLost = true;
mDisplay->notifyDeviceLost();
}
bool RendererVk::isDeviceLost() const
{
return mDeviceLost;
}
angle::Result RendererVk::enableInstanceExtensions(
DisplayVk *displayVk,
const VulkanLayerVector &enabledInstanceLayerNames,
const char *wsiExtension,
bool canLoadDebugUtils)
{
// Enumerate instance extensions that are provided by the vulkan implementation and implicit
// layers.
uint32_t instanceExtensionCount = 0;
{
ANGLE_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceExtensionProperties(
nullptr, &instanceExtensionCount, nullptr));
}
std::vector<VkExtensionProperties> instanceExtensionProps(instanceExtensionCount);
if (instanceExtensionCount > 0)
{
ANGLE_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk,
vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount,
instanceExtensionProps.data()));
// In case fewer items were returned than requested, resize instanceExtensionProps to the
// number of extensions returned (i.e. instanceExtensionCount).
instanceExtensionProps.resize(instanceExtensionCount);
}
// 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_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceExtensionProperties(
layerName, &instanceLayerExtensionCount, nullptr));
}
instanceExtensionProps.resize(previousExtensionCount + instanceLayerExtensionCount);
{
ANGLE_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceExtensionProperties(
layerName, &instanceLayerExtensionCount,
instanceExtensionProps.data() + previousExtensionCount));
}
// In case fewer items were returned than requested, resize instanceExtensionProps to the
// number of extensions returned (i.e. instanceLayerExtensionCount).
instanceExtensionProps.resize(previousExtensionCount + instanceLayerExtensionCount);
}
// Get the list of instance extensions that are available.
vk::ExtensionNameList instanceExtensionNames;
if (!instanceExtensionProps.empty())
{
for (const VkExtensionProperties &i : instanceExtensionProps)
{
instanceExtensionNames.push_back(i.extensionName);
}
std::sort(instanceExtensionNames.begin(), instanceExtensionNames.end(), StrLess);
}
// Set ANGLE features that depend on instance extensions
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsSurfaceCapabilities2Extension,
ExtensionFound(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, instanceExtensionNames));
ANGLE_FEATURE_CONDITION(&mFeatures, supportsSurfaceProtectedCapabilitiesExtension,
ExtensionFound(VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME,
instanceExtensionNames));
// TODO: Validation layer has a bug when vkGetPhysicalDeviceSurfaceFormats2KHR is called
// on Mock ICD with surface handle set as VK_NULL_HANDLE. http://anglebug.com/7631
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsSurfacelessQueryExtension,
ExtensionFound(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, instanceExtensionNames) &&
!isMockICDEnabled());
// VK_KHR_external_fence_capabilities and VK_KHR_extenral_semaphore_capabilities are promoted to
// core in Vulkan 1.1
ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalFenceCapabilities, true);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalSemaphoreCapabilities, true);
// On macOS, there is no native Vulkan driver, so we need to enable the
// portability enumeration extension to allow use of MoltenVK.
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsPortabilityEnumeration,
ExtensionFound(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, instanceExtensionNames));
ANGLE_FEATURE_CONDITION(&mFeatures, enablePortabilityEnumeration,
mFeatures.supportsPortabilityEnumeration.enabled && IsApple());
// Enable extensions that could be used
if (displayVk->isUsingSwapchain())
{
mEnabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
if (ExtensionFound(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, instanceExtensionNames))
{
mEnabledInstanceExtensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
}
}
if (wsiExtension)
{
mEnabledInstanceExtensions.push_back(wsiExtension);
}
mEnableDebugUtils = canLoadDebugUtils && mEnableValidationLayers &&
ExtensionFound(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instanceExtensionNames);
if (mEnableDebugUtils)
{
mEnabledInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
if (mFeatures.supportsSurfaceCapabilities2Extension.enabled)
{
mEnabledInstanceExtensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
}
if (mFeatures.supportsSurfaceProtectedCapabilitiesExtension.enabled)
{
mEnabledInstanceExtensions.push_back(VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME);
}
if (mFeatures.supportsSurfacelessQueryExtension.enabled)
{
mEnabledInstanceExtensions.push_back(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME);
}
if (ExtensionFound(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, instanceExtensionNames))
{
mEnabledInstanceExtensions.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
}
if (mFeatures.enablePortabilityEnumeration.enabled)
{
mEnabledInstanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
}
// Verify the required extensions are in the extension names set. Fail if not.
std::sort(mEnabledInstanceExtensions.begin(), mEnabledInstanceExtensions.end(), StrLess);
ANGLE_VK_TRY(displayVk,
VerifyExtensionsPresent(instanceExtensionNames, mEnabledInstanceExtensions));
return angle::Result::Continue;
}
angle::Result RendererVk::initialize(DisplayVk *displayVk,
egl::Display *display,
const char *wsiExtension,
const char *wsiLayer)
{
bool canLoadDebugUtils = true;
#if defined(ANGLE_SHARED_LIBVULKAN)
{
ANGLE_SCOPED_DISABLE_MSAN();
mLibVulkanLibrary = angle::vk::OpenLibVulkan();
ANGLE_VK_CHECK(displayVk, mLibVulkanLibrary, VK_ERROR_INITIALIZATION_FAILED);
PFN_vkGetInstanceProcAddr vulkanLoaderGetInstanceProcAddr =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(
angle::GetLibrarySymbol(mLibVulkanLibrary, "vkGetInstanceProcAddr"));
// Set all vk* function ptrs
volkInitializeCustom(vulkanLoaderGetInstanceProcAddr);
uint32_t ver = volkGetInstanceVersion();
if (!IsAndroid() && ver < VK_MAKE_VERSION(1, 1, 91))
{
// http://crbug.com/1205999 - non-Android Vulkan Loader versions before 1.1.91 have a
// bug which prevents loading VK_EXT_debug_utils function pointers.
canLoadDebugUtils = false;
}
}
#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_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk, vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
}
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerCount);
if (instanceLayerCount > 0)
{
ANGLE_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
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);
}
auto enumerateInstanceVersion = reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
uint32_t highestApiVersion = mInstanceVersion = VK_API_VERSION_1_0;
if (enumerateInstanceVersion)
{
{
ANGLE_SCOPED_DISABLE_LSAN();
ANGLE_SCOPED_DISABLE_MSAN();
ANGLE_VK_TRY(displayVk, enumerateInstanceVersion(&mInstanceVersion));
}
if (IsVulkan11(mInstanceVersion))
{
// This is the highest version of core Vulkan functionality that ANGLE uses. Per the
// Vulkan spec, the application is allowed to specify a higher version than supported by
// the instance. ANGLE still respects the *device's* version.
highestApiVersion = kPreferredVulkanAPIVersion;
}
}
if (mInstanceVersion < kMinimumVulkanAPIVersion)
{
WARN() << "ANGLE Requires a minimum Vulkan instance version of 1.1";
ANGLE_VK_TRY(displayVk, VK_ERROR_INCOMPATIBLE_DRIVER);
}
ANGLE_TRY(enableInstanceExtensions(displayVk, enabledInstanceLayerNames, wsiExtension,
canLoadDebugUtils));
const std::string appName = angle::GetExecutableName();
mApplicationInfo = {};
mApplicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
mApplicationInfo.pApplicationName = appName.c_str();
mApplicationInfo.applicationVersion = 1;
mApplicationInfo.pEngineName = "ANGLE";
mApplicationInfo.engineVersion = 1;
mApplicationInfo.apiVersion = highestApiVersion;
VkInstanceCreateInfo instanceInfo = {};
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceInfo.flags = 0;
instanceInfo.pApplicationInfo = &mApplicationInfo;
// Enable requested layers and extensions.
instanceInfo.enabledExtensionCount = static_cast<uint32_t>(mEnabledInstanceExtensions.size());
instanceInfo.ppEnabledExtensionNames =
mEnabledInstanceExtensions.empty() ? nullptr : mEnabledInstanceExtensions.data();
instanceInfo.enabledLayerCount = static_cast<uint32_t>(enabledInstanceLayerNames.size());
instanceInfo.ppEnabledLayerNames = enabledInstanceLayerNames.data();
// On macOS, there is no native Vulkan driver, so we need to enable the
// portability enumeration extension to allow use of MoltenVK.
if (mFeatures.enablePortabilityEnumeration.enabled)
{
instanceInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}
// http://anglebug.com/7050 - Shader validation caching is broken on Android
VkValidationFeaturesEXT validationFeatures = {};
VkValidationFeatureDisableEXT disabledFeatures[] = {
VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT};
if (mEnableValidationLayers && IsAndroid())
{
validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
validationFeatures.disabledValidationFeatureCount = 1;
validationFeatures.pDisabledValidationFeatures = disabledFeatures;
vk::AddToPNextChain(&instanceInfo, &validationFeatures);
}
{
ANGLE_SCOPED_DISABLE_MSAN();
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)
initInstanceExtensionEntryPoints();
}
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));
}
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()));
uint32_t preferredVendorId =
static_cast<uint32_t>(attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0));
uint32_t preferredDeviceId =
static_cast<uint32_t>(attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0));
ChoosePhysicalDevice(vkGetPhysicalDeviceProperties, physicalDevices, mEnabledICD,
preferredVendorId, preferredDeviceId, &mPhysicalDevice,
&mPhysicalDeviceProperties);
// The device version that is assumed by ANGLE is the minimum of the actual device version and
// the highest it's allowed to use.
mDeviceVersion = std::min(mPhysicalDeviceProperties.apiVersion, highestApiVersion);
if (mDeviceVersion < kMinimumVulkanAPIVersion)
{
WARN() << "ANGLE Requires a minimum Vulkan device version of 1.1";
ANGLE_VK_TRY(displayVk, VK_ERROR_INCOMPATIBLE_DRIVER);
}
mGarbageCollectionFlushThreshold =
static_cast<uint32_t>(mPhysicalDeviceProperties.limits.maxMemoryAllocationCount *
kPercentMaxMemoryAllocationCount);
vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysicalDeviceFeatures);
// Ensure we can find a graphics queue family.
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount, nullptr);
ANGLE_VK_CHECK(displayVk, queueFamilyCount > 0, VK_ERROR_INITIALIZATION_FAILED);
mQueueFamilyProperties.resize(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount,
mQueueFamilyProperties.data());
uint32_t queueFamilyMatchCount = 0;
// Try first for a protected graphics queue family
uint32_t firstGraphicsQueueFamily = vk::QueueFamily::FindIndex(
mQueueFamilyProperties,
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_PROTECTED_BIT), 0,
&queueFamilyMatchCount);
// else just a graphics queue family
if (queueFamilyMatchCount == 0)
{
firstGraphicsQueueFamily = vk::QueueFamily::FindIndex(
mQueueFamilyProperties, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT), 0,
&queueFamilyMatchCount);
}
ANGLE_VK_CHECK(displayVk, queueFamilyMatchCount > 0, VK_ERROR_INITIALIZATION_FAILED);
// Store the physical device memory properties so we can find the right memory pools.
mMemoryProperties.init(mPhysicalDevice);
ANGLE_VK_CHECK(displayVk, mMemoryProperties.getMemoryTypeCount() > 0,
VK_ERROR_INITIALIZATION_FAILED);
// The counters for the memory allocation tracker should be initialized.
// Each memory allocation could be made in one of the available memory heaps. We initialize the
// per-heap memory allocation trackers for MemoryAllocationType objects here, after
// mMemoryProperties has been set up.
mMemoryAllocationTracker.initMemoryTrackers();
// Determine the threshold for pending garbage sizes.
calculatePendingGarbageSizeLimit();
// 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 (queueFamilyMatchCount == 1)
{
ANGLE_TRY(initializeDevice(displayVk, firstGraphicsQueueFamily));
}
WARN() << "ANGLE VMA version: " << ANGLE_VMA_VERSION;
ANGLE_TRY(initializeMemoryAllocator(displayVk));
// Initialize the format table.
mFormatTable.initialize(this, &mNativeTextureCaps);
setGlobalDebugAnnotator();
// Null terminate the extension list returned for EGL_VULKAN_INSTANCE_EXTENSIONS_ANGLE.
mEnabledInstanceExtensions.push_back(nullptr);
for (vk::ProtectionType protectionType : angle::AllEnums<vk::ProtectionType>())
{
mOneOffCommandPoolMap[protectionType].init(protectionType);
}
return angle::Result::Continue;
}
angle::Result RendererVk::initializeMemoryAllocator(DisplayVk *displayVk)
{
// 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.
mPreferredLargeHeapBlockSize = 4 * 1024 * 1024;
// Create VMA allocator
ANGLE_VK_TRY(displayVk,
mAllocator.init(mPhysicalDevice, mDevice, mInstance, mApplicationInfo.apiVersion,
mPreferredLargeHeapBlockSize));
// Figure out the alignment for default buffer allocations
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.flags = 0;
createInfo.size = 4096;
createInfo.usage = GetDefaultBufferUsageFlags(this);
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
vk::DeviceScoped<vk::Buffer> tempBuffer(mDevice);
tempBuffer.get().init(mDevice, createInfo);
VkMemoryRequirements defaultBufferMemoryRequirements;
tempBuffer.get().getMemoryRequirements(mDevice, &defaultBufferMemoryRequirements);
ASSERT(gl::isPow2(defaultBufferMemoryRequirements.alignment));
const VkPhysicalDeviceLimits &limitsVk = getPhysicalDeviceProperties().limits;
ASSERT(gl::isPow2(limitsVk.minUniformBufferOffsetAlignment));
ASSERT(gl::isPow2(limitsVk.minStorageBufferOffsetAlignment));
ASSERT(gl::isPow2(limitsVk.minTexelBufferOffsetAlignment));
ASSERT(gl::isPow2(limitsVk.minMemoryMapAlignment));
mDefaultBufferAlignment =
std::max({static_cast<size_t>(limitsVk.minUniformBufferOffsetAlignment),
static_cast<size_t>(limitsVk.minStorageBufferOffsetAlignment),
static_cast<size_t>(limitsVk.minTexelBufferOffsetAlignment),
static_cast<size_t>(limitsVk.minMemoryMapAlignment),
static_cast<size_t>(defaultBufferMemoryRequirements.alignment)});
// Initialize staging buffer memory type index and alignment.
// These buffers will only be used as transfer sources or transfer targets.
createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VkMemoryPropertyFlags requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
bool persistentlyMapped = mFeatures.persistentlyMappedBuffers.enabled;
// Uncached coherent staging buffer
VkMemoryPropertyFlags preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
ANGLE_VK_TRY(displayVk, mAllocator.findMemoryTypeIndexForBufferInfo(
createInfo, requiredFlags, preferredFlags, persistentlyMapped,
&mCoherentStagingBufferMemoryTypeIndex));
ASSERT(mCoherentStagingBufferMemoryTypeIndex != kInvalidMemoryTypeIndex);
// Cached (b/219974369) Non-coherent staging buffer
preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
ANGLE_VK_TRY(displayVk, mAllocator.findMemoryTypeIndexForBufferInfo(
createInfo, requiredFlags, preferredFlags, persistentlyMapped,
&mNonCoherentStagingBufferMemoryTypeIndex));
ASSERT(mNonCoherentStagingBufferMemoryTypeIndex != kInvalidMemoryTypeIndex);
// Alignment
mStagingBufferAlignment =
static_cast<size_t>(mPhysicalDeviceProperties.limits.minMemoryMapAlignment);
ASSERT(gl::isPow2(mPhysicalDeviceProperties.limits.nonCoherentAtomSize));
ASSERT(gl::isPow2(mPhysicalDeviceProperties.limits.optimalBufferCopyOffsetAlignment));
// Usually minTexelBufferOffsetAlignment is much smaller than nonCoherentAtomSize
ASSERT(gl::isPow2(mPhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment));
mStagingBufferAlignment = std::max(
{mStagingBufferAlignment,
static_cast<size_t>(mPhysicalDeviceProperties.limits.optimalBufferCopyOffsetAlignment),
static_cast<size_t>(mPhysicalDeviceProperties.limits.nonCoherentAtomSize),
static_cast<size_t>(mPhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment)});
ASSERT(gl::isPow2(mStagingBufferAlignment));
// Device local vertex conversion buffer
createInfo.usage = vk::kVertexBufferUsageFlags;
requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
preferredFlags = 0;
ANGLE_VK_TRY(displayVk, mAllocator.findMemoryTypeIndexForBufferInfo(
createInfo, requiredFlags, preferredFlags, persistentlyMapped,
&mDeviceLocalVertexConversionBufferMemoryTypeIndex));
ASSERT(mDeviceLocalVertexConversionBufferMemoryTypeIndex != kInvalidMemoryTypeIndex);
// Host visible and non-coherent vertex conversion buffer, which is the same as non-coherent
// staging buffer
mHostVisibleVertexConversionBufferMemoryTypeIndex = mNonCoherentStagingBufferMemoryTypeIndex;
// We may use compute shader to do conversion, so we must meet
// minStorageBufferOffsetAlignment requirement as well. Also take into account non-coherent
// alignment requirements.
mVertexConversionBufferAlignment = std::max(
{vk::kVertexBufferAlignment,
static_cast<size_t>(mPhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment),
static_cast<size_t>(mPhysicalDeviceProperties.limits.nonCoherentAtomSize),
static_cast<size_t>(defaultBufferMemoryRequirements.alignment)});
ASSERT(gl::isPow2(mVertexConversionBufferAlignment));
return angle::Result::Continue;
}
// The following features and properties are not promoted to any core Vulkan versions (up to Vulkan
// 1.3):
//
// - VK_EXT_line_rasterization: bresenhamLines (feature)
// - VK_EXT_provoking_vertex: provokingVertexLast (feature)
// - VK_EXT_vertex_attribute_divisor: vertexAttributeInstanceRateDivisor (feature),
// maxVertexAttribDivisor (property)
// - VK_EXT_transform_feedback: transformFeedback (feature),
// geometryStreams (feature)
// - VK_EXT_index_type_uint8: indexTypeUint8 (feature)
// - VK_EXT_device_memory_report: deviceMemoryReport (feature)
// - VK_EXT_multisampled_render_to_single_sampled or
// VK_GOOGLEX_multisampled_render_to_single_sampled: multisampledRenderToSingleSampled (feature)
// - VK_EXT_image_2d_view_of_3d: image2DViewOf3D (feature)
// sampler2DViewOf3D (feature)
// - VK_EXT_custom_border_color: customBorderColors (feature)
// customBorderColorWithoutFormat (feature)
// - VK_EXT_depth_clamp_zero_one: depthClampZeroOne (feature)
// - VK_EXT_depth_clip_enable: depthClipEnable (feature)
// - VK_EXT_depth_clip_control: depthClipControl (feature)
// - VK_EXT_primitives_generated_query: primitivesGeneratedQuery (feature),
// primitivesGeneratedQueryWithRasterizerDiscard
// (property)
// - VK_EXT_primitive_topology_list_restart: primitiveTopologyListRestart (feature)
// - VK_EXT_graphics_pipeline_library: graphicsPipelineLibrary (feature),
// graphicsPipelineLibraryFastLinking (property)
// - VK_KHR_fragment_shading_rate: pipelineFragmentShadingRate (feature)
// - VK_EXT_fragment_shader_interlock: fragmentShaderPixelInterlock (feature)
// - VK_EXT_pipeline_robustness: pipelineRobustness (feature)
// - VK_EXT_pipeline_protected_access: pipelineProtectedAccess (feature)
// - VK_EXT_rasterization_order_attachment_access or
// VK_ARM_rasterization_order_attachment_access: rasterizationOrderColorAttachmentAccess
// (feature)
// - VK_EXT_swapchain_maintenance1: swapchainMaintenance1 (feature)
// - VK_EXT_legacy_dithering: supportsLegacyDithering (feature)
// - VK_EXT_physical_device_drm: hasPrimary (property),
// hasRender (property)
// - VK_EXT_host_image_copy: hostImageCopy (feature),
// pCopySrcLayouts (property),
// pCopyDstLayouts (property),
// identicalMemoryTypeRequirements (property)
//
void RendererVk::appendDeviceExtensionFeaturesNotPromoted(
const vk::ExtensionNameList &deviceExtensionNames,
VkPhysicalDeviceFeatures2KHR *deviceFeatures,
VkPhysicalDeviceProperties2 *deviceProperties)
{
if (ExtensionFound(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mLineRasterizationFeatures);
}
if (ExtensionFound(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mProvokingVertexFeatures);
}
if (ExtensionFound(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mVertexAttributeDivisorFeatures);
vk::AddToPNextChain(deviceProperties, &mVertexAttributeDivisorProperties);
}
if (ExtensionFound(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mTransformFeedbackFeatures);
}
if (ExtensionFound(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mIndexTypeUint8Features);
}
if (ExtensionFound(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mMemoryReportFeatures);
}
if (ExtensionFound(VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME,
deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mMultisampledRenderToSingleSampledFeatures);
}
else if (ExtensionFound(VK_GOOGLEX_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME,
deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mMultisampledRenderToSingleSampledFeaturesGOOGLEX);
}
if (ExtensionFound(VK_EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mImage2dViewOf3dFeatures);
}
if (ExtensionFound(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mCustomBorderColorFeatures);
}
if (ExtensionFound(VK_EXT_DEPTH_CLAMP_ZERO_ONE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mDepthClampZeroOneFeatures);
}
if (ExtensionFound(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mDepthClipEnableFeatures);
}
if (ExtensionFound(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mDepthClipControlFeatures);
}
if (ExtensionFound(VK_EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mPrimitivesGeneratedQueryFeatures);
}
if (ExtensionFound(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mPrimitiveTopologyListRestartFeatures);
}
if (ExtensionFound(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mGraphicsPipelineLibraryFeatures);
vk::AddToPNextChain(deviceProperties, &mGraphicsPipelineLibraryProperties);
}
if (ExtensionFound(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mFragmentShadingRateFeatures);
}
if (ExtensionFound(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mFragmentShaderInterlockFeatures);
}
if (ExtensionFound(VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mPipelineRobustnessFeatures);
}
if (ExtensionFound(VK_EXT_PIPELINE_PROTECTED_ACCESS_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mPipelineProtectedAccessFeatures);
}
// The EXT and ARM versions are interchangeable. The structs and enums alias each other.
if (ExtensionFound(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME,
deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mRasterizationOrderAttachmentAccessFeatures);
}
else if (ExtensionFound(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME,
deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mRasterizationOrderAttachmentAccessFeatures);
}
if (ExtensionFound(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mSwapchainMaintenance1Features);
}
if (ExtensionFound(VK_EXT_LEGACY_DITHERING_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mDitheringFeatures);
}
if (ExtensionFound(VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceProperties, &mDrmProperties);
}
if (ExtensionFound(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, deviceExtensionNames))
{
// VkPhysicalDeviceHostImageCopyPropertiesEXT has a count + array query. Typically, that
// requires getting the properties once with a nullptr array, to get the count, and then
// again with an array of that size. For simplicity, ANGLE just uses an array that's big
// enough. If that array goes terribly large in the future, ANGLE may lose knowledge of
// some likely esoteric layouts, which doesn't really matter.
constexpr uint32_t kMaxLayoutCount = 50;
mHostImageCopySrcLayoutsStorage.resize(kMaxLayoutCount, VK_IMAGE_LAYOUT_UNDEFINED);
mHostImageCopyDstLayoutsStorage.resize(kMaxLayoutCount, VK_IMAGE_LAYOUT_UNDEFINED);
mHostImageCopyProperties.copySrcLayoutCount = kMaxLayoutCount;
mHostImageCopyProperties.copyDstLayoutCount = kMaxLayoutCount;
mHostImageCopyProperties.pCopySrcLayouts = mHostImageCopySrcLayoutsStorage.data();
mHostImageCopyProperties.pCopyDstLayouts = mHostImageCopyDstLayoutsStorage.data();
vk::AddToPNextChain(deviceFeatures, &mHostImageCopyFeatures);
vk::AddToPNextChain(deviceProperties, &mHostImageCopyProperties);
}
}
// The following features and properties used by ANGLE have been promoted to Vulkan 1.1:
//
// - (unpublished VK_KHR_subgroup): supportedStages (property),
// supportedOperations (property)
// - (unpublished VK_KHR_protected_memory): protectedMemory (feature)
// - VK_KHR_sampler_ycbcr_conversion: samplerYcbcrConversion (feature)
// - VK_KHR_multiview: multiview (feature),
// maxMultiviewViewCount (property)
//
//
// Note that subgroup and protected memory features and properties came from unpublished extensions
// and are core in Vulkan 1.1.
//
void RendererVk::appendDeviceExtensionFeaturesPromotedTo11(
const vk::ExtensionNameList &deviceExtensionNames,
VkPhysicalDeviceFeatures2KHR *deviceFeatures,
VkPhysicalDeviceProperties2 *deviceProperties)
{
vk::AddToPNextChain(deviceProperties, &mSubgroupProperties);
vk::AddToPNextChain(deviceFeatures, &mProtectedMemoryFeatures);
if (ExtensionFound(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mSamplerYcbcrConversionFeatures);
}
if (ExtensionFound(VK_KHR_MULTIVIEW_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mMultiviewFeatures);
vk::AddToPNextChain(deviceProperties, &mMultiviewProperties);
}
}
// The following features and properties used by ANGLE have been promoted to Vulkan 1.2:
//
// - VK_KHR_shader_float16_int8: shaderFloat16 (feature)
// - VK_KHR_depth_stencil_resolve: supportedDepthResolveModes (property),
// independentResolveNone (property)
// - VK_KHR_driver_properties: driverName (property),
// driverID (property)
// - VK_KHR_shader_subgroup_extended_types: shaderSubgroupExtendedTypes (feature)
// - VK_EXT_host_query_reset: hostQueryReset (feature)
// - VK_KHR_imageless_framebuffer: imagelessFramebuffer (feature)
// - VK_KHR_timeline_semaphore: timelineSemaphore (feature)
//
// Note that supportedDepthResolveModes is used just to check if the property struct is populated.
// ANGLE always uses VK_RESOLVE_MODE_SAMPLE_ZERO_BIT for both depth and stencil, and support for
// this bit is mandatory as long as the extension (or Vulkan 1.2) exists.
//
void RendererVk::appendDeviceExtensionFeaturesPromotedTo12(
const vk::ExtensionNameList &deviceExtensionNames,
VkPhysicalDeviceFeatures2KHR *deviceFeatures,
VkPhysicalDeviceProperties2 *deviceProperties)
{
if (ExtensionFound(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mShaderFloat16Int8Features);
}
if (ExtensionFound(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceProperties, &mDepthStencilResolveProperties);
}
if (ExtensionFound(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceProperties, &mDriverProperties);
}
if (ExtensionFound(VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mSubgroupExtendedTypesFeatures);
}
if (ExtensionFound(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mHostQueryResetFeatures);
}
if (ExtensionFound(VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mImagelessFramebufferFeatures);
}
if (ExtensionFound(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mTimelineSemaphoreFeatures);
}
}
// The following features and properties used by ANGLE have been promoted to Vulkan 1.3:
//
// - VK_EXT_pipeline_creation_cache_control: pipelineCreationCacheControl (feature)
// - VK_EXT_extended_dynamic_state: extendedDynamicState (feature)
// - VK_EXT_extended_dynamic_state2: extendedDynamicState2 (feature),
// extendedDynamicState2LogicOp (feature)
//
// Note that VK_EXT_extended_dynamic_state2 is partially promoted to Vulkan 1.3. If ANGLE creates a
// Vulkan 1.3 device, it would still need to enable this extension separately for
// extendedDynamicState2LogicOp.
//
void RendererVk::appendDeviceExtensionFeaturesPromotedTo13(
const vk::ExtensionNameList &deviceExtensionNames,
VkPhysicalDeviceFeatures2KHR *deviceFeatures,
VkPhysicalDeviceProperties2 *deviceProperties)
{
if (ExtensionFound(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mPipelineCreationCacheControlFeatures);
}
if (ExtensionFound(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mExtendedDynamicStateFeatures);
}
if (ExtensionFound(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(deviceFeatures, &mExtendedDynamicState2Features);
}
}
void RendererVk::queryDeviceExtensionFeatures(const vk::ExtensionNameList &deviceExtensionNames)
{
// Default initialize all extension features to false.
mPhysicalDevice11Properties = {};
mPhysicalDevice11Properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
mPhysicalDevice11Features = {};
mPhysicalDevice11Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
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;
mSubgroupExtendedTypesFeatures = {};
mSubgroupExtendedTypesFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES;
mMemoryReportFeatures = {};
mMemoryReportFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT;
mShaderFloat16Int8Features = {};
mShaderFloat16Int8Features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
mDepthStencilResolveProperties = {};
mDepthStencilResolveProperties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
mCustomBorderColorFeatures = {};
mCustomBorderColorFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
mMultisampledRenderToSingleSampledFeatures = {};
mMultisampledRenderToSingleSampledFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT;
mMultisampledRenderToSingleSampledFeaturesGOOGLEX = {};
mMultisampledRenderToSingleSampledFeaturesGOOGLEX.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_GOOGLEX;
mImage2dViewOf3dFeatures = {};
mImage2dViewOf3dFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT;
mMultiviewFeatures = {};
mMultiviewFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
mMultiviewProperties = {};
mMultiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
mDriverProperties = {};
mDriverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
mSamplerYcbcrConversionFeatures = {};
mSamplerYcbcrConversionFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
mProtectedMemoryFeatures = {};
mProtectedMemoryFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
mHostQueryResetFeatures = {};
mHostQueryResetFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
mDepthClampZeroOneFeatures = {};
mDepthClampZeroOneFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_ZERO_ONE_FEATURES_EXT;
mDepthClipEnableFeatures = {};
mDepthClipEnableFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
mDepthClipControlFeatures = {};
mDepthClipControlFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
mPrimitivesGeneratedQueryFeatures = {};
mPrimitivesGeneratedQueryFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT;
mPrimitiveTopologyListRestartFeatures = {};
mPrimitiveTopologyListRestartFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
mPipelineCreationCacheControlFeatures = {};
mPipelineCreationCacheControlFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT;
mExtendedDynamicStateFeatures = {};
mExtendedDynamicStateFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
mExtendedDynamicState2Features = {};
mExtendedDynamicState2Features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
mGraphicsPipelineLibraryFeatures = {};
mGraphicsPipelineLibraryFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT;
mGraphicsPipelineLibraryProperties = {};
mGraphicsPipelineLibraryProperties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT;
mFragmentShadingRateFeatures = {};
mFragmentShadingRateFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
mFragmentShaderInterlockFeatures = {};
mFragmentShaderInterlockFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
mImagelessFramebufferFeatures = {};
mImagelessFramebufferFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR;
mPipelineRobustnessFeatures = {};
mPipelineRobustnessFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT;
mPipelineProtectedAccessFeatures = {};
mPipelineProtectedAccessFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROTECTED_ACCESS_FEATURES_EXT;
mRasterizationOrderAttachmentAccessFeatures = {};
mRasterizationOrderAttachmentAccessFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT;
mSwapchainMaintenance1Features = {};
mSwapchainMaintenance1Features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT;
mDitheringFeatures = {};
mDitheringFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LEGACY_DITHERING_FEATURES_EXT;
mDrmProperties = {};
mDrmProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
mTimelineSemaphoreFeatures = {};
mTimelineSemaphoreFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
mHostImageCopyFeatures = {};
mHostImageCopyFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT;
mHostImageCopyProperties = {};
mHostImageCopyProperties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_PROPERTIES_EXT;
// 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;
appendDeviceExtensionFeaturesNotPromoted(deviceExtensionNames, &deviceFeatures,
&deviceProperties);
appendDeviceExtensionFeaturesPromotedTo11(deviceExtensionNames, &deviceFeatures,
&deviceProperties);
appendDeviceExtensionFeaturesPromotedTo12(deviceExtensionNames, &deviceFeatures,
&deviceProperties);
appendDeviceExtensionFeaturesPromotedTo13(deviceExtensionNames, &deviceFeatures,
&deviceProperties);
vkGetPhysicalDeviceFeatures2(mPhysicalDevice, &deviceFeatures);
vkGetPhysicalDeviceProperties2(mPhysicalDevice, &deviceProperties);
// Clean up pNext chains
mPhysicalDevice11Properties.pNext = nullptr;
mPhysicalDevice11Features.pNext = nullptr;
mLineRasterizationFeatures.pNext = nullptr;
mMemoryReportFeatures.pNext = nullptr;
mProvokingVertexFeatures.pNext = nullptr;
mVertexAttributeDivisorFeatures.pNext = nullptr;
mVertexAttributeDivisorProperties.pNext = nullptr;
mTransformFeedbackFeatures.pNext = nullptr;
mIndexTypeUint8Features.pNext = nullptr;
mSubgroupProperties.pNext = nullptr;
mSubgroupExtendedTypesFeatures.pNext = nullptr;
mCustomBorderColorFeatures.pNext = nullptr;
mShaderFloat16Int8Features.pNext = nullptr;
mDepthStencilResolveProperties.pNext = nullptr;
mMultisampledRenderToSingleSampledFeatures.pNext = nullptr;
mMultisampledRenderToSingleSampledFeaturesGOOGLEX.pNext = nullptr;
mImage2dViewOf3dFeatures.pNext = nullptr;
mMultiviewFeatures.pNext = nullptr;
mMultiviewProperties.pNext = nullptr;
mDriverProperties.pNext = nullptr;
mSamplerYcbcrConversionFeatures.pNext = nullptr;
mProtectedMemoryFeatures.pNext = nullptr;
mHostQueryResetFeatures.pNext = nullptr;
mDepthClampZeroOneFeatures.pNext = nullptr;
mDepthClipEnableFeatures.pNext = nullptr;
mDepthClipControlFeatures.pNext = nullptr;
mPrimitivesGeneratedQueryFeatures.pNext = nullptr;
mPrimitiveTopologyListRestartFeatures.pNext = nullptr;
mPipelineCreationCacheControlFeatures.pNext = nullptr;
mExtendedDynamicStateFeatures.pNext = nullptr;
mExtendedDynamicState2Features.pNext = nullptr;
mGraphicsPipelineLibraryFeatures.pNext = nullptr;
mGraphicsPipelineLibraryProperties.pNext = nullptr;
mFragmentShadingRateFeatures.pNext = nullptr;
mFragmentShaderInterlockFeatures.pNext = nullptr;
mImagelessFramebufferFeatures.pNext = nullptr;
mPipelineRobustnessFeatures.pNext = nullptr;
mPipelineProtectedAccessFeatures.pNext = nullptr;
mRasterizationOrderAttachmentAccessFeatures.pNext = nullptr;
mSwapchainMaintenance1Features.pNext = nullptr;
mDitheringFeatures.pNext = nullptr;
mDrmProperties.pNext = nullptr;
mTimelineSemaphoreFeatures.pNext = nullptr;
mHostImageCopyFeatures.pNext = nullptr;
mHostImageCopyProperties.pNext = nullptr;
}
// See comment above appendDeviceExtensionFeaturesNotPromoted. Additional extensions are enabled
// here which don't have feature structs:
//
// - VK_KHR_shared_presentable_image
// - VK_EXT_memory_budget
// - VK_KHR_incremental_present
// - VK_EXT_queue_family_foreign
// - VK_ANDROID_external_memory_android_hardware_buffer
// - VK_GGP_frame_token
// - VK_KHR_external_memory_fd
// - VK_KHR_external_memory_fuchsia
// - VK_KHR_external_semaphore_fd
// - VK_KHR_external_fence_fd
// - VK_FUCHSIA_external_semaphore
// - VK_EXT_shader_stencil_export
// - VK_EXT_load_store_op_none
// - VK_QCOM_render_pass_store_ops
// - VK_GOOGLE_display_timing
// - VK_EXT_external_memory_dma_buf
// - VK_EXT_image_drm_format_modifier
// - VK_EXT_blend_operation_advanced
// - VK_EXT_full_screen_exclusive
//
void RendererVk::enableDeviceExtensionsNotPromoted(
const vk::ExtensionNameList &deviceExtensionNames)
{
if (mFeatures.supportsSharedPresentableImageExtension.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME);
}
if (mFeatures.supportsDepthClampZeroOne.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLAMP_ZERO_ONE_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mDepthClampZeroOneFeatures);
}
if (mFeatures.supportsMemoryBudget.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
}
if (mFeatures.supportsIncrementalPresent.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);
}
#if defined(ANGLE_PLATFORM_ANDROID)
if (mFeatures.supportsAndroidHardwareBuffer.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
mEnabledDeviceExtensions.push_back(
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
}
#else
ASSERT(!mFeatures.supportsAndroidHardwareBuffer.enabled);
#endif
#if defined(ANGLE_PLATFORM_GGP)
if (mFeatures.supportsGGPFrameToken.enabled)
{
mEnabledDeviceExtensions.push_back(VK_GGP_FRAME_TOKEN_EXTENSION_NAME);
}
#else
ASSERT(!mFeatures.supportsGGPFrameToken.enabled);
#endif
if (mFeatures.supportsExternalMemoryFd.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
}
if (mFeatures.supportsExternalMemoryFuchsia.enabled)
{
mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME);
}
if (mFeatures.supportsExternalSemaphoreFd.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
}
if (mFeatures.supportsExternalFenceFd.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME);
}
if (mFeatures.supportsExternalSemaphoreFuchsia.enabled)
{
mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
}
if (mFeatures.supportsShaderStencilExport.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
}
if (mFeatures.supportsRenderPassLoadStoreOpNone.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME);
}
else if (mFeatures.supportsRenderPassStoreOpNone.enabled)
{
mEnabledDeviceExtensions.push_back(VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME);
}
if (mFeatures.supportsTimestampSurfaceAttribute.enabled)
{
mEnabledDeviceExtensions.push_back(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
}
if (mFeatures.bresenhamLineRasterization.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mLineRasterizationFeatures);
}
if (mFeatures.provokingVertex.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mProvokingVertexFeatures);
}
if (mVertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
{
mEnabledDeviceExtensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &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 (mFeatures.supportsTransformFeedbackExtension.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mTransformFeedbackFeatures);
}
if (mFeatures.supportsCustomBorderColor.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mCustomBorderColorFeatures);
}
if (mFeatures.supportsIndexTypeUint8.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mIndexTypeUint8Features);
}
if (mFeatures.supportsMultisampledRenderToSingleSampled.enabled)
{
mEnabledDeviceExtensions.push_back(
VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mMultisampledRenderToSingleSampledFeatures);
}
if (mFeatures.supportsMultisampledRenderToSingleSampledGOOGLEX.enabled)
{
ASSERT(!mFeatures.supportsMultisampledRenderToSingleSampled.enabled);
mEnabledDeviceExtensions.push_back(
VK_GOOGLEX_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mMultisampledRenderToSingleSampledFeaturesGOOGLEX);
}
if (mFeatures.logMemoryReportCallbacks.enabled || mFeatures.logMemoryReportStats.enabled)
{
ASSERT(mMemoryReportFeatures.deviceMemoryReport);
mEnabledDeviceExtensions.push_back(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME);
}
if (mFeatures.supportsExternalMemoryDmaBufAndModifiers.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
mEnabledDeviceExtensions.push_back(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
}
if (mFeatures.supportsDepthClipControl.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mDepthClipControlFeatures);
}
if (mFeatures.supportsPrimitivesGeneratedQuery.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mPrimitivesGeneratedQueryFeatures);
}
if (mFeatures.supportsPrimitiveTopologyListRestart.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mPrimitiveTopologyListRestartFeatures);
}
if (mFeatures.supportsBlendOperationAdvanced.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME);
}
if (mFeatures.supportsGraphicsPipelineLibrary.enabled)
{
// VK_EXT_graphics_pipeline_library requires VK_KHR_pipeline_library
ASSERT(ExtensionFound(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, deviceExtensionNames));
mEnabledDeviceExtensions.push_back(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
mEnabledDeviceExtensions.push_back(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mGraphicsPipelineLibraryFeatures);
}
if (mFeatures.supportsFragmentShadingRate.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mFragmentShadingRateFeatures);
}
if (mFeatures.supportsFragmentShaderPixelInterlock.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mFragmentShaderInterlockFeatures);
}
if (mFeatures.supportsPipelineRobustness.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mPipelineRobustnessFeatures);
}
if (mFeatures.supportsPipelineProtectedAccess.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PIPELINE_PROTECTED_ACCESS_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mPipelineProtectedAccessFeatures);
}
if (mFeatures.supportsRasterizationOrderAttachmentAccess.enabled)
{
if (ExtensionFound(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME,
deviceExtensionNames))
{
mEnabledDeviceExtensions.push_back(
VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME);
}
else
{
ASSERT(ExtensionFound(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME,
deviceExtensionNames));
mEnabledDeviceExtensions.push_back(
VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME);
}
vk::AddToPNextChain(&mEnabledFeatures, &mRasterizationOrderAttachmentAccessFeatures);
}
if (mFeatures.supportsImage2dViewOf3d.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mImage2dViewOf3dFeatures);
}
if (mFeatures.supportsSwapchainMaintenance1.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mSwapchainMaintenance1Features);
}
if (mFeatures.supportsLegacyDithering.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_LEGACY_DITHERING_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mDitheringFeatures);
}
if (mFeatures.supportsFormatFeatureFlags2.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME);
}
if (mFeatures.supportsHostImageCopy.enabled)
{
// VK_EXT_host_image_copy requires VK_KHR_copy_commands2 and VK_KHR_format_feature_flags2.
// VK_KHR_format_feature_flags2 is enabled separately.
ASSERT(ExtensionFound(VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, deviceExtensionNames));
ASSERT(ExtensionFound(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, deviceExtensionNames));
mEnabledDeviceExtensions.push_back(VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME);
mEnabledDeviceExtensions.push_back(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mHostImageCopyFeatures);
}
#if defined(ANGLE_PLATFORM_WINDOWS)
// We only need the VK_EXT_full_screen_exclusive extension if we are opting
// out of it via VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT (i.e. working
// around driver bugs).
if (getFeatures().supportsFullScreenExclusive.enabled &&
getFeatures().forceDisableFullScreenExclusive.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME);
}
#endif
}
// See comment above appendDeviceExtensionFeaturesPromotedTo11. Additional extensions are enabled
// here which don't have feature structs:
//
// - VK_KHR_get_memory_requirements2
// - VK_KHR_bind_memory2
// - VK_KHR_maintenance1
// - VK_KHR_external_memory
// - VK_KHR_external_semaphore
// - VK_KHR_external_fence
//
void RendererVk::enableDeviceExtensionsPromotedTo11(
const vk::ExtensionNameList &deviceExtensionNames)
{
// OVR_multiview disallows multiview with geometry and tessellation, so don't request these
// features.
mMultiviewFeatures.multiviewGeometryShader = VK_FALSE;
mMultiviewFeatures.multiviewTessellationShader = VK_FALSE;
mPhysicalDevice11Features.multiviewGeometryShader = VK_FALSE;
mPhysicalDevice11Features.multiviewTessellationShader = VK_FALSE;
// Disable protected memory if not needed as it can introduce overhead
if (!mFeatures.supportsProtectedMemory.enabled)
{
mPhysicalDevice11Features.protectedMemory = VK_FALSE;
}
if (mFeatures.supportsMultiview.enabled)
{
vk::AddToPNextChain(&mEnabledFeatures, &mMultiviewFeatures);
}
if (mFeatures.supportsYUVSamplerConversion.enabled)
{
vk::AddToPNextChain(&mEnabledFeatures, &mSamplerYcbcrConversionFeatures);
}
if (mFeatures.supportsProtectedMemory.enabled)
{
vk::AddToPNextChain(&mEnabledFeatures, &mProtectedMemoryFeatures);
}
}
// See comment above appendDeviceExtensionFeaturesPromotedTo12. Additional extensions are enabled
// here which don't have feature structs:
//
// - VK_KHR_create_renderpass2
// - VK_KHR_image_format_list
// - VK_KHR_sampler_mirror_clamp_to_edge
//
void RendererVk::enableDeviceExtensionsPromotedTo12(
const vk::ExtensionNameList &deviceExtensionNames)
{
if (mFeatures.supportsRenderpass2.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
}
if (mFeatures.supportsImageFormatList.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
}
if (mFeatures.supportsSamplerMirrorClampToEdge.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME);
}
if (mFeatures.supportsDepthStencilResolve.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
}
if (mFeatures.allowGenerateMipmapWithCompute.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mSubgroupExtendedTypesFeatures);
}
if (mFeatures.supportsShaderFloat16.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mShaderFloat16Int8Features);
}
if (mFeatures.supportsHostQueryReset.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mHostQueryResetFeatures);
}
if (mFeatures.supportsImagelessFramebuffer.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mImagelessFramebufferFeatures);
}
if (mFeatures.supportsTimelineSemaphore.enabled)
{
mEnabledDeviceExtensions.push_back(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mTimelineSemaphoreFeatures);
}
}
// See comment above appendDeviceExtensionFeaturesPromotedTo13.
void RendererVk::enableDeviceExtensionsPromotedTo13(
const vk::ExtensionNameList &deviceExtensionNames)
{
if (mFeatures.supportsPipelineCreationCacheControl.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mPipelineCreationCacheControlFeatures);
}
if (mFeatures.supportsPipelineCreationFeedback.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
}
if (mFeatures.supportsExtendedDynamicState.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mExtendedDynamicStateFeatures);
}
if (mFeatures.supportsExtendedDynamicState2.enabled)
{
mEnabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
vk::AddToPNextChain(&mEnabledFeatures, &mExtendedDynamicState2Features);
}
}
angle::Result RendererVk::enableDeviceExtensions(DisplayVk *displayVk,
const VulkanLayerVector &enabledDeviceLayerNames)
{
// 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));
// Work-around a race condition in the Android platform during Android start-up, that can cause
// the second call to vkEnumerateDeviceExtensionProperties to have an additional extension. In
// that case, the second call will return VK_INCOMPLETE. To work-around that, add 1 to
// deviceExtensionCount and ask for one more extension property than the first call said there
// were. See: http://anglebug.com/6715 and internal-to-Google bug: b/206733351.
deviceExtensionCount++;
std::vector<VkExtensionProperties> deviceExtensionProps(deviceExtensionCount);
ANGLE_VK_TRY(displayVk,
vkEnumerateDeviceExtensionProperties(
mPhysicalDevice, nullptr, &deviceExtensionCount, deviceExtensionProps.data()));
// In case fewer items were returned than requested, resize deviceExtensionProps to the number
// of extensions returned (i.e. deviceExtensionCount). See: b/208937840
deviceExtensionProps.resize(deviceExtensionCount);
// 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(