diff --git a/impeller/base/timing.h b/impeller/base/timing.h index b60104337e861..1496fd9eb9503 100644 --- a/impeller/base/timing.h +++ b/impeller/base/timing.h @@ -8,6 +8,7 @@ namespace impeller { +using MillisecondsF = std::chrono::duration; using SecondsF = std::chrono::duration; using Clock = std::chrono::high_resolution_clock; using TimePoint = std::chrono::time_point; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index c9cb8722cb652..84585b84ec33f 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -54,7 +54,7 @@ bool CapabilitiesVK::AreValidationsEnabled() const { return enable_validations_; } -std::optional> CapabilitiesVK::GetRequiredLayers() +std::optional> CapabilitiesVK::GetEnabledLayers() const { std::vector required; @@ -71,7 +71,7 @@ std::optional> CapabilitiesVK::GetRequiredLayers() } std::optional> -CapabilitiesVK::GetRequiredInstanceExtensions() const { +CapabilitiesVK::GetEnabledInstanceExtensions() const { std::vector required; if (!HasExtension("VK_KHR_surface")) { @@ -150,9 +150,29 @@ CapabilitiesVK::GetRequiredInstanceExtensions() const { return required; } -std::optional> -CapabilitiesVK::GetRequiredDeviceExtensions( - const vk::PhysicalDevice& physical_device) const { +static const char* GetDeviceExtensionName(OptionalDeviceExtensionVK ext) { + switch (ext) { + case OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback: + return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME; + case OptionalDeviceExtensionVK::kLast: + return "Unknown"; + } + return "Unknown"; +} + +static void IterateOptionalDeviceExtensions( + const std::function& it) { + if (!it) { + return; + } + for (size_t i = 0; + i < static_cast(OptionalDeviceExtensionVK::kLast); i++) { + it(static_cast(i)); + } +} + +static std::optional> GetSupportedDeviceExtensions( + const vk::PhysicalDevice& physical_device) { auto device_extensions = physical_device.enumerateDeviceExtensionProperties(); if (device_extensions.result != vk::Result::eSuccess) { return std::nullopt; @@ -161,21 +181,42 @@ CapabilitiesVK::GetRequiredDeviceExtensions( std::set exts; for (const auto& device_extension : device_extensions.value) { exts.insert(device_extension.extensionName); + }; + + return exts; +} + +std::optional> +CapabilitiesVK::GetEnabledDeviceExtensions( + const vk::PhysicalDevice& physical_device) const { + auto exts = GetSupportedDeviceExtensions(physical_device); + + if (!exts.has_value()) { + return std::nullopt; } - std::vector required; + std::vector enabled; - if (exts.find("VK_KHR_swapchain") == exts.end()) { + if (exts->find("VK_KHR_swapchain") == exts->end()) { VALIDATION_LOG << "Device does not support the swapchain extension."; return std::nullopt; } - required.push_back("VK_KHR_swapchain"); + enabled.push_back("VK_KHR_swapchain"); // Required for non-conformant implementations like MoltenVK. - if (exts.find("VK_KHR_portability_subset") != exts.end()) { - required.push_back("VK_KHR_portability_subset"); + if (exts->find("VK_KHR_portability_subset") != exts->end()) { + enabled.push_back("VK_KHR_portability_subset"); } - return required; + + // Enable all optional extensions if the device supports it. + IterateOptionalDeviceExtensions([&](auto ext) { + auto ext_name = GetDeviceExtensionName(ext); + if (exts->find(ext_name) != exts->end()) { + enabled.push_back(ext_name); + } + }); + + return enabled; } static bool HasSuitableColorFormat(const vk::PhysicalDevice& device, @@ -227,7 +268,7 @@ static bool HasRequiredQueues(const vk::PhysicalDevice& physical_device) { } std::optional -CapabilitiesVK::GetRequiredDeviceFeatures( +CapabilitiesVK::GetEnabledDeviceFeatures( const vk::PhysicalDevice& device) const { if (!PhysicalDeviceSupportsRequiredFormats(device)) { VALIDATION_LOG << "Device doesn't support the required formats."; @@ -244,7 +285,7 @@ CapabilitiesVK::GetRequiredDeviceFeatures( return std::nullopt; } - if (!GetRequiredDeviceExtensions(device).has_value()) { + if (!GetEnabledDeviceExtensions(device).has_value()) { VALIDATION_LOG << "Device doesn't support the required queues."; return std::nullopt; } @@ -282,7 +323,7 @@ void CapabilitiesVK::SetOffscreenFormat(PixelFormat pixel_format) const { color_format_ = pixel_format; } -bool CapabilitiesVK::SetDevice(const vk::PhysicalDevice& device) { +bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { if (HasSuitableDepthStencilFormat(device, vk::Format::eS8Uint)) { depth_stencil_format_ = PixelFormat::kS8UInt; } else if (HasSuitableDepthStencilFormat(device, @@ -307,6 +348,21 @@ bool CapabilitiesVK::SetDevice(const vk::PhysicalDevice& device) { .supportedOperations & vk::SubgroupFeatureFlagBits::eArithmetic); + // Determine the optional device extensions this physical device supports. + { + optional_device_extensions_.clear(); + auto exts = GetSupportedDeviceExtensions(device); + if (!exts.has_value()) { + return false; + } + IterateOptionalDeviceExtensions([&](auto ext) { + auto ext_name = GetDeviceExtensionName(ext); + if (exts->find(ext_name) != exts->end()) { + optional_device_extensions_.insert(ext); + } + }); + } + return true; } @@ -348,7 +404,7 @@ bool CapabilitiesVK::SupportsCompute() const { // |Capabilities| bool CapabilitiesVK::SupportsComputeSubgroups() const { - // Set by |SetDevice|. + // Set by |SetPhysicalDevice|. return supports_compute_subgroups_; } @@ -381,4 +437,10 @@ CapabilitiesVK::GetPhysicalDeviceProperties() const { return device_properties_; } +bool CapabilitiesVK::HasOptionalDeviceExtension( + OptionalDeviceExtensionVK extension) const { + return optional_device_extensions_.find(extension) != + optional_device_extensions_.end(); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index 46a6a895de322..98614011206ff 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -18,6 +18,12 @@ namespace impeller { class ContextVK; +enum class OptionalDeviceExtensionVK : uint32_t { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_pipeline_creation_feedback.html + kEXTPipelineCreationFeedback, + kLast, +}; + //------------------------------------------------------------------------------ /// @brief The Vulkan layers and extensions wrangler. /// @@ -32,17 +38,20 @@ class CapabilitiesVK final : public Capabilities, bool AreValidationsEnabled() const; - std::optional> GetRequiredLayers() const; + bool HasOptionalDeviceExtension(OptionalDeviceExtensionVK extension) const; + + std::optional> GetEnabledLayers() const; - std::optional> GetRequiredInstanceExtensions() const; + std::optional> GetEnabledInstanceExtensions() const; - std::optional> GetRequiredDeviceExtensions( + std::optional> GetEnabledDeviceExtensions( const vk::PhysicalDevice& physical_device) const; - std::optional GetRequiredDeviceFeatures( + std::optional GetEnabledDeviceFeatures( const vk::PhysicalDevice& physical_device) const; - [[nodiscard]] bool SetDevice(const vk::PhysicalDevice& physical_device); + [[nodiscard]] bool SetPhysicalDevice( + const vk::PhysicalDevice& physical_device); const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const; @@ -90,6 +99,7 @@ class CapabilitiesVK final : public Capabilities, private: const bool enable_validations_; std::map> exts_; + std::set optional_device_extensions_; mutable PixelFormat color_format_ = PixelFormat::kUnknown; PixelFormat depth_stencil_format_ = PixelFormat::kUnknown; vk::PhysicalDeviceProperties device_properties_; diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 74ba94804a0f4..6f6978147fc06 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -42,7 +42,7 @@ static std::optional PickPhysicalDevice( const CapabilitiesVK& caps, const vk::Instance& instance) { for (const auto& device : instance.enumeratePhysicalDevices().value) { - if (caps.GetRequiredDeviceFeatures(device).has_value()) { + if (caps.GetEnabledDeviceFeatures(device).has_value()) { return device; } } @@ -140,8 +140,8 @@ void ContextVK::Setup(Settings settings) { gHasValidationLayers = caps->AreValidationsEnabled(); - auto enabled_layers = caps->GetRequiredLayers(); - auto enabled_extensions = caps->GetRequiredInstanceExtensions(); + auto enabled_layers = caps->GetEnabledLayers(); + auto enabled_extensions = caps->GetEnabledInstanceExtensions(); if (!enabled_layers.has_value() || !enabled_extensions.has_value()) { VALIDATION_LOG << "Device has insufficient capabilities."; @@ -268,7 +268,7 @@ void ContextVK::Setup(Settings settings) { /// Create the logical device. /// auto enabled_device_extensions = - caps->GetRequiredDeviceExtensions(device_holder->physical_device); + caps->GetEnabledDeviceExtensions(device_holder->physical_device); if (!enabled_device_extensions.has_value()) { // This shouldn't happen since we already did device selection. But doesn't // hurt to check again. @@ -283,9 +283,9 @@ void ContextVK::Setup(Settings settings) { const auto queue_create_infos = GetQueueCreateInfos( {graphics_queue.value(), compute_queue.value(), transfer_queue.value()}); - const auto required_features = - caps->GetRequiredDeviceFeatures(device_holder->physical_device); - if (!required_features.has_value()) { + const auto enabled_features = + caps->GetEnabledDeviceFeatures(device_holder->physical_device); + if (!enabled_features.has_value()) { // This shouldn't happen since the device can't be picked if this was not // true. But doesn't hurt to check. return; @@ -295,7 +295,7 @@ void ContextVK::Setup(Settings settings) { device_info.setQueueCreateInfos(queue_create_infos); device_info.setPEnabledExtensionNames(enabled_device_extensions_c); - device_info.setPEnabledFeatures(&required_features.value()); + device_info.setPEnabledFeatures(&enabled_features.value()); // Device layers are deprecated and ignored. { @@ -308,7 +308,7 @@ void ContextVK::Setup(Settings settings) { device_holder->device = std::move(device_result.value); } - if (!caps->SetDevice(device_holder->physical_device)) { + if (!caps->SetPhysicalDevice(device_holder->physical_device)) { VALIDATION_LOG << "Capabilities could not be updated."; return; } diff --git a/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc b/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc index 8220d1d55c123..c7cd74a45f2c9 100644 --- a/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc @@ -186,4 +186,8 @@ void PipelineCacheVK::PersistCacheToDisk() const { } } +const CapabilitiesVK* PipelineCacheVK::GetCapabilities() const { + return CapabilitiesVK::Cast(caps_.get()); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pipeline_cache_vk.h b/impeller/renderer/backend/vulkan/pipeline_cache_vk.h index 519ad7c6b3fa3..ad26855c1e0e8 100644 --- a/impeller/renderer/backend/vulkan/pipeline_cache_vk.h +++ b/impeller/renderer/backend/vulkan/pipeline_cache_vk.h @@ -31,6 +31,8 @@ class PipelineCacheVK { vk::UniquePipeline CreatePipeline(const vk::ComputePipelineCreateInfo& info); + const CapabilitiesVK* GetCapabilities() const; + void PersistCacheToDisk() const; private: diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 2cf6a0fed89e4..27efb372a1ed8 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -4,11 +4,14 @@ #include "impeller/renderer/backend/vulkan/pipeline_library_vk.h" +#include #include +#include #include "flutter/fml/container.h" #include "flutter/fml/trace_event.h" #include "impeller/base/promise.h" +#include "impeller/base/timing.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" @@ -138,10 +141,105 @@ constexpr vk::FrontFace ToVKFrontFace(WindingOrder order) { FML_UNREACHABLE(); } +static vk::PipelineCreationFeedbackEXT EmptyFeedback() { + vk::PipelineCreationFeedbackEXT feedback; + // If the VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT is not set in flags, an + // implementation must not set any other bits in flags, and the values of all + // other VkPipelineCreationFeedback data members are undefined. + feedback.flags = vk::PipelineCreationFeedbackFlagBits::eValid; + return feedback; +} + +static void ReportPipelineCreationFeedbackToLog( + std::stringstream& stream, + const vk::PipelineCreationFeedbackEXT& feedback) { + const auto pipeline_cache_hit = + feedback.flags & + vk::PipelineCreationFeedbackFlagBits::eApplicationPipelineCacheHit; + const auto base_pipeline_accl = + feedback.flags & + vk::PipelineCreationFeedbackFlagBits::eBasePipelineAcceleration; + auto duration = std::chrono::duration_cast( + std::chrono::nanoseconds{feedback.duration}); + stream << "Time: " << duration.count() << "ms" + << " Cache Hit: " << static_cast(pipeline_cache_hit) + << " Base Accel: " << static_cast(base_pipeline_accl) + << " Thread: " << std::this_thread::get_id(); +} + +static void ReportPipelineCreationFeedbackToLog( + const PipelineDescriptor& desc, + const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) { + std::stringstream stream; + stream << std::fixed << std::showpoint << std::setprecision(2); + stream << std::endl << ">>>>>>" << std::endl; + stream << "Pipeline '" << desc.GetLabel() << "' "; + ReportPipelineCreationFeedbackToLog(stream, + *feedback.pPipelineCreationFeedback); + if (feedback.pipelineStageCreationFeedbackCount != 0) { + stream << std::endl; + } + for (size_t i = 0, count = feedback.pipelineStageCreationFeedbackCount; + i < count; i++) { + stream << "\tStage " << i + 1 << ": "; + ReportPipelineCreationFeedbackToLog( + stream, feedback.pPipelineStageCreationFeedbacks[i]); + if (i != count - 1) { + stream << std::endl; + } + } + stream << std::endl << "<<<<<<" << std::endl; + FML_LOG(ERROR) << stream.str(); +} + +static void ReportPipelineCreationFeedbackToTrace( + const PipelineDescriptor& desc, + const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) { + static int64_t gPipelineCacheHits = 0; + static int64_t gPipelineCacheMisses = 0; + static int64_t gPipelines = 0; + if (feedback.pPipelineCreationFeedback->flags & + vk::PipelineCreationFeedbackFlagBits::eApplicationPipelineCacheHit) { + gPipelineCacheHits++; + } else { + gPipelineCacheMisses++; + } + gPipelines++; + FML_TRACE_COUNTER("impeller", // + "PipelineCacheHits", gPipelineCacheHits, // + "PipelineCacheMisses", gPipelineCacheMisses, // + "TotalPipelines", gPipelines // + ); +} + +static void ReportPipelineCreationFeedback( + const PipelineDescriptor& desc, + const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) { + constexpr bool kReportPipelineCreationFeedbackToLogs = false; + constexpr bool kReportPipelineCreationFeedbackToTraces = true; + if (kReportPipelineCreationFeedbackToLogs) { + ReportPipelineCreationFeedbackToLog(desc, feedback); + } + if (kReportPipelineCreationFeedbackToTraces) { + ReportPipelineCreationFeedbackToTrace(desc, feedback); + } +} + std::unique_ptr PipelineLibraryVK::CreatePipeline( const PipelineDescriptor& desc) { TRACE_EVENT0("flutter", __FUNCTION__); - vk::GraphicsPipelineCreateInfo pipeline_info; + vk::StructureChain + chain; + + const auto& supports_pipeline_creation_feedback = + pso_cache_->GetCapabilities()->HasOptionalDeviceExtension( + OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback); + if (!supports_pipeline_creation_feedback) { + chain.unlink(); + } + + auto& pipeline_info = chain.get(); //---------------------------------------------------------------------------- /// Dynamic States @@ -319,6 +417,17 @@ std::unique_ptr PipelineLibraryVK::CreatePipeline( desc.GetBackStencilAttachmentDescriptor()); pipeline_info.setPDepthStencilState(&depth_stencil_state); + //---------------------------------------------------------------------------- + /// Setup the optional pipeline creation feedback struct so we can understand + /// how Vulkan created the PSO. + /// + auto& feedback = chain.get(); + auto pipeline_feedback = EmptyFeedback(); + std::vector stage_feedbacks( + pipeline_info.stageCount, EmptyFeedback()); + feedback.setPPipelineCreationFeedback(&pipeline_feedback); + feedback.setPipelineStageCreationFeedbacks(stage_feedbacks); + //---------------------------------------------------------------------------- /// Finally, all done with the setup info. Create the pipeline itself. /// @@ -328,6 +437,10 @@ std::unique_ptr PipelineLibraryVK::CreatePipeline( return nullptr; } + if (supports_pipeline_creation_feedback) { + ReportPipelineCreationFeedback(desc, feedback); + } + ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline_layout.value, "Pipeline Layout " + desc.GetLabel()); ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline,