| // |
| // 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/glslang_wrapper_utils.h" |
| #include "libANGLE/renderer/vulkan/CompilerVk.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| #include "libANGLE/renderer/vulkan/DisplayVk.h" |
| #include "libANGLE/renderer/vulkan/FramebufferVk.h" |
| #include "libANGLE/renderer/vulkan/ProgramVk.h" |
| #include "libANGLE/renderer/vulkan/ResourceVk.h" |
| #include "libANGLE/renderer/vulkan/VertexArrayVk.h" |
| #include "libANGLE/renderer/vulkan/vk_caps_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_format_utils.h" |
| #include "libANGLE/trace.h" |
| #include "platform/PlatformMethods.h" |
| |
| // Consts |
| namespace |
| { |
| constexpr VkFormatFeatureFlags kInvalidFormatFeatureFlags = static_cast<VkFormatFeatureFlags>(-1); |
| |
| #if defined(ANGLE_EXPOSE_NON_CONFORMANT_EXTENSIONS_AND_VERSIONS) |
| constexpr bool kExposeNonConformantExtensionsAndVersions = true; |
| #else |
| constexpr bool kExposeNonConformantExtensionsAndVersions = false; |
| #endif |
| |
| #if defined(ANGLE_USE_SPIRV_GENERATION_THROUGH_GLSLANG) |
| constexpr bool kUseSpirvGenThroughGlslang = true; |
| #else |
| constexpr bool kUseSpirvGenThroughGlslang = 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; |
| |
| // Update the pipeline cache every this many swaps. |
| constexpr uint32_t kPipelineCacheVkUpdatePeriod = 60; |
| // Per the Vulkan specification, as long as Vulkan 1.1+ is returned by vkEnumerateInstanceVersion, |
| // ANGLE must indicate the highest version of Vulkan functionality that it uses. The Vulkan |
| // validation layers will issue messages for any core functionality that requires a higher version. |
| // This value must be increased whenever ANGLE starts using functionality from a newer core |
| // version of Vulkan. |
| constexpr uint32_t kPreferredVulkanAPIVersion = VK_API_VERSION_1_1; |
| |
| angle::vk::ICD ChooseICDFromAttribs(const egl::AttributeMap &attribs) |
| { |
| #if !defined(ANGLE_PLATFORM_ANDROID) |
| // Mock ICD does not currently run on Android |
| EGLAttrib deviceType = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, |
| EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE); |
| |
| switch (deviceType) |
| { |
| case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: |
| break; |
| case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: |
| return angle::vk::ICD::Mock; |
| case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE: |
| return angle::vk::ICD::SwiftShader; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| #endif // !defined(ANGLE_PLATFORM_ANDROID) |
| |
| return angle::vk::ICD::Default; |
| } |
| |
| bool StrLess(const char *a, const char *b) |
| { |
| return strcmp(a, b) < 0; |
| } |
| |
| bool ExtensionFound(const char *needle, const vk::ExtensionNameList &haystack) |
| { |
| // NOTE: The list must be sorted. |
| return std::binary_search(haystack.begin(), haystack.end(), needle, StrLess); |
| } |
| |
| VkResult VerifyExtensionsPresent(const vk::ExtensionNameList &haystack, |
| const vk::ExtensionNameList &needles) |
| { |
| // NOTE: The lists must be sorted. |
| if (std::includes(haystack.begin(), haystack.end(), needles.begin(), needles.end(), StrLess)) |
| { |
| return VK_SUCCESS; |
| } |
| for (const char *needle : needles) |
| { |
| if (!ExtensionFound(needle, haystack)) |
| { |
| ERR() << "Extension not supported: " << needle; |
| } |
| } |
| return VK_ERROR_EXTENSION_NOT_PRESENT; |
| } |
| |
| // Array of Validation error/warning messages that will be ignored, should include bugID |
| constexpr const char *kSkippedMessages[] = { |
| // http://anglebug.com/2866 |
| "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", |
| // http://anglebug.com/4883 |
| "UNASSIGNED-CoreValidation-Shader-InputNotProduced", |
| // http://anglebug.com/2796 |
| "UNASSIGNED-CoreValidation-Shader-PointSizeMissing", |
| // http://anglebug.com/3832 |
| "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428", |
| // Best Practices Skips https://issuetracker.google.com/issues/166641492 |
| // https://issuetracker.google.com/issues/166793850 |
| "UNASSIGNED-BestPractices-vkCreateCommandPool-command-buffer-reset", |
| "UNASSIGNED-BestPractices-pipeline-stage-flags", |
| "UNASSIGNED-BestPractices-Error-Result", |
| "UNASSIGNED-BestPractices-vkAllocateMemory-small-allocation", |
| "UNASSIGNED-BestPractices-vkBindMemory-small-dedicated-allocation", |
| "UNASSIGNED-BestPractices-vkAllocateMemory-too-many-objects", |
| "UNASSIGNED-BestPractices-vkCreateDevice-deprecated-extension", |
| "UNASSIGNED-BestPractices-vkCreateRenderPass-image-requires-memory", |
| "UNASSIGNED-BestPractices-vkCreateGraphicsPipelines-too-many-instanced-vertex-buffers", |
| "UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw", |
| "UNASSIGNED-BestPractices-vkCmdClearAttachments-clear-after-load", |
| "UNASSIGNED-BestPractices-TransitionUndefinedToReadOnly", |
| // http://anglebug.com/4928 |
| "VUID-vkMapMemory-memory-00683", |
| // http://anglebug.com/5027 |
| "UNASSIGNED-CoreValidation-Shader-PushConstantOutOfRange", |
| // http://anglebug.com/5304 |
| "VUID-vkCmdDraw-magFilter-04553", |
| "VUID-vkCmdDrawIndexed-magFilter-04553", |
| // http://anglebug.com/5309 |
| "VUID-VkImageViewCreateInfo-usage-02652", |
| // http://anglebug.com/5336 |
| "UNASSIGNED-BestPractices-vkCreateDevice-specialuse-extension", |
| // http://anglebug.com/5331 |
| "VUID-VkSubpassDescriptionDepthStencilResolve-depthResolveMode-parameter", |
| "VUID-VkSubpassDescriptionDepthStencilResolve-stencilResolveMode-parameter", |
| // 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/6442 |
| "UNASSIGNED-CoreValidation-Shader-InterfaceTypeMismatch", |
| // 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/7105 |
| "VUID-vkCmdDraw-None-06538", |
| "VUID-vkCmdDrawIndexed-None-06538", |
| // http://anglebug.com/7325 |
| "VUID-vkCmdBindVertexBuffers2-pStrides-06209", |
| // http://anglebug.com/7338 |
| "VUID-VkGraphicsPipelineCreateInfo-renderPass-06040", |
| "VUID-VkGraphicsPipelineCreateInfo-renderPass-06039", |
| }; |
| |
| // 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. The message to check is made more specific (by checking the exact set/binding and part |
| // of seq_no) to reduce the chances of it suppressing a bug in other valid tests. |
| // http://anglebug.com/6417 |
| // |
| // From: Texture2DBaseMaxTestES3.Fuzz545ImmutableTexRenderFeedback/ES3_Vulkan |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 6", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 7", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdDraw", |
| }, |
| // From: FramebufferTest_ES3.FramebufferBindToNewLevelAfterMaxIncreaseShouldntCrash/ES3_Vulkan |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 10,", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 2,", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 9,", |
| }, |
| // From: FramebufferTest_ES3.SampleFromAttachedTextureWithDifferentLOD/ES3_Vulkan |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 8,", |
| }, |
| // With Vulkan secondary command buffers: |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "Recorded access info (recorded_usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, command: " |
| "vkCmdDraw, seq_no: 1, reset_no: 1). Access info (prior_usage: " |
| "SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, command: " |
| "vkCmdBeginRenderPass, seq_no:", |
| }, |
| // From: TracePerfTest.Run/vulkan_aztec_ruins |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass, seq_no: 11", |
| }, |
| // 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: TracePerfTest.Run/vulkan_swiftshader_manhattan_31 http://anglebug.com/6701 |
| { |
| "SYNC-HAZARD-WRITE_AFTER_WRITE", |
| "Hazard WRITE_AFTER_WRITE in subpass 0 for attachment 1 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", |
| }, |
| // 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", |
| "vkCmdDrawIndexed: Hazard READ_AFTER_WRITE for vertex", |
| "usage: SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "vkCmdDrawIndexedIndirect: Hazard READ_AFTER_WRITE for vertex", |
| "usage: SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "vkCmdDrawIndirect: Hazard READ_AFTER_WRITE for vertex", |
| "usage: SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "vkCmdDrawIndexedIndirect: Hazard READ_AFTER_WRITE for index", |
| "usage: SYNC_INDEX_INPUT_INDEX_READ", |
| }, |
| { |
| "SYNC-HAZARD-WRITE_AFTER_READ", |
| "vkCmdDraw: 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", |
| "vkCmdCopyImageToBuffer: 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", |
| "vkCmdCopyBuffer: 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", |
| "vkCmdDispatch: 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", |
| }, |
| // From: TracePerfTest.Run/vulkan_swiftshader_manhattan_31. 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 apriori 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: TracePerfTest.Run/vulkan_dead_by_daylight |
| // From: TracePerfTest.Run/vulkan_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}, |
| // http://anglebug.com/7070 |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: VK_IMAGE_LAYOUT_GENERAL, " |
| "binding #0, index 0. Access info (usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, " |
| "prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, write_barriers: 0, " |
| "command: vkCmdBeginRenderPass", |
| }, |
| // From: TracePerfTest.Run/vulkan_car_chase http://anglebug.com/7125 |
| { |
| "SYNC-HAZARD-WRITE_AFTER_READ", |
| "type: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER", |
| }, |
| // From: TracePerfTest.Run/vulkan_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_STORAGE_READ, read_barriers: " |
| "VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, command: vkCmdDrawIndexed", |
| }, |
| // 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_STORAGE_READ, prior_usage: SYNC_IMAGE_LAYOUT_TRANSITION, " |
| "write_barriers: " |
| "SYNC_VERTEX_ATTRIBUTE_INPUT_VERTEX_ATTRIBUTE_READ|SYNC_VERTEX_SHADER_SHADER_SAMPLED_READ|" |
| "SYNC_VERTEX_SHADER_SHADER_STORAGE_READ|SYNC_VERTEX_SHADER_UNIFORM_READ|SYNC_TESSELLATION_" |
| "CONTROL_SHADER_SHADER_SAMPLED_READ|SYNC_TESSELLATION_CONTROL_SHADER_SHADER_STORAGE_READ|" |
| "SYNC_TESSELLATION_CONTROL_SHADER_UNIFORM_READ|SYNC_TESSELLATION_EVALUATION_SHADER_SHADER_" |
| "SAMPLED_READ|SYNC_TESSELLATION_EVALUATION_SHADER_SHADER_STORAGE_READ|SYNC_TESSELLATION_EV" |
| "ALUATION_SHADER_UNIFORM_READ|SYNC_GEOMETRY_SHADER_SHADER_SAMPLED_READ|SYNC_GEOMETRY_SHADER" |
| "_SHADER_STORAGE_READ|SYNC_GEOMETRY_SHADER_UNIFORM_READ|SYNC_FRAGMENT_SHADER_SHADER_SAMPLED_" |
| "READ" |
| "|SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ|SYNC_FRAGMENT_SHADER_UNIFORM_READ, " |
| "command: vkCmdPipelineBarrier, seq_no: 4,", |
| "", false}, |
| // On SwiftShader, 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_STORAGE_READ, prior_usage: SYNC_IMAGE_LAYOUT_TRANSITION, " |
| "write_barriers: " |
| "SYNC_FRAGMENT_SHADER_SHADER_SAMPLED_READ|SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ|SYNC_" |
| "FRAGMENT_SHADER_UNIFORM_READ, " |
| "command: vkCmdPipelineBarrier, seq_no: 3,", |
| "", false}, |
| |
| }; |
| |
| // 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, prior_usage: " |
| "SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE, " |
| "command: vkCmdDraw", |
| "", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, binding ", |
| "Access info (usage: " |
| "SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, prior_usage: " |
| "SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, write_barriers: 0, command: " |
| "vkCmdEndRenderPass", |
| }, |
| { |
| "SYNC-HAZARD-READ_AFTER_WRITE", |
| "type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageLayout: " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, binding ", |
| "Access info (usage: " |
| "SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, prior_usage: " |
| "SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_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, command: " |
| "vkCmdEndRenderPass", |
| }, |
| }; |
| |
| // 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 |
| { |
| "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, write_barriers: " |
| "SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_LATE_FRAGMENT_TESTS_DEPTH_" |
| "STENCIL_ATTACHMENT_READ, command: vkCmdPipelineBarrier", |
| "", |
| }, |
| { |
| "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, write_barriers: " |
| "SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_LATE_FRAGMENT_TESTS_DEPTH_" |
| "STENCIL_ATTACHMENT_READ, command: vkCmdPipelineBarrier", |
| "", |
| }, |
| // 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, write_barriers: " |
| "SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_LATE_FRAGMENT_TESTS_DEPTH_" |
| "STENCIL_ATTACHMENT_READ|SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ|SYNC_COLOR_" |
| "ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, command: vkCmdPipelineBarrier", |
| "", |
| }, |
| // 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, write_barriers: " |
| "SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_FRAGMENT_SHADER_SHADER_" |
| "SAMPLED_" |
| "READ|SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ|SYNC_FRAGMENT_SHADER_UNIFORM_READ|SYNC_LATE_" |
| "FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ, command: vkCmdPipelineBarrier", |
| "", |
| }, |
| { |
| "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, write_barriers: " |
| "SYNC_EARLY_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_READ|SYNC_LATE_FRAGMENT_TESTS_DEPTH_" |
| "STENCIL_ATTACHMENT_READ|SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_READ|SYNC_COLOR_" |
| "ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE, command: vkCmdPipelineBarrier", |
| "", |
| }, |
| }; |
| |
| enum class DebugMessageReport |
| { |
| Ignore, |
| Print, |
| }; |
| |
| // 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) |
| { |
| return DebugMessageReport::Print; |
| } |
| |
| // Check with non-syncval messages: |
| for (const char *msg : kSkippedMessages) |
| { |
| if (strstr(message, msg) != nullptr) |
| { |
| 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, but framebuffer fetch has not |
| // been used by the application, report it. |
| if (msg.isDueToNonConformantCoherentFramebufferFetch && !isFramebufferFetchUsed) |
| { |
| 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_DEBUG_REPORT_CALLBACK_EXT: |
| return "Debug Report Callback"; |
| case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV: |
| return "Indirect Commands Layout"; |
| case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT: |
| return "Debug Utils Messenger"; |
| case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT: |
| return "Validation Cache"; |
| case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV: |
| return "Acceleration Structure"; |
| default: |
| return "<Unrecognized>"; |
| } |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL |
| DebugUtilsMessenger(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, |
| VkDebugUtilsMessageTypeFlagsEXT messageTypes, |
| const VkDebugUtilsMessengerCallbackDataEXT *callbackData, |
| void *userData) |
| { |
| 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 VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags, |
| VkDebugReportObjectTypeEXT objectType, |
| uint64_t object, |
| size_t location, |
| int32_t messageCode, |
| const char *layerPrefix, |
| const char *message, |
| void *userData) |
| { |
| RendererVk *rendererVk = static_cast<RendererVk *>(userData); |
| |
| if (ShouldReportDebugMessage(rendererVk, message, message) == DebugMessageReport::Ignore) |
| { |
| return VK_FALSE; |
| } |
| if ((flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) != 0) |
| { |
| ERR() << message; |
| #if !defined(NDEBUG) |
| // Abort the call in Debug builds. |
| return VK_TRUE; |
| #endif |
| } |
| else if ((flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) != 0) |
| { |
| WARN() << message; |
| } |
| else |
| { |
| // Uncomment this if you want Vulkan spam. |
| // WARN() << message; |
| } |
| |
| return VK_FALSE; |
| } |
| |
| 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); |
| } |
| |
| ANGLE_MAYBE_UNUSED bool FencePropertiesCompatibleWithAndroid( |
| const VkExternalFenceProperties &externalFenceProperties) |
| { |
| // handleType here is the external fence type - |
| // we want type compatible with creating and export/dup() Android FD |
| |
| // Imported handleType that can be exported - need for vkGetFenceFdKHR() |
| if ((externalFenceProperties.exportFromImportedHandleTypes & |
| VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0) |
| { |
| return false; |
| } |
| |
| // HandleTypes which can be specified at creating a fence |
| if ((externalFenceProperties.compatibleHandleTypes & |
| VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0) |
| { |
| return false; |
| } |
| |
| constexpr VkExternalFenceFeatureFlags kFeatureFlags = |
| (VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR | |
| VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR); |
| if ((externalFenceProperties.externalFenceFeatures & kFeatureFlags) != kFeatureFlags) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ANGLE_MAYBE_UNUSED bool SemaphorePropertiesCompatibleWithAndroid( |
| const VkExternalSemaphoreProperties &externalSemaphoreProperties) |
| { |
| // handleType here is the external semaphore type - |
| // we want type compatible with importing an Android FD |
| |
| constexpr VkExternalSemaphoreFeatureFlags kFeatureFlags = |
| (VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR); |
| if ((externalSemaphoreProperties.externalSemaphoreFeatures & kFeatureFlags) != kFeatureFlags) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| 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 << chunkIndex; |
| |
| const std::string &hashString = hashStream.str(); |
| angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(hashString.c_str()), |
| hashString.length(), hashOut->data()); |
| } |
| |
| bool 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 false; |
| } |
| |
| // 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)) |
| { |
| return false; |
| } |
| |
| // 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; |
| |
| // Store {numChunks, chunkCompressedData} in keyData, numChunks is used to validate the data. |
| // For example, if the compressed size is 68841 bytes(67k), divide into {2,34421 bytes} and |
| // {2,34420 bytes}. |
| constexpr size_t kBlobHeaderSize = sizeof(uint8_t); |
| size_t compressedOffset = 0; |
| |
| const size_t numChunks = UnsignedCeilDivide(static_cast<unsigned int>(compressedData.size()), |
| kMaxBlobCacheSize - kBlobHeaderSize); |
| size_t chunkSize = UnsignedCeilDivide(static_cast<unsigned int>(compressedData.size()), |
| static_cast<unsigned int>(numChunks)); |
| |
| for (size_t chunkIndex = 0; chunkIndex < numChunks; ++chunkIndex) |
| { |
| if (chunkIndex == numChunks - 1) |
| { |
| chunkSize = compressedData.size() - compressedOffset; |
| } |
| |
| angle::MemoryBuffer keyData; |
| if (!keyData.resize(kBlobHeaderSize + chunkSize)) |
| { |
| return false; |
| } |
| |
| ASSERT(numChunks <= UINT8_MAX); |
| keyData.data()[0] = static_cast<uint8_t>(numChunks); |
| memcpy(keyData.data() + kBlobHeaderSize, compressedData.data() + compressedOffset, |
| chunkSize); |
| compressedOffset += chunkSize; |
| |
| // Create unique hash key. |
| egl::BlobCache::Key chunkCacheHash; |
| ComputePipelineCacheVkChunkKey(physicalDeviceProperties, chunkIndex, &chunkCacheHash); |
| |
| displayVk->getBlobCache()->putApplication(chunkCacheHash, keyData); |
| } |
| |
| return true; |
| } |
| |
| 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), |
| mResult(true) |
| {} |
| |
| void operator()() override |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "CompressAndStorePipelineCacheVk"); |
| mResult = CompressAndStorePipelineCacheVk( |
| mContextVk->getRenderer()->getPhysicalDeviceProperties(), mDisplayVk, mContextVk, |
| mCacheData, mMaxTotalSize); |
| } |
| |
| bool getResult() { return mResult; } |
| |
| private: |
| DisplayVk *mDisplayVk; |
| ContextVk *mContextVk; |
| std::vector<uint8_t> mCacheData; |
| size_t mMaxTotalSize; |
| bool mResult; |
| }; |
| |
| class WaitableCompressEventImpl : public WaitableCompressEvent |
| { |
| public: |
| WaitableCompressEventImpl(std::shared_ptr<angle::WaitableEvent> waitableEvent, |
| std::shared_ptr<CompressAndStorePipelineCacheTask> compressTask) |
| : WaitableCompressEvent(waitableEvent), mCompressTask(compressTask) |
| {} |
| |
| bool getResult() override { return mCompressTask->getResult(); } |
| |
| private: |
| std::shared_ptr<CompressAndStorePipelineCacheTask> mCompressTask; |
| }; |
| |
| angle::Result GetAndDecompressPipelineCacheVk(VkPhysicalDeviceProperties physicalDeviceProperties, |
| DisplayVk *displayVk, |
| angle::MemoryBuffer *uncompressedData, |
| bool *success) |
| { |
| // 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; |
| constexpr size_t kBlobHeaderSize = sizeof(uint8_t); |
| |
| if (!displayVk->getBlobCache()->get(displayVk->getScratchBuffer(), chunkCacheHash, &keyData, |
| &keySize) || |
| keyData.size() < kBlobHeaderSize) |
| { |
| // Nothing in the cache. |
| return angle::Result::Continue; |
| } |
| |
| // Get the number of chunks. |
| size_t numChunks = keyData.data()[0]; |
| size_t chunkSize = keySize - kBlobHeaderSize; |
| 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() < kBlobHeaderSize) |
| { |
| // Can't find every part of the cache data. |
| WARN() << "Failed to get pipeline cache chunk " << chunkIndex << " of " << numChunks; |
| return angle::Result::Continue; |
| } |
| |
| size_t checkNumber = keyData.data()[0]; |
| chunkSize = keySize - kBlobHeaderSize; |
| |
| if (checkNumber != numChunks || compressedData.size() < (compressedSize + chunkSize)) |
| { |
| // Validate the number value and enough space to store. |
| WARN() << "Pipeline cache chunk header corrupted: checkNumber = " << checkNumber |
| << ", numChunks = " << numChunks |
| << ", compressedData.size() = " << compressedData.size() |
| << ", (compressedSize + chunkSize) = " << (compressedSize + chunkSize); |
| return angle::Result::Continue; |
| } |
| memcpy(compressedData.data() + compressedSize, keyData.data() + kBlobHeaderSize, chunkSize); |
| compressedSize += chunkSize; |
| } |
| |
| ANGLE_VK_CHECK( |
| displayVk, |
| egl::DecompressBlobCacheData(compressedData.data(), compressedSize, uncompressedData), |
| VK_ERROR_INITIALIZATION_FAILED); |
| |
| *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 |
| |
| // RendererVk implementation. |
| RendererVk::RendererVk() |
| : mDisplay(nullptr), |
| mLibVulkanLibrary(nullptr), |
| mCapsInitialized(false), |
| mApiVersion(0), |
| mInstance(VK_NULL_HANDLE), |
| mEnableValidationLayers(false), |
| mEnableDebugUtils(false), |
| mAngleDebuggerMode(false), |
| mEnabledICD(angle::vk::ICD::Default), |
| mDebugUtilsMessenger(VK_NULL_HANDLE), |
| mDebugReportCallback(VK_NULL_HANDLE), |
| mPhysicalDevice(VK_NULL_HANDLE), |
| mMaxVertexAttribDivisor(1), |
| mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()), |
| mMaxVertexAttribStride(0), |
| mMinImportedHostPointerAlignment(1), |
| mDefaultUniformBufferSize(kPreferredDefaultUniformBufferSize), |
| mDevice(VK_NULL_HANDLE), |
| mDeviceLost(false), |
| mSuballocationGarbageSizeInBytes(0), |
| mSuballocationGarbageDestroyed(0), |
| mSuballocationGarbageSizeInBytesCachedAtomic(0), |
| mCoherentStagingBufferMemoryTypeIndex(kInvalidMemoryTypeIndex), |
| mNonCoherentStagingBufferMemoryTypeIndex(kInvalidMemoryTypeIndex), |
| mStagingBufferAlignment(1), |
| mHostVisibleVertexConversionBufferMemoryTypeIndex(kInvalidMemoryTypeIndex), |
| mDeviceLocalVertexConversionBufferMemoryTypeIndex(kInvalidMemoryTypeIndex), |
| mVertexConversionBufferAlignment(1), |
| mPipelineCacheVkUpdateTimeout(kPipelineCacheVkUpdatePeriod), |
| mPipelineCacheDirty(false), |
| mPipelineCacheInitialized(false), |
| mValidationMessageCount(0), |
| mCommandProcessor(this), |
| mSupportedVulkanPipelineStageMask(0) |
| { |
| VkFormatProperties invalid = {0, 0, kInvalidFormatFeatureFlags}; |
| mFormatProperties.fill(invalid); |
| |
| // We currently don't have any big-endian devices in the list of supported platforms. There are |
| // a number of places in the Vulkan backend that make this assumption. This assertion is made |
| // early to fail immediately on big-endian platforms. |
| ASSERT(IsLittleEndian()); |
| } |
| |
| RendererVk::~RendererVk() |
| { |
| mAllocator.release(); |
| mPipelineCache.release(); |
| ASSERT(!hasSharedGarbage()); |
| |
| if (mLibVulkanLibrary) |
| { |
| angle::CloseSystemLibrary(mLibVulkanLibrary); |
| } |
| } |
| |
| bool RendererVk::hasSharedGarbage() |
| { |
| std::unique_lock<std::mutex> lock(mGarbageMutex); |
| return !mSharedGarbage.empty() || !mPendingSubmissionGarbage.empty() || |
| !mSuballocationGarbage.empty() || !mPendingSubmissionSuballocationGarbage.empty(); |
| } |
| |
| void RendererVk::releaseSharedResources(vk::ResourceUseList *resourceList) |
| { |
| // resource list may access same resources referenced by garbage collection so need to protect |
| // that access with a lock. |
| std::unique_lock<std::mutex> lock(mGarbageMutex); |
| resourceList->releaseResourceUses(); |
| } |
| |
| void RendererVk::onDestroy(vk::Context *context) |
| { |
| if (isDeviceLost()) |
| { |
| handleDeviceLost(); |
| } |
| |
| for (std::unique_ptr<vk::BufferBlock> &block : mOrphanedBufferBlocks) |
| { |
| ASSERT(block->isEmpty()); |
| block->destroy(this); |
| } |
| mOrphanedBufferBlocks.clear(); |
| |
| { |
| std::unique_lock<std::mutex> lock(mCommandQueueMutex); |
| if (isAsyncCommandQueueEnabled()) |
| { |
| mCommandProcessor.destroy(context); |
| } |
| else |
| { |
| mCommandQueue.destroy(context); |
| } |
| } |
| |
| // Assigns an infinite "last completed" serial to force garbage to delete. |
| cleanupGarbage(Serial::Infinite()); |
| ASSERT(!hasSharedGarbage()); |
| |
| for (PendingOneOffCommands &pending : mPendingOneOffCommands) |
| { |
| pending.commandBuffer.releaseHandle(); |
| } |
| |
| mOneOffCommandPool.destroy(mDevice); |
| |
| mPipelineCache.destroy(mDevice); |
| mSamplerCache.destroy(this); |
| mYuvConversionCache.destroy(this); |
| mVkFormatDescriptorCountMap.clear(); |
| |
| mOutsideRenderPassCommandBufferRecycler.onDestroy(); |
| mRenderPassCommandBufferRecycler.onDestroy(); |
| |
| mAllocator.destroy(); |
| |
| sh::FinalizeGlslang(); |
| |
| if (mDevice) |
| { |
| vkDestroyDevice(mDevice, nullptr); |
| mDevice = VK_NULL_HANDLE; |
| } |
| |
| if (mDebugUtilsMessenger) |
| { |
| vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugUtilsMessenger, nullptr); |
| |
| ASSERT(mDebugReportCallback == VK_NULL_HANDLE); |
| } |
| else if (mDebugReportCallback) |
| { |
| vkDestroyDebugReportCallbackEXT(mInstance, mDebugReportCallback, nullptr); |
| } |
| |
| logCacheStats(); |
| |
| if (mInstance) |
| { |
| vkDestroyInstance(mInstance, nullptr); |
| mInstance = VK_NULL_HANDLE; |
| } |
| |
| if (mCompressEvent) |
| { |
| mCompressEvent->wait(); |
| mCompressEvent.reset(); |
| } |
| |
| mMemoryProperties.destroy(); |
| mPhysicalDevice = VK_NULL_HANDLE; |
| } |
| |
| void RendererVk::notifyDeviceLost() |
| { |
| mDeviceLost = true; |
| mDisplay->notifyDeviceLost(); |
| } |
| |
| bool RendererVk::isDeviceLost() const |
| { |
| return mDeviceLost; |
| } |
| |
| angle::Result RendererVk::initialize(DisplayVk *displayVk, |
| egl::Display *display, |
| const char *wsiExtension, |
| const char *wsiLayer) |
| { |
| 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() && VK_API_VERSION_MAJOR(ver) == 1 && |
| (VK_API_VERSION_MINOR(ver) < 1 || |
| (VK_API_VERSION_MINOR(ver) == 1 && VK_API_VERSION_PATCH(ver) < 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); |
| } |
| |
| // 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())); |
| } |
| |
| // 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)); |
| } |
| } |
| |
| vk::ExtensionNameList instanceExtensionNames; |
| if (!instanceExtensionProps.empty()) |
| { |
| for (const VkExtensionProperties &i : instanceExtensionProps) |
| { |
| instanceExtensionNames.push_back(i.extensionName); |
| } |
| std::sort(instanceExtensionNames.begin(), instanceExtensionNames.end(), StrLess); |
| } |
| |
| if (displayVk->isUsingSwapchain()) |
| { |
| mEnabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); |
| } |
| if (wsiExtension) |
| { |
| mEnabledInstanceExtensions.push_back(wsiExtension); |
| } |
| |
| mEnableDebugUtils = canLoadDebugUtils && mEnableValidationLayers && |
| ExtensionFound(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instanceExtensionNames); |
| |
| bool enableDebugReport = |
| mEnableValidationLayers && !mEnableDebugUtils && |
| ExtensionFound(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instanceExtensionNames); |
| |
| if (mEnableDebugUtils) |
| { |
| mEnabledInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| } |
| else if (enableDebugReport) |
| { |
| mEnabledInstanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); |
| } |
| |
| if (ExtensionFound(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsSurfaceCapabilities2Extension, true); |
| } |
| |
| if (ExtensionFound(VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME, |
| instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back(VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsSurfaceProtectedCapabilitiesExtension, true); |
| } |
| |
| if (ExtensionFound(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsSurfacelessQueryExtension, true); |
| } |
| |
| // 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)); |
| |
| // Enable VK_KHR_get_physical_device_properties_2 if available. |
| if (ExtensionFound(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, |
| instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back( |
| VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| if (ExtensionFound(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalFenceCapabilities, true); |
| } |
| |
| if (ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, |
| instanceExtensionNames)) |
| { |
| mEnabledInstanceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalSemaphoreCapabilities, true); |
| } |
| |
| mApplicationInfo = {}; |
| mApplicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| std::string appName = angle::GetExecutableName(); |
| mApplicationInfo.pApplicationName = appName.c_str(); |
| mApplicationInfo.applicationVersion = 1; |
| mApplicationInfo.pEngineName = "ANGLE"; |
| mApplicationInfo.engineVersion = 1; |
| |
| auto enumerateInstanceVersion = reinterpret_cast<PFN_vkEnumerateInstanceVersion>( |
| vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); |
| if (!enumerateInstanceVersion) |
| { |
| mApiVersion = VK_API_VERSION_1_0; |
| } |
| else |
| { |
| uint32_t apiVersion = VK_API_VERSION_1_0; |
| { |
| ANGLE_SCOPED_DISABLE_LSAN(); |
| ANGLE_SCOPED_DISABLE_MSAN(); |
| ANGLE_VK_TRY(displayVk, enumerateInstanceVersion(&apiVersion)); |
| } |
| if ((VK_VERSION_MAJOR(apiVersion) > 1) || (VK_VERSION_MINOR(apiVersion) >= 1)) |
| { |
| // This is the highest version of core Vulkan functionality that ANGLE uses. |
| mApiVersion = kPreferredVulkanAPIVersion; |
| } |
| else |
| { |
| // Since only 1.0 instance-level functionality is available, this must set to 1.0. |
| mApiVersion = VK_API_VERSION_1_0; |
| } |
| } |
| mApplicationInfo.apiVersion = mApiVersion; |
| |
| 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(); |
| mEnabledInstanceExtensions.push_back(nullptr); |
| |
| instanceInfo.enabledLayerCount = static_cast<uint32_t>(enabledInstanceLayerNames.size()); |
| instanceInfo.ppEnabledLayerNames = enabledInstanceLayerNames.data(); |
| |
| VkValidationFeatureEnableEXT enabledFeatures[] = { |
| VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT}; |
| VkValidationFeaturesEXT validationFeatures = {}; |
| validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; |
| validationFeatures.enabledValidationFeatureCount = 1; |
| validationFeatures.pEnabledValidationFeatures = enabledFeatures; |
| |
| // http://anglebug.com/7050 - Shader validation caching is broken on Android |
| VkValidationFeatureDisableEXT disabledFeatures[] = { |
| VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT}; |
| if (IsAndroid()) |
| { |
| validationFeatures.disabledValidationFeatureCount = 1; |
| validationFeatures.pDisabledValidationFeatures = disabledFeatures; |
| } |
| |
| if (mEnableValidationLayers) |
| { |
| // Enable best practices output which includes perfdoc layer |
| 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) |
| } |
| |
| if (mEnableDebugUtils) |
| { |
| // Use the newer EXT_debug_utils if it exists. |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitDebugUtilsEXTFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| |
| // Create the messenger callback. |
| VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {}; |
| |
| constexpr VkDebugUtilsMessageSeverityFlagsEXT kSeveritiesToLog = |
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | |
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; |
| |
| constexpr VkDebugUtilsMessageTypeFlagsEXT kMessagesToLog = |
| VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | |
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | |
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; |
| |
| messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; |
| messengerInfo.messageSeverity = kSeveritiesToLog; |
| messengerInfo.messageType = kMessagesToLog; |
| messengerInfo.pfnUserCallback = &DebugUtilsMessenger; |
| messengerInfo.pUserData = this; |
| |
| ANGLE_VK_TRY(displayVk, vkCreateDebugUtilsMessengerEXT(mInstance, &messengerInfo, nullptr, |
| &mDebugUtilsMessenger)); |
| } |
| else if (enableDebugReport) |
| { |
| // Fallback to EXT_debug_report. |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitDebugReportEXTFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| |
| VkDebugReportCallbackCreateInfoEXT debugReportInfo = {}; |
| |
| debugReportInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; |
| debugReportInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; |
| debugReportInfo.pfnCallback = &DebugReportCallback; |
| debugReportInfo.pUserData = this; |
| |
| ANGLE_VK_TRY(displayVk, vkCreateDebugReportCallbackEXT(mInstance, &debugReportInfo, nullptr, |
| &mDebugReportCallback)); |
| } |
| |
| if (std::find(mEnabledInstanceExtensions.begin(), mEnabledInstanceExtensions.end(), |
| VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) != |
| mEnabledInstanceExtensions.end()) |
| { |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitGetPhysicalDeviceProperties2KHRFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| ASSERT(vkGetPhysicalDeviceProperties2KHR); |
| } |
| |
| uint32_t physicalDeviceCount = 0; |
| ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr)); |
| ANGLE_VK_CHECK(displayVk, physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED); |
| |
| // TODO(jmadill): Handle multiple physical devices. For now, use the first device. |
| std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount); |
| ANGLE_VK_TRY(displayVk, vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, |
| physicalDevices.data())); |
| ChoosePhysicalDevice(vkGetPhysicalDeviceProperties, physicalDevices, mEnabledICD, |
| &mPhysicalDevice, &mPhysicalDeviceProperties); |
| |
| mGarbageCollectionFlushThreshold = |
| static_cast<uint32_t>(mPhysicalDeviceProperties.limits.maxMemoryAllocationCount * |
| kPercentMaxMemoryAllocationCount); |
| vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysicalDeviceFeatures); |
| |
| // Ensure we can find a graphics queue family. |
| uint32_t 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); |
| |
| // 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)); |
| } |
| |
| // 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); |
| |
| ANGLE_TRY(initializeMemoryAllocator(displayVk)); |
| |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle,startup", "GlslangWarmup"); |
| sh::InitializeGlslang(); |
| } |
| |
| // Initialize the format table. |
| mFormatTable.initialize(this, &mNativeTextureCaps); |
| |
| setGlobalDebugAnnotator(); |
| |
| 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)); |
| mStagingBufferAlignment = std::max( |
| {mStagingBufferAlignment, |
| static_cast<size_t>(mPhysicalDeviceProperties.limits.optimalBufferCopyOffsetAlignment), |
| static_cast<size_t>(mPhysicalDeviceProperties.limits.nonCoherentAtomSize)}); |
| 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; |
| } |
| |
| void RendererVk::queryDeviceExtensionFeatures(const vk::ExtensionNameList &deviceExtensionNames) |
| { |
| // Default initialize all extension features to false. |
| mLineRasterizationFeatures = {}; |
| mLineRasterizationFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; |
| |
| mProvokingVertexFeatures = {}; |
| mProvokingVertexFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; |
| |
| mVertexAttributeDivisorFeatures = {}; |
| mVertexAttributeDivisorFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT; |
| |
| mVertexAttributeDivisorProperties = {}; |
| mVertexAttributeDivisorProperties.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT; |
| |
| mTransformFeedbackFeatures = {}; |
| mTransformFeedbackFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; |
| |
| mIndexTypeUint8Features = {}; |
| mIndexTypeUint8Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT; |
| |
| mSubgroupProperties = {}; |
| mSubgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; |
| |
| 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; |
| |
| mExternalMemoryHostProperties = {}; |
| mExternalMemoryHostProperties.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT; |
| |
| mShaderFloat16Int8Features = {}; |
| mShaderFloat16Int8Features.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; |
| |
| 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; |
| |
| 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; |
| |
| mProtectedMemoryProperties = {}; |
| mProtectedMemoryProperties.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES; |
| |
| mHostQueryResetFeatures = {}; |
| mHostQueryResetFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT; |
| |
| mDepthClipControlFeatures = {}; |
| mDepthClipControlFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT; |
| |
| mBlendOperationAdvancedFeatures = {}; |
| mBlendOperationAdvancedFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_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; |
| |
| mFragmentShadingRateFeatures = {}; |
| mFragmentShadingRateFeatures.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; |
| |
| if (!vkGetPhysicalDeviceProperties2KHR || !vkGetPhysicalDeviceFeatures2KHR) |
| { |
| return; |
| } |
| |
| // Query features and properties. |
| VkPhysicalDeviceFeatures2KHR deviceFeatures = {}; |
| deviceFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
| |
| VkPhysicalDeviceProperties2 deviceProperties = {}; |
| deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; |
| |
| // Query line rasterization features |
| if (ExtensionFound(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mLineRasterizationFeatures); |
| } |
| |
| // Query provoking vertex features |
| if (ExtensionFound(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mProvokingVertexFeatures); |
| } |
| |
| // Query attribute divisor features and properties |
| if (ExtensionFound(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mVertexAttributeDivisorFeatures); |
| vk::AddToPNextChain(&deviceProperties, &mVertexAttributeDivisorProperties); |
| } |
| |
| // Query transform feedback features |
| if (ExtensionFound(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mTransformFeedbackFeatures); |
| } |
| |
| // Query uint8 index type features |
| if (ExtensionFound(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mIndexTypeUint8Features); |
| } |
| |
| // Query memory report features |
| if (ExtensionFound(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mMemoryReportFeatures); |
| } |
| |
| // Query external memory host properties |
| if (ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceProperties, &mExternalMemoryHostProperties); |
| } |
| |
| // Query Ycbcr conversion properties |
| if (ExtensionFound(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mSamplerYcbcrConversionFeatures); |
| } |
| |
| // Query float16/int8 features |
| if (ExtensionFound(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mShaderFloat16Int8Features); |
| } |
| |
| // Query depth/stencil resolve properties |
| if (ExtensionFound(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceProperties, &mDepthStencilResolveProperties); |
| } |
| |
| // Query multisampled render to single-sampled features |
| if (ExtensionFound(VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME, |
| deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mMultisampledRenderToSingleSampledFeatures); |
| } |
| |
| if (ExtensionFound(VK_EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mImage2dViewOf3dFeatures); |
| } |
| |
| // Query multiview features and properties |
| if (ExtensionFound(VK_KHR_MULTIVIEW_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mMultiviewFeatures); |
| vk::AddToPNextChain(&deviceProperties, &mMultiviewProperties); |
| } |
| |
| // Query driver properties |
| if (ExtensionFound(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceProperties, &mDriverProperties); |
| } |
| |
| // Query custom border color features |
| if (ExtensionFound(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mCustomBorderColorFeatures); |
| } |
| |
| // Query subgroup properties |
| vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties); |
| |
| // Query subgroup extended types features |
| if (ExtensionFound(VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mSubgroupExtendedTypesFeatures); |
| } |
| |
| // Query protected memory features and properties |
| if (mPhysicalDeviceProperties.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mProtectedMemoryFeatures); |
| vk::AddToPNextChain(&deviceProperties, &mProtectedMemoryProperties); |
| } |
| |
| // Query host query reset features |
| if (ExtensionFound(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, deviceExtensionNames) || |
| mPhysicalDeviceProperties.apiVersion >= VK_MAKE_VERSION(1, 2, 0)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mHostQueryResetFeatures); |
| } |
| |
| if (ExtensionFound(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mDepthClipControlFeatures); |
| } |
| |
| if (ExtensionFound(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mBlendOperationAdvancedFeatures); |
| } |
| |
| 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); |
| } |
| |
| if (ExtensionFound(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| vk::AddToPNextChain(&deviceFeatures, &mFragmentShadingRateFeatures); |
| } |
| |
| vkGetPhysicalDeviceFeatures2KHR(mPhysicalDevice, &deviceFeatures); |
| vkGetPhysicalDeviceProperties2KHR(mPhysicalDevice, &deviceProperties); |
| |
| // Clean up pNext chains |
| 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; |
| mExternalMemoryHostProperties.pNext = nullptr; |
| mCustomBorderColorFeatures.pNext = nullptr; |
| mShaderFloat16Int8Features.pNext = nullptr; |
| mDepthStencilResolveProperties.pNext = nullptr; |
| mMultisampledRenderToSingleSampledFeatures.pNext = nullptr; |
| mImage2dViewOf3dFeatures.pNext = nullptr; |
| mMultiviewFeatures.pNext = nullptr; |
| mMultiviewProperties.pNext = nullptr; |
| mDriverProperties.pNext = nullptr; |
| mSamplerYcbcrConversionFeatures.pNext = nullptr; |
| mProtectedMemoryFeatures.pNext = nullptr; |
| mProtectedMemoryProperties.pNext = nullptr; |
| mHostQueryResetFeatures.pNext = nullptr; |
| mDepthClipControlFeatures.pNext = nullptr; |
| mBlendOperationAdvancedFeatures.pNext = nullptr; |
| mExtendedDynamicStateFeatures.pNext = nullptr; |
| mExtendedDynamicState2Features.pNext = nullptr; |
| mFragmentShadingRateFeatures.pNext = nullptr; |
| } |
| |
| angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex) |
| { |
| uint32_t deviceLayerCount = 0; |
| ANGLE_VK_TRY(displayVk, |
| vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, nullptr)); |
| |
| std::vector<VkLayerProperties> deviceLayerProps(deviceLayerCount); |
| ANGLE_VK_TRY(displayVk, vkEnumerateDeviceLayerProperties(mPhysicalDevice, &deviceLayerCount, |
| deviceLayerProps.data())); |
| |
| VulkanLayerVector enabledDeviceLayerNames; |
| if (mEnableValidationLayers) |
| { |
| mEnableValidationLayers = |
| GetAvailableValidationLayers(deviceLayerProps, false, &enabledDeviceLayerNames); |
| } |
| |
| const char *wsiLayer = displayVk->getWSILayer(); |
| if (wsiLayer) |
| { |
| enabledDeviceLayerNames.push_back(wsiLayer); |
| } |
| |
| // Enumerate device extensions that are provided by the vulkan |
| // implementation and implicit layers. |
| uint32_t deviceExtensionCount = 0; |
| ANGLE_VK_TRY(displayVk, vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, |
| &deviceExtensionCount, nullptr)); |
| |
| // 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( |
| mPhysicalDevice, layerName, &deviceLayerExtensionCount, |
| deviceExtensionProps.data() + previousExtensionCount)); |
| } |
| |
| vk::ExtensionNameList deviceExtensionNames; |
| if (!deviceExtensionProps.empty()) |
| { |
| ASSERT(deviceExtensionNames.size() <= deviceExtensionProps.size()); |
| for (const VkExtensionProperties &prop : deviceExtensionProps) |
| { |
| deviceExtensionNames.push_back(prop.extensionName); |
| } |
| std::sort(deviceExtensionNames.begin(), deviceExtensionNames.end(), StrLess); |
| } |
| |
| if (displayVk->isUsingSwapchain()) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| } |
| |
| // Query extensions and their features. |
| queryDeviceExtensionFeatures(deviceExtensionNames); |
| |
| // Initialize features and workarounds. |
| initFeatures(displayVk, deviceExtensionNames); |
| |
| // App based feature overrides. |
| appBasedFeatureOverrides(displayVk, deviceExtensionNames); |
| |
| // Enable VK_KHR_shared_presentable_image |
| if (ExtensionFound(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME); |
| ANGLE_FEATURE_CONDITION(&mFeatures, supportsSharedPresentableImageExtension, true); |
| } |
| |
| // Enable VK_EXT_depth_clip_enable, if supported |
| if (ExtensionFound(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME); |
| } |
| |
| // Enable VK_KHR_get_memory_requirements2, if supported |
| bool hasGetMemoryRequirements2KHR = false; |
| if (ExtensionFound(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); |
| hasGetMemoryRequirements2KHR = true; |
| } |
| |
| if (ExtensionFound(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); |
| } |
| |
| // Enable VK_KHR_bind_memory2, if supported |
| bool hasBindMemory2KHR = false; |
| if (ExtensionFound(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, deviceExtensionNames)) |
| { |
| hasBindMemory2KHR = true; |
| mEnabledDeviceExtensions.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME); |
| } |
| |
| // Enable KHR_MAINTENANCE1 to support viewport flipping. |
| if (mPhysicalDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsRenderpass2.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsIncrementalPresent.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME); |
| } |
| |
| #if defined(ANGLE_PLATFORM_ANDROID) |
| if (getFeatures().supportsAndroidHardwareBuffer.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME); |
| mEnabledDeviceExtensions.push_back( |
| VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME); |
| # if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalMemoryHardwareBufferANDROIDFunctions(mInstance); |
| # endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| #else |
| ASSERT(!getFeatures().supportsAndroidHardwareBuffer.enabled); |
| #endif |
| |
| #if defined(ANGLE_PLATFORM_GGP) |
| if (getFeatures().supportsGGPFrameToken.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_GGP_FRAME_TOKEN_EXTENSION_NAME); |
| } |
| ANGLE_VK_CHECK(displayVk, getFeatures().supportsGGPFrameToken.enabled, |
| VK_ERROR_EXTENSION_NOT_PRESENT); |
| #else |
| ASSERT(!getFeatures().supportsGGPFrameToken.enabled); |
| #endif |
| |
| if (getFeatures().supportsAndroidHardwareBuffer.enabled || |
| getFeatures().supportsExternalMemoryFd.enabled || |
| getFeatures().supportsExternalMemoryFuchsia.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsExternalMemoryFd.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsExternalMemoryFuchsia.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsExternalSemaphoreFd.enabled || |
| getFeatures().supportsExternalSemaphoreFuchsia.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME); |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalSemaphoreFdFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| |
| if (getFeatures().supportsExternalSemaphoreFd.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsExternalSemaphoreCapabilities.enabled) |
| { |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalSemaphoreCapabilitiesFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| |
| if (getFeatures().supportsExternalFenceFd.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME); |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalFenceFdFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| |
| if (getFeatures().supportsExternalFenceCapabilities.enabled) |
| { |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalFenceCapabilitiesFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| |
| if (getFeatures().supportsExternalSemaphoreFuchsia.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsShaderStencilExport.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsRenderPassLoadStoreOpNone.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME); |
| } |
| else if (getFeatures().supportsRenderPassStoreOpNone.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsImageFormatList.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); |
| } |
| |
| std::sort(mEnabledDeviceExtensions.begin(), mEnabledDeviceExtensions.end(), StrLess); |
| ANGLE_VK_TRY(displayVk, |
| VerifyExtensionsPresent(deviceExtensionNames, mEnabledDeviceExtensions)); |
| |
| // Select additional features to be enabled. |
| mEnabledFeatures = {}; |
| mEnabledFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
| // Used to support cubemap array: |
| mEnabledFeatures.features.imageCubeArray = getFeatures().supportsImageCubeArray.enabled; |
| // Used to support framebuffers with multiple attachments: |
| mEnabledFeatures.features.independentBlend = mPhysicalDeviceFeatures.independentBlend; |
| // Used to support multi_draw_indirect |
| mEnabledFeatures.features.multiDrawIndirect = mPhysicalDeviceFeatures.multiDrawIndirect; |
| // Used to support robust buffer access: |
| mEnabledFeatures.features.robustBufferAccess = mPhysicalDeviceFeatures.robustBufferAccess; |
| // Used to support Anisotropic filtering: |
| mEnabledFeatures.features.samplerAnisotropy = mPhysicalDeviceFeatures.samplerAnisotropy; |
| // Used to support wide lines: |
| mEnabledFeatures.features.wideLines = mPhysicalDeviceFeatures.wideLines; |
| // Used to emulate transform feedback: |
| mEnabledFeatures.features.vertexPipelineStoresAndAtomics = |
| mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics; |
| // Used to implement storage buffers and images in the fragment shader: |
| mEnabledFeatures.features.fragmentStoresAndAtomics = |
| mPhysicalDeviceFeatures.fragmentStoresAndAtomics; |
| // Used to emulate the primitives generated query: |
| mEnabledFeatures.features.pipelineStatisticsQuery = |
| getFeatures().supportsPipelineStatisticsQuery.enabled; |
| // Used to support geometry shaders: |
| mEnabledFeatures.features.geometryShader = mPhysicalDeviceFeatures.geometryShader; |
| // Used to support EXT_gpu_shader5: |
| mEnabledFeatures.features.shaderImageGatherExtended = |
| mPhysicalDeviceFeatures.shaderImageGatherExtended; |
| // Used to support EXT_gpu_shader5: |
| mEnabledFeatures.features.shaderUniformBufferArrayDynamicIndexing = |
| mPhysicalDeviceFeatures.shaderUniformBufferArrayDynamicIndexing; |
| mEnabledFeatures.features.shaderSampledImageArrayDynamicIndexing = |
| mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing; |
| // Used to support APPLE_clip_distance |
| mEnabledFeatures.features.shaderClipDistance = mPhysicalDeviceFeatures.shaderClipDistance; |
| // Used to support OES_sample_shading |
| mEnabledFeatures.features.sampleRateShading = mPhysicalDeviceFeatures.sampleRateShading; |
| // Used to support depth clears through draw calls. |
| mEnabledFeatures.features.depthClamp = mPhysicalDeviceFeatures.depthClamp; |
| // Used to support EXT_clip_cull_distance |
| mEnabledFeatures.features.shaderCullDistance = mPhysicalDeviceFeatures.shaderCullDistance; |
| // Used to support tessellation Shader: |
| mEnabledFeatures.features.tessellationShader = mPhysicalDeviceFeatures.tessellationShader; |
| // Used to support EXT_blend_func_extended |
| mEnabledFeatures.features.dualSrcBlend = mPhysicalDeviceFeatures.dualSrcBlend; |
| |
| if (!vk::OutsideRenderPassCommandBuffer::ExecutesInline() || |
| !vk::RenderPassCommandBuffer::ExecutesInline()) |
| { |
| mEnabledFeatures.features.inheritedQueries = mPhysicalDeviceFeatures.inheritedQueries; |
| } |
| |
| // Setup device initialization struct |
| VkDeviceCreateInfo createInfo = {}; |
| |
| // Based on available extension features, decide on which extensions and features to enable. |
| |
| if (mLineRasterizationFeatures.bresenhamLines) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mLineRasterizationFeatures); |
| } |
| |
| if (mProvokingVertexFeatures.provokingVertexLast) |
| { |
| 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())); |
| } |
| |
| // Enable VK_KHR_shader_subgroup_extended_types if needed |
| if (getFeatures().allowGenerateMipmapWithCompute.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mSubgroupExtendedTypesFeatures); |
| } |
| |
| if (getFeatures().supportsTransformFeedbackExtension.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mTransformFeedbackFeatures); |
| } |
| |
| if (getFeatures().supportsCustomBorderColor.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mCustomBorderColorFeatures); |
| } |
| |
| if (getFeatures().supportsIndexTypeUint8.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mIndexTypeUint8Features); |
| } |
| |
| if (getFeatures().supportsDepthStencilResolve.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); |
| } |
| |
| if (getFeatures().supportsMultisampledRenderToSingleSampled.enabled) |
| { |
| mEnabledDeviceExtensions.push_back( |
| VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mMultisampledRenderToSingleSampledFeatures); |
| } |
| |
| if (getFeatures().supportsMultiview.enabled) |
| { |
| // OVR_multiview disallows multiview with geometry and tessellation, so don't request these |
| // features. |
| mMultiviewFeatures.multiviewGeometryShader = VK_FALSE; |
| mMultiviewFeatures.multiviewTessellationShader = VK_FALSE; |
| mEnabledDeviceExtensions.push_back(VK_KHR_MULTIVIEW_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mMultiviewFeatures); |
| } |
| |
| if (getFeatures().logMemoryReportCallbacks.enabled || |
| getFeatures().logMemoryReportStats.enabled) |
| { |
| if (mMemoryReportFeatures.deviceMemoryReport) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME); |
| |
| mMemoryReportCallback = {}; |
| mMemoryReportCallback.sType = |
| VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT; |
| mMemoryReportCallback.pfnUserCallback = &MemoryReportCallback; |
| mMemoryReportCallback.pUserData = this; |
| vk::AddToPNextChain(&createInfo, &mMemoryReportCallback); |
| } |
| else |
| { |
| WARN() << "Disabling the following feature(s) because driver does not support " |
| "VK_EXT_device_memory_report extension:"; |
| if (getFeatures().logMemoryReportStats.enabled) |
| { |
| WARN() << "\tlogMemoryReportStats"; |
| ANGLE_FEATURE_CONDITION(&mFeatures, logMemoryReportStats, false); |
| } |
| if (getFeatures().logMemoryReportCallbacks.enabled) |
| { |
| WARN() << "\tlogMemoryReportCallbacks"; |
| ANGLE_FEATURE_CONDITION(&mFeatures, logMemoryReportCallbacks, false); |
| } |
| } |
| } |
| |
| if (getFeatures().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 (getFeatures().supportsExternalMemoryHost.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME); |
| mMinImportedHostPointerAlignment = |
| mExternalMemoryHostProperties.minImportedHostPointerAlignment; |
| #if !defined(ANGLE_SHARED_LIBVULKAN) |
| InitExternalMemoryHostFunctions(mInstance); |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| } |
| |
| if (getFeatures().supportsYUVSamplerConversion.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mSamplerYcbcrConversionFeatures); |
| } |
| |
| if (getFeatures().supportsShaderFloat16.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mShaderFloat16Int8Features); |
| } |
| |
| if (getFeatures().supportsProtectedMemory.enabled) |
| { |
| vk::AddToPNextChain(&mEnabledFeatures, &mProtectedMemoryFeatures); |
| } |
| |
| if (getFeatures().supportsHostQueryReset.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mHostQueryResetFeatures); |
| } |
| |
| if (getFeatures().supportsDepthClipControl.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mDepthClipControlFeatures); |
| } |
| |
| if (getFeatures().supportsBlendOperationAdvanced.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mBlendOperationAdvancedFeatures); |
| } |
| |
| if (getFeatures().supportsExtendedDynamicState.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mExtendedDynamicStateFeatures); |
| } |
| |
| if (getFeatures().supportsExtendedDynamicState2.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mExtendedDynamicState2Features); |
| } |
| |
| if (getFeatures().supportsFragmentShadingRate.enabled) |
| { |
| mEnabledDeviceExtensions.push_back(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); |
| vk::AddToPNextChain(&mEnabledFeatures, &mFragmentShadingRateFeatures); |
| } |
| |
| mCurrentQueueFamilyIndex = queueFamilyIndex; |
| |
| vk::QueueFamily queueFamily; |
| queueFamily.initialize(mQueueFamilyProperties[queueFamilyIndex], queueFamilyIndex); |
| ANGLE_VK_CHECK(displayVk, queueFamily.getDeviceQueueCount() > 0, |
| VK_ERROR_INITIALIZATION_FAILED); |
| |
| // We enable protected context only if both supportsProtectedMemory and device also supports |
| // protected. There are cases we have to disable supportsProtectedMemory feature due to driver |
| // bugs. |
| bool enableProtectedContent = |
| queueFamily.supportsProtected() && getFeatures().supportsProtectedMemory.enabled; |
| |
| uint32_t queueCount = std::min(queueFamily.getDeviceQueueCount(), |
| static_cast<uint32_t>(egl::ContextPriority::EnumCount)); |
| |
| uint32_t queueCreateInfoCount = 1; |
| VkDeviceQueueCreateInfo queueCreateInfo[1] = {}; |
| queueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
| queueCreateInfo[0].flags = enableProtectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0; |
| queueCreateInfo[0].queueFamilyIndex = queueFamilyIndex; |
| queueCreateInfo[0].queueCount = queueCount; |
| queueCreateInfo[0].pQueuePriorities = vk::QueueFamily::kQueuePriorities; |
| |
| // Create Device |
| createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
| createInfo.flags = 0; |
| createInfo.queueCreateInfoCount = queueCreateInfoCount; |
| createInfo.pQueueCreateInfos = queueCreateInfo; |
| createInfo.enabledLayerCount = static_cast<uint32_t>(enabledDeviceLayerNames.size()); |
| createInfo.ppEnabledLayerNames = enabledDeviceLayerNames.data(); |
| createInfo.enabledExtensionCount = static_cast<uint32_t>(mEnabledDeviceExtensions.size()); |
| createInfo.ppEnabledExtensionNames = |
| mEnabledDeviceExtensions.empty() ? nullptr : mEnabledDeviceExtensions.data(); |
| mEnabledDeviceExtensions.push_back(nullptr); |
| |
| // Enable core features without assuming VkPhysicalDeviceFeatures2KHR is accepted in the |
| // pNext chain of VkDeviceCreateInfo. |
| createInfo.pEnabledFeatures = &mEnabledFeatures.features; |
| |
| // Append the feature structs chain to the end of createInfo structs chain. |
| if (mEnabledFeatures.pNext) |
| { |
| vk::AppendToPNextChain(&createInfo, mEnabledFeatures.pNext); |
| } |
| |
| ANGLE_VK_TRY(displayVk, vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice)); |
| #if defined(ANGLE_SHARED_LIBVULKAN) |
| // Load volk if we are loading dynamically |
| volkLoadDevice(mDevice); |
| #endif // defined(ANGLE_SHARED_LIBVULKAN) |
| |
| vk::DeviceQueueMap graphicsQueueMap = |
| queueFamily.initializeQueueMap(mDevice, enableProtectedContent, 0, queueCount); |
| |
| if (isAsyncCommandQueueEnabled()) |
| { |
| ANGLE_TRY(mCommandProcessor.init(displayVk, graphicsQueueMap)); |
| } |
| else |
| { |
| ANGLE_TRY(mCommandQueue.init(displayVk, graphicsQueueMap)); |
| } |
| |
| #if defined(ANGLE_SHARED_LIBVULKAN) |
| // Avoid compiler warnings on unused-but-set variables. |
| ANGLE_UNUSED_VARIABLE(hasGetMemoryRequirements2KHR); |
| ANGLE_UNUSED_VARIABLE(hasBindMemory2KHR); |
| #else |
| if (getFeatures().supportsHostQueryReset.enabled) |
| { |
| InitHostQueryResetFunctions(mInstance); |
| } |
| if (hasGetMemoryRequirements2KHR) |
| { |
| InitGetMemoryRequirements2KHRFunctions(mDevice); |
| } |
| if (hasBindMemory2KHR) |
| { |
| InitBindMemory2KHRFunctions(mDevice); |
| } |
| if (getFeatures().supportsTransformFeedbackExtension.enabled) |
| { |
| InitTransformFeedbackEXTFunctions(mDevice); |
| } |
| if (getFeatures().supportsYUVSamplerConversion.enabled) |
| { |
| InitSamplerYcbcrKHRFunctions(mDevice); |
| } |
| if (getFeatures().supportsRenderpass2.enabled) |
| { |
| InitRenderPass2KHRFunctions(mDevice); |
| } |
| if (getFeatures().supportsExtendedDynamicState.enabled) |
| { |
| InitExtendedDynamicStateEXTFunctions(mDevice); |
| } |
| if (getFeatures().supportsExtendedDynamicState2.enabled) |
| { |
| InitExtendedDynamicState2EXTFunctions(mDevice); |
| } |
| #endif // !defined(ANGLE_SHARED_LIBVULKAN) |
| |
| if (getFeatures().forceMaxUniformBufferSize16KB.enabled) |
| { |
| mDefaultUniformBufferSize = kMinDefaultUniformBufferSize; |
| } |
| // Cap it with the driver limit |
| mDefaultUniformBufferSize = std::min( |
| mDefaultUniformBufferSize, getPhysicalDeviceProperties().limits.maxUniformBufferRange); |
| |
| // Initialize the vulkan pipeline cache. |
| bool success = false; |
| { |
| std::unique_lock<std::mutex> lock(mPipelineCacheMutex); |
| ANGLE_TRY(initPipelineCache(displayVk, &mPipelineCache, &success)); |
| } |
| |
| // Track the set of supported pipeline stages. This is used when issuing image layout |
| // transitions that cover many stages (such as AllGraphicsReadOnly) to mask out unsupported |
| // stages, which avoids enumerating every possible combination of stages in the layouts. |
| VkPipelineStageFlags unsupportedStages = 0; |
| if (!mPhysicalDeviceFeatures.tessellationShader) |
| { |
| unsupportedStages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | |
| VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; |
| } |
| if (!mPhysicalDeviceFeatures.geometryShader) |
| { |
| unsupportedStages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; |
| } |
| mSupportedVulkanPipelineStageMask = ~unsupportedStages; |
| |
| // Build the list of syncval errors that are currently expected and should be skipped. |
| mSkippedSyncvalMessages.insert(mSkippedSyncvalMessages.end(), kSkippedSyncvalMessages, |
| kSkippedSyncvalMessages + ArraySize(kSkippedSyncvalMessages)); |
| if (!getFeatures().supportsRenderPassStoreOpNone.enabled && |
| !getFeatures().supportsRenderPassLoadStoreOpNone.enabled) |
| { |
| mSkippedSyncvalMessages.insert(mSkippedSyncvalMessages.end(), |
| kSkippedSyncvalMessagesWithoutStoreOpNone, |
| kSkippedSyncvalMessagesWithoutStoreOpNone + |
| ArraySize(kSkippedSyncvalMessagesWithoutStoreOpNone)); |
| } |
| if (!getFeatures().supportsRenderPassLoadStoreOpNone.enabled) |
| { |
| mSkippedSyncvalMessages.insert( |
| mSkippedSyncvalMessages.end(), kSkippedSyncvalMessagesWithoutLoadStoreOpNone, |
| kSkippedSyncvalMessagesWithoutLoadStoreOpNone + |
| ArraySize(kSkippedSyncvalMessagesWithoutLoadStoreOpNone)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result RendererVk::selectPresentQueueForSurface(DisplayVk *displayVk, |
| VkSurfaceKHR surface, |
| uint32_t *presentQueueOut) |
| { |
| // We've already initialized a device, and can't re-create it unless it's never been used. |
| // TODO(jmadill): Handle the re-creation case if necessary. |
| if (mDevice != VK_NULL_HANDLE) |
| { |
| ASSERT(mCurrentQueueFamilyIndex != std::numeric_limits<uint32_t>::max()); |
| |
| // Check if the current device supports present on this surface. |
| VkBool32 supportsPresent = VK_FALSE; |
| ANGLE_VK_TRY(displayVk, |
| vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, mCurrentQueueFamilyIndex, |
| surface, &supportsPresent)); |
| |
| if (supportsPresent == VK_TRUE) |
| { |
| *presentQueueOut = mCurrentQueueFamilyIndex; |
| return angle::Result::Continue; |
| } |
| } |
| |
| // Find a graphics and present queue. |
| Optional<uint32_t> newPresentQueue; |
| uint32_t queueCount = static_cast<uint32_t>(mQueueFamilyProperties.size()); |
| constexpr VkQueueFlags kGraphicsAndCompute = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; |
| for (uint32_t queueIndex = 0; queueIndex < queueCount; ++queueIndex) |
| { |
| const auto &queueInfo = mQueueFamilyProperties[queueIndex]; |
| if ((queueInfo.queueFlags & kGraphicsAndCompute) == kGraphicsAndCompute) |
| { |
| VkBool32 supportsPresent = VK_FALSE; |
| ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceSupportKHR( |
| mPhysicalDevice, queueIndex, surface, &supportsPresent)); |
| |
| if (supportsPresent == VK_TRUE) |
| { |
| newPresentQueue = queueIndex; |
| break; |
| } |
| } |
| } |
| |
| ANGLE_VK_CHECK(displayVk, newPresentQueue.valid(), VK_ERROR_INITIALIZATION_FAILED); |
| ANGLE_TRY(initializeDevice(displayVk, newPresentQueue.value())); |
| |
| *presentQueueOut = newPresentQueue.value(); |
| return angle::Result::Continue; |
| } |
| |
| std::string RendererVk::getVendorString() const |
| { |
| return GetVendorString(mPhysicalDeviceProperties.vendorID); |
| } |
| |
| std::string RendererVk::getRendererDescription() const |
| { |
| std::stringstream strstr; |
| |
| uint32_t apiVersion = mPhysicalDeviceProperties.apiVersion; |
| |
| strstr << "Vulkan "; |
| strstr << VK_VERSION_MAJOR(apiVersion) << "."; |
| strstr << VK_VERSION_MINOR(apiVersion) << "."; |
| strstr << VK_VERSION_PATCH(apiVersion); |
| |
| strstr << " ("; |
| |
| // In the case of NVIDIA, deviceName does not necessarily contain "NVIDIA". Add "NVIDIA" so that |
| // Vulkan end2end tests can be selectively disabled on NVIDIA. TODO(jmadill): should not be |
|