| // |
| // 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 ¤t, 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( |
|