diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6d5dece0bce16..ac8392aaebc70 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5478,6 +5478,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_vk.cc + ../.. ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.cc + ../../../flutter/LICENSE @@ -8335,6 +8337,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.cc diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 77c915f955d03..4aaa978353759 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -240,64 +240,49 @@ ContentContext::ContentContext( if (context_->GetCapabilities()->SupportsFramebufferFetch()) { framebuffer_blend_color_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColor), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kColor), supports_decal}); framebuffer_blend_colorburn_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColorBurn), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kColorBurn), supports_decal}); framebuffer_blend_colordodge_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColorDodge), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kColorDodge), supports_decal}); framebuffer_blend_darken_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kDarken), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kDarken), supports_decal}); framebuffer_blend_difference_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kDifference), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kDifference), supports_decal}); framebuffer_blend_exclusion_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kExclusion), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kExclusion), supports_decal}); framebuffer_blend_hardlight_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kHardLight), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kHardLight), supports_decal}); framebuffer_blend_hue_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kHue), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kHue), supports_decal}); framebuffer_blend_lighten_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kLighten), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kLighten), supports_decal}); framebuffer_blend_luminosity_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kLuminosity), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kLuminosity), supports_decal}); framebuffer_blend_multiply_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kMultiply), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kMultiply), supports_decal}); framebuffer_blend_overlay_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kOverlay), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kOverlay), supports_decal}); framebuffer_blend_saturation_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kSaturation), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kSaturation), supports_decal}); framebuffer_blend_screen_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kScreen), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kScreen), supports_decal}); framebuffer_blend_softlight_pipelines_.CreateDefault( *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kSoftLight), supports_decal}, - UseSubpassInput::kYes); + {static_cast(BlendSelectValues::kSoftLight), supports_decal}); } blend_color_pipelines_.CreateDefault( diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index ca3a9ccccf20c..370d7b3d1cddf 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -826,15 +826,13 @@ class ContentContext { void CreateDefault(const Context& context, const ContentContextOptions& options, - const std::initializer_list& constants = {}, - UseSubpassInput subpass_input = UseSubpassInput::kNo) { + const std::initializer_list& constants = {}) { auto desc = PipelineT::Builder::MakeDefaultPipelineDescriptor(context, constants); if (!desc.has_value()) { VALIDATION_LOG << "Failed to create default pipeline."; return; } - desc->SetUseSubpassInput(subpass_input); options.ApplyToPipelineDescriptor(*desc); SetDefault(options, std::make_unique(context, desc)); } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 17d83085de404..6841439095d06 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2689,39 +2689,6 @@ TEST_P(EntityTest, DecalSpecializationAppliedToMorphologyFilter) { expected_constants); } -TEST_P(EntityTest, FramebufferFetchPipelinesDeclareUsage) { - auto content_context = - ContentContext(GetContext(), TypographerContextSkia::Make()); - if (!content_context.GetDeviceCapabilities().SupportsFramebufferFetch()) { - GTEST_SKIP() << "Framebuffer fetch not supported."; - } - - ContentContextOptions options; - options.color_attachment_pixel_format = PixelFormat::kR8G8B8A8UNormInt; - auto color_burn = - content_context.GetFramebufferBlendColorBurnPipeline(options); - - EXPECT_TRUE(color_burn->GetDescriptor().UsesSubpassInput()); -} - -TEST_P(EntityTest, PipelineDescriptorEqAndHash) { - auto desc_1 = std::make_shared(); - auto desc_2 = std::make_shared(); - - EXPECT_TRUE(desc_1->IsEqual(*desc_2)); - EXPECT_EQ(desc_1->GetHash(), desc_2->GetHash()); - - desc_1->SetUseSubpassInput(UseSubpassInput::kYes); - - EXPECT_FALSE(desc_1->IsEqual(*desc_2)); - EXPECT_NE(desc_1->GetHash(), desc_2->GetHash()); - - desc_2->SetUseSubpassInput(UseSubpassInput::kYes); - - EXPECT_TRUE(desc_1->IsEqual(*desc_2)); - EXPECT_EQ(desc_1->GetHash(), desc_2->GetHash()); -} - // This doesn't really tell you if the hashes will have frequent // collisions, but since this type is only used to hash a bounded // set of options, we can just compare benchmarks. diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 16081a9ba4470..ee299ee2660eb 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -8,6 +8,9 @@ import("//flutter/testing/testing.gni") impeller_shaders("shader_fixtures") { name = "fixtures" + # 2.3 adds support for framebuffer fetch in Metal. + metal_version = "2.3" + # Not analyzing because they are not performance critical, and mipmap uses # textureLod, which uses an extension that malioc does not support. analyze = false @@ -16,8 +19,9 @@ impeller_shaders("shader_fixtures") { "array.vert", "box_fade.frag", "box_fade.vert", - "colors.vert", "colors.frag", + "colors.vert", + "half.frag", "impeller.frag", "impeller.vert", "inactive_uniforms.frag", @@ -27,12 +31,16 @@ impeller_shaders("shader_fixtures") { "mipmaps.frag", "mipmaps.vert", "sample.comp", + "sepia.frag", + "sepia.vert", + "simple.vert", "stage1.comp", "stage2.comp", - "simple.vert", + "swizzle.frag", "test_texture.frag", "test_texture.vert", - "half.frag", + "texture.frag", + "texture.vert", ] if (impeller_enable_opengles) { diff --git a/impeller/fixtures/sepia.frag b/impeller/fixtures/sepia.frag new file mode 100644 index 0000000000000..1379a5f4f533b --- /dev/null +++ b/impeller/fixtures/sepia.frag @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +out vec4 frag_color; + +layout(input_attachment_index = 0) uniform subpassInputMS subpass_input; + +void main() { + // https://github.com/chinmaygarde/merle/blob/3eecb311ac8862c41f0c53a5d9b360be923142bb/src/texture.cc#L195 + const mat4 sepia_matrix = mat4(0.3588, 0.2990, 0.2392, 0.0000, // + 0.7044, 0.5870, 0.4696, 0.0000, // + 0.1368, 0.1140, 0.0912, 0.0000, // + 0.0000, 0.0000, 0.0000, 1.0000 // + ); + frag_color = sepia_matrix * subpassLoad(subpass_input, 0); +} diff --git a/impeller/fixtures/sepia.vert b/impeller/fixtures/sepia.vert new file mode 100644 index 0000000000000..b8b09eeaae0e8 --- /dev/null +++ b/impeller/fixtures/sepia.vert @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform UniformBuffer { + mat4 mvp; +} +uniform_buffer; + +in vec3 vertex_position; + +void main() { + gl_Position = uniform_buffer.mvp * vec4(vertex_position, 1.0); +} diff --git a/impeller/fixtures/swizzle.frag b/impeller/fixtures/swizzle.frag new file mode 100644 index 0000000000000..63d793e3ead99 --- /dev/null +++ b/impeller/fixtures/swizzle.frag @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +out vec4 frag_color; + +layout(input_attachment_index = 0) uniform subpassInputMS subpass_input; + +void main() { + frag_color = subpassLoad(subpass_input, 0).gbra; +} diff --git a/impeller/fixtures/texture.frag b/impeller/fixtures/texture.frag new file mode 100644 index 0000000000000..0b7091261bc6a --- /dev/null +++ b/impeller/fixtures/texture.frag @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +in vec2 interporlated_texture_coordinates; + +out vec4 frag_color; + +uniform sampler2D texture_contents; + +void main() { + frag_color = texture(texture_contents, interporlated_texture_coordinates); +} diff --git a/impeller/fixtures/texture.vert b/impeller/fixtures/texture.vert new file mode 100644 index 0000000000000..fd65238d83de7 --- /dev/null +++ b/impeller/fixtures/texture.vert @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform UniformBuffer { + mat4 mvp; +} +uniform_buffer; + +in vec3 vertex_position; +in vec2 texture_coordinates; + +out vec2 interpolated_texture_coordinates; + +void main() { + gl_Position = uniform_buffer.mvp * vec4(vertex_position, 1.0); + interpolated_texture_coordinates = texture_coordinates; +} diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 78a356cb49e82..f759ea269b24e 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -75,6 +75,8 @@ impeller_component("vulkan") { "pipeline_vk.h", "queue_vk.cc", "queue_vk.h", + "render_pass_builder_vk.cc", + "render_pass_builder_vk.h", "render_pass_vk.cc", "render_pass_vk.h", "resource_manager_vk.cc", diff --git a/impeller/renderer/backend/vulkan/allocator_vk.cc b/impeller/renderer/backend/vulkan/allocator_vk.cc index 3f5a046c6a39b..2f90cd768dc4e 100644 --- a/impeller/renderer/backend/vulkan/allocator_vk.cc +++ b/impeller/renderer/backend/vulkan/allocator_vk.cc @@ -147,7 +147,6 @@ AllocatorVK::AllocatorVK(std::weak_ptr context, allocator_.reset(allocator); supports_memoryless_textures_ = capabilities.SupportsDeviceTransientTextures(); - supports_framebuffer_fetch_ = capabilities.SupportsFramebufferFetch(); is_valid_ = true; } @@ -167,8 +166,7 @@ static constexpr vk::ImageUsageFlags ToVKImageUsageFlags( PixelFormat format, TextureUsageMask usage, StorageMode mode, - bool supports_memoryless_textures, - bool supports_framebuffer_fetch) { + bool supports_memoryless_textures) { vk::ImageUsageFlags vk_usage; switch (mode) { @@ -188,9 +186,7 @@ static constexpr vk::ImageUsageFlags ToVKImageUsageFlags( } else { vk_usage |= vk::ImageUsageFlagBits::eColorAttachment; } - if (supports_framebuffer_fetch) { - vk_usage |= vk::ImageUsageFlagBits::eInputAttachment; - } + vk_usage |= vk::ImageUsageFlagBits::eInputAttachment; } if (usage & static_cast(TextureUsage::kShaderRead)) { @@ -267,8 +263,7 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { const TextureDescriptor& desc, VmaAllocator allocator, vk::Device device, - bool supports_memoryless_textures, - bool supports_framebuffer_fetch) + bool supports_memoryless_textures) : TextureSourceVK(desc), resource_(std::move(resource_manager)) { FML_DCHECK(desc.format != PixelFormat::kUnknown); vk::ImageCreateInfo image_info; @@ -285,9 +280,9 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { image_info.arrayLayers = ToArrayLayerCount(desc.type); image_info.tiling = vk::ImageTiling::eOptimal; image_info.initialLayout = vk::ImageLayout::eUndefined; - image_info.usage = ToVKImageUsageFlags( - desc.format, desc.usage, desc.storage_mode, - supports_memoryless_textures, supports_framebuffer_fetch); + image_info.usage = + ToVKImageUsageFlags(desc.format, desc.usage, desc.storage_mode, + supports_memoryless_textures); image_info.sharingMode = vk::SharingMode::eExclusive; VmaAllocationCreateInfo alloc_nfo = {}; @@ -433,8 +428,7 @@ std::shared_ptr AllocatorVK::OnCreateTexture( desc, // allocator_.get(), // device_holder->GetDevice(), // - supports_memoryless_textures_, // - supports_framebuffer_fetch_ // + supports_memoryless_textures_ // ); if (!source->IsValid()) { return nullptr; diff --git a/impeller/renderer/backend/vulkan/allocator_vk.h b/impeller/renderer/backend/vulkan/allocator_vk.h index c1489abe7c8a6..caaedfc520966 100644 --- a/impeller/renderer/backend/vulkan/allocator_vk.h +++ b/impeller/renderer/backend/vulkan/allocator_vk.h @@ -34,7 +34,6 @@ class AllocatorVK final : public Allocator { ISize max_texture_size_; bool is_valid_ = false; bool supports_memoryless_textures_ = false; - bool supports_framebuffer_fetch_ = false; // TODO(jonahwilliams): figure out why CI can't create these buffer pools. bool created_buffer_pool_ = true; diff --git a/impeller/renderer/backend/vulkan/barrier_vk.h b/impeller/renderer/backend/vulkan/barrier_vk.h index b4bc2257899b1..bded9c3d5dc5d 100644 --- a/impeller/renderer/backend/vulkan/barrier_vk.h +++ b/impeller/renderer/backend/vulkan/barrier_vk.h @@ -18,6 +18,13 @@ namespace impeller { /// and the Vulkan spec. The docs for the various member of this /// class are based on verbiage in the spec. /// +/// A useful mnemonic for building a mental model of how to add +/// these barriers is to build a sentence like so; "All commands +/// before this barrier may continue till they encounter a in the . And, all commands after +/// this barrier may proceed till in the ." +/// struct BarrierVK { vk::CommandBuffer cmd_buffer = {}; vk::ImageLayout new_layout = vk::ImageLayout::eUndefined; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index 4d83152795af7..08d07918c42ac 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -157,10 +157,6 @@ static const char* GetDeviceExtensionName(OptionalDeviceExtensionVK ext) { switch (ext) { case OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback: return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME; - case OptionalDeviceExtensionVK::kARMRasterizationOrderAttachmentAccess: - return VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME; - case OptionalDeviceExtensionVK::kEXTRasterizationOrderAttachmentAccess: - return VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME; case OptionalDeviceExtensionVK::kLast: return "Unknown"; } @@ -406,18 +402,6 @@ bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { }); } - { - supports_framebuffer_fetch_ = - (optional_device_extensions_.find( - OptionalDeviceExtensionVK:: - kARMRasterizationOrderAttachmentAccess) != - optional_device_extensions_.end() || - optional_device_extensions_.find( - OptionalDeviceExtensionVK:: - kEXTRasterizationOrderAttachmentAccess) != - optional_device_extensions_.end()); - } - return true; } @@ -448,7 +432,7 @@ bool CapabilitiesVK::SupportsTextureToTextureBlits() const { // |Capabilities| bool CapabilitiesVK::SupportsFramebufferFetch() const { - return supports_framebuffer_fetch_; + return true; } // |Capabilities| diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index 4cb7ce8996600..b2dad43260b99 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -22,8 +22,6 @@ 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, - kARMRasterizationOrderAttachmentAccess, - kEXTRasterizationOrderAttachmentAccess, kLast, }; @@ -112,7 +110,6 @@ class CapabilitiesVK final : public Capabilities, vk::PhysicalDeviceProperties device_properties_; bool supports_compute_subgroups_ = false; bool supports_device_transient_textures_ = false; - bool supports_framebuffer_fetch_ = false; bool is_valid_ = false; bool HasExtension(const std::string& ext) const; diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 1a625f357a609..b6cf44dc66bfa 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -566,6 +566,11 @@ std::shared_ptr ContextVK::GetGPUTracer() const { return gpu_tracer_; } +std::shared_ptr ContextVK::GetDescriptorPoolRecycler() + const { + return descriptor_pool_recycler_; +} + std::shared_ptr ContextVK::GetCommandQueue() const { return command_queue_vk_; } diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index 07884f1f1ef35..a0e058f2d8eee 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -161,9 +161,7 @@ class ContextVK final : public Context, std::shared_ptr GetCommandPoolRecycler() const; - std::shared_ptr GetDescriptorPoolRecycler() const { - return descriptor_pool_recycler_; - } + std::shared_ptr GetDescriptorPoolRecycler() const; std::shared_ptr GetCommandQueue() const override; diff --git a/impeller/renderer/backend/vulkan/debug_report_vk.cc b/impeller/renderer/backend/vulkan/debug_report_vk.cc index 39068e056e0ec..c18c97087d622 100644 --- a/impeller/renderer/backend/vulkan/debug_report_vk.cc +++ b/impeller/renderer/backend/vulkan/debug_report_vk.cc @@ -103,10 +103,19 @@ DebugReportVK::Result DebugReportVK::OnDebugCallback( vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* data) { + // This is a real issue caused by INPUT_ATTACHMENT_BIT not being a supported + // `VkSurfaceCapabilitiesKHR::supportedUsageFlags` on any platform other than + // Android. This is necessary for all the framebuffer fetch related tests. We + // can get away with suppressing this on macOS but this must be fixed. + if (data->messageIdNumber == 0x2c36905d) { + return Result::kContinue; + } + // Issue in older versions of the SDK. // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/3554 - if (strstr(data->pMessageIdName, "CoreValidation-Shader-OutputNotConsumed") != - nullptr) { + if (data->pMessageIdName != nullptr && + strstr(data->pMessageIdName, "CoreValidation-Shader-OutputNotConsumed") != + nullptr) { return Result::kContinue; } diff --git a/impeller/renderer/backend/vulkan/device_holder.h b/impeller/renderer/backend/vulkan/device_holder.h index bd26e09e2029b..09bd14185c544 100644 --- a/impeller/renderer/backend/vulkan/device_holder.h +++ b/impeller/renderer/backend/vulkan/device_holder.h @@ -12,7 +12,9 @@ namespace impeller { class DeviceHolder { public: virtual ~DeviceHolder() = default; + virtual const vk::Device& GetDevice() const = 0; + virtual const vk::PhysicalDevice& GetPhysicalDevice() const = 0; }; diff --git a/impeller/renderer/backend/vulkan/formats_vk.h b/impeller/renderer/backend/vulkan/formats_vk.h index 9e39a5906f38f..d73705f90f0b2 100644 --- a/impeller/renderer/backend/vulkan/formats_vk.h +++ b/impeller/renderer/backend/vulkan/formats_vk.h @@ -6,7 +6,9 @@ #define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_FORMATS_VK_H_ #include +#include +#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" @@ -393,102 +395,6 @@ constexpr bool PixelFormatIsDepthStencil(PixelFormat format) { return false; } -enum class AttachmentKind { - kColor, - kDepth, - kStencil, - kDepthStencil, -}; - -constexpr AttachmentKind AttachmentKindFromFormat(PixelFormat format) { - switch (format) { - case PixelFormat::kUnknown: - case PixelFormat::kA8UNormInt: - case PixelFormat::kR8UNormInt: - case PixelFormat::kR8G8UNormInt: - case PixelFormat::kR8G8B8A8UNormInt: - case PixelFormat::kR8G8B8A8UNormIntSRGB: - case PixelFormat::kB8G8R8A8UNormInt: - case PixelFormat::kB8G8R8A8UNormIntSRGB: - case PixelFormat::kR32G32B32A32Float: - case PixelFormat::kR16G16B16A16Float: - case PixelFormat::kB10G10R10XR: - case PixelFormat::kB10G10R10XRSRGB: - case PixelFormat::kB10G10R10A10XR: - return AttachmentKind::kColor; - case PixelFormat::kS8UInt: - return AttachmentKind::kStencil; - case PixelFormat::kD24UnormS8Uint: - case PixelFormat::kD32FloatS8UInt: - return AttachmentKind::kDepthStencil; - } - FML_UNREACHABLE(); -} - -constexpr vk::AttachmentDescription CreateAttachmentDescription( - PixelFormat format, - SampleCount sample_count, - LoadAction load_action, - StoreAction store_action, - vk::ImageLayout current_layout, - bool supports_framebuffer_fetch) { - vk::AttachmentDescription vk_attachment; - - vk_attachment.format = ToVKImageFormat(format); - vk_attachment.samples = ToVKSampleCount(sample_count); - - // The Vulkan spec has somewhat complicated rules for when these ops are used - // and ignored. Just set safe defaults. - vk_attachment.loadOp = vk::AttachmentLoadOp::eDontCare; - vk_attachment.storeOp = vk::AttachmentStoreOp::eDontCare; - vk_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare; - vk_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare; - - const auto kind = AttachmentKindFromFormat(format); - - switch (kind) { - case AttachmentKind::kColor: - // If the attachment uses a color format, then loadOp and storeOp are - // used, and stencilLoadOp and stencilStoreOp are ignored. - vk_attachment.loadOp = ToVKAttachmentLoadOp(load_action); - vk_attachment.storeOp = ToVKAttachmentStoreOp(store_action); - break; - case AttachmentKind::kDepth: - case AttachmentKind::kDepthStencil: - // If the format has depth and/or stencil components, loadOp and storeOp - // apply only to the depth data, while stencilLoadOp and stencilStoreOp - // define how the stencil data is handled. - vk_attachment.loadOp = ToVKAttachmentLoadOp(load_action); - vk_attachment.storeOp = ToVKAttachmentStoreOp(store_action); - [[fallthrough]]; - case AttachmentKind::kStencil: - vk_attachment.stencilLoadOp = ToVKAttachmentLoadOp(load_action); - vk_attachment.stencilStoreOp = ToVKAttachmentStoreOp(store_action); - break; - } - - switch (kind) { - case AttachmentKind::kColor: - vk_attachment.initialLayout = current_layout; - if (supports_framebuffer_fetch) { - vk_attachment.finalLayout = vk::ImageLayout::eGeneral; - } else { - vk_attachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal; - } - break; - case AttachmentKind::kDepth: - case AttachmentKind::kStencil: - case AttachmentKind::kDepthStencil: - // Separate depth stencil layouts feature is only available in Vulkan 1.2. - vk_attachment.initialLayout = current_layout; - vk_attachment.finalLayout = - vk::ImageLayout::eDepthStencilAttachmentOptimal; - break; - } - - return vk_attachment; -} - static constexpr vk::AttachmentReference kUnusedAttachmentReference = { VK_ATTACHMENT_UNUSED, vk::ImageLayout::eUndefined}; diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 5149158a6904a..600ef830450d5 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -30,7 +30,6 @@ PipelineLibraryVK::PipelineLibraryVK( fml::UniqueFD cache_directory, std::shared_ptr worker_task_runner) : device_holder_(device_holder), - supports_framebuffer_fetch_(caps->SupportsFramebufferFetch()), pso_cache_(std::make_shared(std::move(caps), device_holder, std::move(cache_directory))), @@ -50,472 +49,6 @@ bool PipelineLibraryVK::IsValid() const { return is_valid_; } -//------------------------------------------------------------------------------ -/// @brief Creates an attachment description that does just enough to -/// ensure render pass compatibility with the pass associated later -/// with the framebuffer. -/// -/// See -/// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility -/// -static vk::AttachmentDescription CreatePlaceholderAttachmentDescription( - PixelFormat format, - SampleCount sample_count) { - // Load store ops are immaterial for pass compatibility. The right ops will be - // picked up when the pass associated with framebuffer. - return CreateAttachmentDescription(format, // - sample_count, // - LoadAction::kDontCare, // - StoreAction::kDontCare, // - vk::ImageLayout::eUndefined, // - false // - ); -} - -//---------------------------------------------------------------------------- -/// Render Pass -/// We are NOT going to use the same render pass with the framebuffer (later) -/// and the graphics pipeline (here). Instead, we are going to ensure that the -/// sub-passes are compatible. To see the compatibility rules, see the Vulkan -/// spec: -/// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility -/// -static vk::UniqueRenderPass CreateCompatRenderPassForPipeline( - const vk::Device& device, - const PipelineDescriptor& desc, - bool supports_framebuffer_fetch) { - std::vector attachments; - - std::vector color_refs; - std::vector subpass_color_ref; - vk::AttachmentReference depth_stencil_ref = kUnusedAttachmentReference; - - color_refs.resize(desc.GetMaxColorAttacmentBindIndex() + 1, - kUnusedAttachmentReference); - - const auto sample_count = desc.GetSampleCount(); - - for (const auto& [bind_point, color] : desc.GetColorAttachmentDescriptors()) { - color_refs[bind_point] = - vk::AttachmentReference{static_cast(attachments.size()), - vk::ImageLayout::eColorAttachmentOptimal}; - attachments.emplace_back( - CreatePlaceholderAttachmentDescription(color.format, sample_count)); - } - subpass_color_ref.push_back(vk::AttachmentReference{ - static_cast(0), vk::ImageLayout::eColorAttachmentOptimal}); - - if (auto depth = desc.GetDepthStencilAttachmentDescriptor(); - depth.has_value()) { - depth_stencil_ref = vk::AttachmentReference{ - static_cast(attachments.size()), - vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreatePlaceholderAttachmentDescription( - desc.GetDepthPixelFormat(), sample_count)); - } - if (desc.HasStencilAttachmentDescriptors()) { - depth_stencil_ref = vk::AttachmentReference{ - static_cast(attachments.size()), - vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreatePlaceholderAttachmentDescription( - desc.GetStencilPixelFormat(), sample_count)); - } - - vk::SubpassDescription subpass_desc; - subpass_desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; - - // If the device supports framebuffer fetch, compatibility pipelines are - // always created with the self reference and rasterization order flag. This - // ensures that all compiled pipelines are compatible with a render pass that - // contains a framebuffer fetch shader (advanced blends). - std::vector subpass_dependencies; - if (supports_framebuffer_fetch) { - subpass_desc.setFlags(vk::SubpassDescriptionFlagBits:: - eRasterizationOrderAttachmentColorAccessARM); - subpass_desc.setInputAttachments(subpass_color_ref); - } - subpass_desc.setColorAttachments(color_refs); - subpass_desc.setPDepthStencilAttachment(&depth_stencil_ref); - - vk::RenderPassCreateInfo render_pass_desc; - render_pass_desc.setAttachments(attachments); - render_pass_desc.setPSubpasses(&subpass_desc); - render_pass_desc.setSubpassCount(1u); - render_pass_desc.setDependencies(subpass_dependencies); - - auto [result, pass] = device.createRenderPassUnique(render_pass_desc); - if (result != vk::Result::eSuccess) { - VALIDATION_LOG << "Failed to create render pass for pipeline '" - << desc.GetLabel() << "'. Error: " << vk::to_string(result); - return {}; - } - - // This pass is not used with the render pass. It is only necessary to tell - // Vulkan the expected render pass layout. The actual pass will be created - // later during render pass setup and will need to be compatible with this - // one. - ContextVK::SetDebugName(device, pass.get(), - "Compat Render Pass: " + desc.GetLabel()); - - return std::move(pass); -} - -constexpr vk::FrontFace ToVKFrontFace(WindingOrder order) { - switch (order) { - case WindingOrder::kClockwise: - return vk::FrontFace::eClockwise; - case WindingOrder::kCounterClockwise: - return vk::FrontFace::eCounterClockwise; - } - 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++; - static constexpr int64_t kImpellerPipelineTraceID = 1988; - FML_TRACE_COUNTER("impeller", // - "PipelineCache", // series name - kImpellerPipelineTraceID, // series ID - "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::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 - /// - vk::PipelineDynamicStateCreateInfo dynamic_create_state_info; - std::vector dynamic_states = { - vk::DynamicState::eViewport, - vk::DynamicState::eScissor, - vk::DynamicState::eStencilReference, - }; - dynamic_create_state_info.setDynamicStates(dynamic_states); - pipeline_info.setPDynamicState(&dynamic_create_state_info); - - //---------------------------------------------------------------------------- - /// Viewport State - /// - vk::PipelineViewportStateCreateInfo viewport_state; - viewport_state.setViewportCount(1u); - viewport_state.setScissorCount(1u); - // The actual viewport and scissor rects are not set here since they are - // dynamic as mentioned above in the dynamic state info. - pipeline_info.setPViewportState(&viewport_state); - - //---------------------------------------------------------------------------- - /// Shader Stages - /// - const auto& constants = desc.GetSpecializationConstants(); - - std::vector> map_entries( - desc.GetStageEntrypoints().size()); - std::vector specialization_infos( - desc.GetStageEntrypoints().size()); - std::vector shader_stages; - - size_t entrypoint_count = 0; - for (const auto& entrypoint : desc.GetStageEntrypoints()) { - auto stage = ToVKShaderStageFlagBits(entrypoint.first); - if (!stage.has_value()) { - VALIDATION_LOG << "Unsupported shader type in pipeline: " - << desc.GetLabel(); - return nullptr; - } - - std::vector& entries = - map_entries[entrypoint_count]; - for (auto i = 0u; i < constants.size(); i++) { - vk::SpecializationMapEntry entry; - entry.offset = (i * sizeof(Scalar)); - entry.size = sizeof(Scalar); - entry.constantID = i; - entries.emplace_back(entry); - } - - vk::SpecializationInfo& specialization_info = - specialization_infos[entrypoint_count]; - specialization_info.setMapEntries(map_entries[entrypoint_count]); - specialization_info.setPData(constants.data()); - specialization_info.setDataSize(sizeof(Scalar) * constants.size()); - - vk::PipelineShaderStageCreateInfo info; - info.setStage(stage.value()); - info.setPName("main"); - info.setModule( - ShaderFunctionVK::Cast(entrypoint.second.get())->GetModule()); - info.setPSpecializationInfo(&specialization_info); - shader_stages.push_back(info); - entrypoint_count++; - } - pipeline_info.setStages(shader_stages); - - //---------------------------------------------------------------------------- - /// Rasterization State - /// - vk::PipelineRasterizationStateCreateInfo rasterization_state; - rasterization_state.setFrontFace(ToVKFrontFace(desc.GetWindingOrder())); - rasterization_state.setCullMode(ToVKCullModeFlags(desc.GetCullMode())); - rasterization_state.setPolygonMode(ToVKPolygonMode(desc.GetPolygonMode())); - rasterization_state.setLineWidth(1.0f); - rasterization_state.setDepthClampEnable(false); - rasterization_state.setRasterizerDiscardEnable(false); - pipeline_info.setPRasterizationState(&rasterization_state); - - //---------------------------------------------------------------------------- - /// Multi-sample State - /// - vk::PipelineMultisampleStateCreateInfo multisample_state; - multisample_state.setRasterizationSamples( - ToVKSampleCountFlagBits(desc.GetSampleCount())); - pipeline_info.setPMultisampleState(&multisample_state); - - //---------------------------------------------------------------------------- - /// Primitive Input Assembly State - vk::PipelineInputAssemblyStateCreateInfo input_assembly; - const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType()); - input_assembly.setTopology(topology); - pipeline_info.setPInputAssemblyState(&input_assembly); - - //---------------------------------------------------------------------------- - /// Color Blend State - std::vector attachment_blend_state; - for (const auto& color_desc : desc.GetColorAttachmentDescriptors()) { - // TODO(csg): The blend states are per color attachment. But it isn't clear - // how the color attachment indices are specified in the pipeline create - // info. But, this should always work for one color attachment. - attachment_blend_state.push_back( - ToVKPipelineColorBlendAttachmentState(color_desc.second)); - } - vk::PipelineColorBlendStateCreateInfo blend_state; - blend_state.setAttachments(attachment_blend_state); - pipeline_info.setPColorBlendState(&blend_state); - - std::shared_ptr strong_device = device_holder_.lock(); - if (!strong_device) { - return nullptr; - } - - auto render_pass = CreateCompatRenderPassForPipeline( - strong_device->GetDevice(), desc, supports_framebuffer_fetch_); - if (render_pass) { - pipeline_info.setBasePipelineHandle(VK_NULL_HANDLE); - pipeline_info.setSubpass(0); - pipeline_info.setRenderPass(render_pass.get()); - } else { - return nullptr; - } - - //---------------------------------------------------------------------------- - /// Vertex Input Setup - /// - std::vector attr_descs; - std::vector buffer_descs; - - const auto& stage_inputs = desc.GetVertexDescriptor()->GetStageInputs(); - const auto& stage_buffer_layouts = - desc.GetVertexDescriptor()->GetStageLayouts(); - for (const ShaderStageIOSlot& stage_in : stage_inputs) { - vk::VertexInputAttributeDescription attr_desc; - attr_desc.setBinding(stage_in.binding); - attr_desc.setLocation(stage_in.location); - attr_desc.setFormat(ToVertexDescriptorFormat(stage_in)); - attr_desc.setOffset(stage_in.offset); - attr_descs.push_back(attr_desc); - } - for (const ShaderStageBufferLayout& layout : stage_buffer_layouts) { - vk::VertexInputBindingDescription binding_description; - binding_description.setBinding(layout.binding); - binding_description.setInputRate(vk::VertexInputRate::eVertex); - binding_description.setStride(layout.stride); - buffer_descs.push_back(binding_description); - } - - vk::PipelineVertexInputStateCreateInfo vertex_input_state; - vertex_input_state.setVertexAttributeDescriptions(attr_descs); - vertex_input_state.setVertexBindingDescriptions(buffer_descs); - - pipeline_info.setPVertexInputState(&vertex_input_state); - - //---------------------------------------------------------------------------- - /// Pipeline Layout a.k.a the descriptor sets and uniforms. - /// - std::vector desc_bindings; - - for (auto layout : desc.GetVertexDescriptor()->GetDescriptorSetLayouts()) { - auto vk_desc_layout = ToVKDescriptorSetLayoutBinding(layout); - desc_bindings.push_back(vk_desc_layout); - } - - vk::DescriptorSetLayoutCreateInfo descs_layout_info; - descs_layout_info.setBindings(desc_bindings); - - auto [descs_result, descs_layout] = - strong_device->GetDevice().createDescriptorSetLayoutUnique( - descs_layout_info); - if (descs_result != vk::Result::eSuccess) { - VALIDATION_LOG << "unable to create uniform descriptors"; - return nullptr; - } - - ContextVK::SetDebugName(strong_device->GetDevice(), descs_layout.get(), - "Descriptor Set Layout " + desc.GetLabel()); - - //---------------------------------------------------------------------------- - /// Create the pipeline layout. - /// - vk::PipelineLayoutCreateInfo pipeline_layout_info; - pipeline_layout_info.setSetLayouts(descs_layout.get()); - auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique( - pipeline_layout_info); - if (pipeline_layout.result != vk::Result::eSuccess) { - VALIDATION_LOG << "Could not create pipeline layout for pipeline " - << desc.GetLabel() << ": " - << vk::to_string(pipeline_layout.result); - return nullptr; - } - pipeline_info.setLayout(pipeline_layout.value.get()); - - //---------------------------------------------------------------------------- - /// Create the depth stencil state. - /// - auto depth_stencil_state = ToVKPipelineDepthStencilStateCreateInfo( - desc.GetDepthStencilAttachmentDescriptor(), - desc.GetFrontStencilAttachmentDescriptor(), - 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. - /// - auto pipeline = pso_cache_->CreatePipeline(pipeline_info); - if (!pipeline) { - VALIDATION_LOG << "Could not create graphics pipeline: " << desc.GetLabel(); - 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, - "Pipeline " + desc.GetLabel()); - - return std::make_unique(device_holder_, - weak_from_this(), // - desc, // - std::move(pipeline), // - std::move(render_pass), // - std::move(pipeline_layout.value), // - std::move(descs_layout) // - ); -} - std::unique_ptr PipelineLibraryVK::CreateComputePipeline( const ComputePipelineDescriptor& desc) { TRACE_EVENT0("flutter", __FUNCTION__); @@ -653,14 +186,11 @@ PipelineFuture PipelineLibraryVK::GetPipeline( return; } - auto pipeline = PipelineLibraryVK::Cast(*thiz).CreatePipeline(descriptor); - if (!pipeline) { - promise->set_value(nullptr); - VALIDATION_LOG << "Could not create pipeline: " << descriptor.GetLabel(); - return; - } - - promise->set_value(std::move(pipeline)); + promise->set_value(PipelineVK::Create( + descriptor, // + PipelineLibraryVK::Cast(*thiz).device_holder_.lock(), // + weak_this // + )); }); return pipeline_future; @@ -741,4 +271,13 @@ void PipelineLibraryVK::PersistPipelineCacheToDisk() { }); } +const std::shared_ptr& PipelineLibraryVK::GetPSOCache() const { + return pso_cache_; +} + +const std::shared_ptr& +PipelineLibraryVK::GetWorkerTaskRunner() const { + return worker_task_runner_; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.h b/impeller/renderer/backend/vulkan/pipeline_library_vk.h index 7866b4909d6a0..d683a9c9863c2 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.h +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.h @@ -32,18 +32,21 @@ class PipelineLibraryVK final void DidAcquireSurfaceFrame(); + const std::shared_ptr& GetPSOCache() const; + + const std::shared_ptr& GetWorkerTaskRunner() const; + private: friend ContextVK; std::weak_ptr device_holder_; - bool supports_framebuffer_fetch_ = false; std::shared_ptr pso_cache_; std::shared_ptr worker_task_runner_; Mutex pipelines_mutex_; PipelineMap pipelines_ IPLR_GUARDED_BY(pipelines_mutex_); Mutex compute_pipelines_mutex_; - ComputePipelineMap compute_pipelines_ - IPLR_GUARDED_BY(compute_pipelines_mutex_); + ComputePipelineMap compute_pipelines_ IPLR_GUARDED_BY( + compute_pipelines_mutex_); std::atomic_size_t frames_acquired_ = 0u; bool is_valid_ = false; @@ -68,8 +71,6 @@ class PipelineLibraryVK final void RemovePipelinesWithEntryPoint( std::shared_ptr function) override; - std::unique_ptr CreatePipeline(const PipelineDescriptor& desc); - std::unique_ptr CreateComputePipeline( const ComputePipelineDescriptor& desc); diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc index c1ca1e3084765..497fa2c3032cc 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -4,8 +4,447 @@ #include "impeller/renderer/backend/vulkan/pipeline_vk.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/timing.h" +#include "impeller/renderer/backend/vulkan/capabilities_vk.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h" +#include "impeller/renderer/backend/vulkan/shader_function_vk.h" +#include "impeller/renderer/backend/vulkan/vertex_descriptor_vk.h" + namespace impeller { +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; +} + +constexpr vk::FrontFace ToVKFrontFace(WindingOrder order) { + switch (order) { + case WindingOrder::kClockwise: + return vk::FrontFace::eClockwise; + case WindingOrder::kCounterClockwise: + return vk::FrontFace::eCounterClockwise; + } + FML_UNREACHABLE(); +} + +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++; + static constexpr int64_t kImpellerPipelineTraceID = 1988; + FML_TRACE_COUNTER("impeller", // + "PipelineCache", // series name + kImpellerPipelineTraceID, // series ID + "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); + } +} + +//---------------------------------------------------------------------------- +/// Render Pass +/// We are NOT going to use the same render pass with the framebuffer (later) +/// and the graphics pipeline (here). Instead, we are going to ensure that the +/// sub-passes are compatible. To see the compatibility rules, see the Vulkan +/// spec: +/// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility +/// +static vk::UniqueRenderPass CreateCompatRenderPassForPipeline( + const vk::Device& device, + const PipelineDescriptor& desc) { + RenderPassBuilderVK builder; + + for (const auto& [bind_point, color] : desc.GetColorAttachmentDescriptors()) { + builder.SetColorAttachment(bind_point, // + color.format, // + desc.GetSampleCount(), // + LoadAction::kDontCare, // + StoreAction::kDontCare // + ); + } + + if (auto depth = desc.GetDepthStencilAttachmentDescriptor(); + depth.has_value()) { + builder.SetDepthStencilAttachment(desc.GetDepthPixelFormat(), // + desc.GetSampleCount(), // + LoadAction::kDontCare, // + StoreAction::kDontCare // + ); + } + + if (desc.HasStencilAttachmentDescriptors()) { + builder.SetStencilAttachment(desc.GetStencilPixelFormat(), // + desc.GetSampleCount(), // + LoadAction::kDontCare, // + StoreAction::kDontCare // + ); + } + + auto pass = builder.Build(device); + if (!pass) { + VALIDATION_LOG << "Failed to create render pass for pipeline: " + << desc.GetLabel(); + return {}; + } + + ContextVK::SetDebugName(device, pass.get(), + "Compat Render Pass: " + desc.GetLabel()); + + return pass; +} + +std::unique_ptr PipelineVK::Create( + const PipelineDescriptor& desc, + const std::shared_ptr& device_holder, + const std::weak_ptr& weak_library) { + TRACE_EVENT0("flutter", "PipelineVK::Create"); + + auto library = weak_library.lock(); + + if (!device_holder || !library) { + return nullptr; + } + + const auto& pso_cache = PipelineLibraryVK::Cast(*library).GetPSOCache(); + + vk::StructureChain + chain; + + const auto* caps = pso_cache->GetCapabilities(); + + const auto supports_pipeline_creation_feedback = + caps->HasOptionalDeviceExtension( + OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback); + if (!supports_pipeline_creation_feedback) { + chain.unlink(); + } + + auto& pipeline_info = chain.get(); + + //---------------------------------------------------------------------------- + /// Dynamic States + /// + vk::PipelineDynamicStateCreateInfo dynamic_create_state_info; + std::vector dynamic_states = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + vk::DynamicState::eStencilReference, + }; + dynamic_create_state_info.setDynamicStates(dynamic_states); + pipeline_info.setPDynamicState(&dynamic_create_state_info); + + //---------------------------------------------------------------------------- + /// Viewport State + /// + vk::PipelineViewportStateCreateInfo viewport_state; + viewport_state.setViewportCount(1u); + viewport_state.setScissorCount(1u); + // The actual viewport and scissor rects are not set here since they are + // dynamic as mentioned above in the dynamic state info. + pipeline_info.setPViewportState(&viewport_state); + + //---------------------------------------------------------------------------- + /// Shader Stages + /// + const auto& constants = desc.GetSpecializationConstants(); + + std::vector> map_entries( + desc.GetStageEntrypoints().size()); + std::vector specialization_infos( + desc.GetStageEntrypoints().size()); + std::vector shader_stages; + + size_t entrypoint_count = 0; + for (const auto& entrypoint : desc.GetStageEntrypoints()) { + auto stage = ToVKShaderStageFlagBits(entrypoint.first); + if (!stage.has_value()) { + VALIDATION_LOG << "Unsupported shader type in pipeline: " + << desc.GetLabel(); + return nullptr; + } + + std::vector& entries = + map_entries[entrypoint_count]; + for (auto i = 0u; i < constants.size(); i++) { + vk::SpecializationMapEntry entry; + entry.offset = (i * sizeof(Scalar)); + entry.size = sizeof(Scalar); + entry.constantID = i; + entries.emplace_back(entry); + } + + vk::SpecializationInfo& specialization_info = + specialization_infos[entrypoint_count]; + specialization_info.setMapEntries(map_entries[entrypoint_count]); + specialization_info.setPData(constants.data()); + specialization_info.setDataSize(sizeof(Scalar) * constants.size()); + + vk::PipelineShaderStageCreateInfo info; + info.setStage(stage.value()); + info.setPName("main"); + info.setModule( + ShaderFunctionVK::Cast(entrypoint.second.get())->GetModule()); + info.setPSpecializationInfo(&specialization_info); + shader_stages.push_back(info); + entrypoint_count++; + } + pipeline_info.setStages(shader_stages); + + //---------------------------------------------------------------------------- + /// Rasterization State + /// + vk::PipelineRasterizationStateCreateInfo rasterization_state; + rasterization_state.setFrontFace(ToVKFrontFace(desc.GetWindingOrder())); + rasterization_state.setCullMode(ToVKCullModeFlags(desc.GetCullMode())); + rasterization_state.setPolygonMode(ToVKPolygonMode(desc.GetPolygonMode())); + rasterization_state.setLineWidth(1.0f); + rasterization_state.setDepthClampEnable(false); + rasterization_state.setRasterizerDiscardEnable(false); + pipeline_info.setPRasterizationState(&rasterization_state); + + //---------------------------------------------------------------------------- + /// Multi-sample State + /// + vk::PipelineMultisampleStateCreateInfo multisample_state; + multisample_state.setRasterizationSamples( + ToVKSampleCountFlagBits(desc.GetSampleCount())); + pipeline_info.setPMultisampleState(&multisample_state); + + //---------------------------------------------------------------------------- + /// Primitive Input Assembly State + vk::PipelineInputAssemblyStateCreateInfo input_assembly; + const auto topology = ToVKPrimitiveTopology(desc.GetPrimitiveType()); + input_assembly.setTopology(topology); + pipeline_info.setPInputAssemblyState(&input_assembly); + + //---------------------------------------------------------------------------- + /// Color Blend State + std::vector attachment_blend_state; + for (const auto& color_desc : desc.GetColorAttachmentDescriptors()) { + // TODO(csg): The blend states are per color attachment. But it isn't clear + // how the color attachment indices are specified in the pipeline create + // info. But, this should always work for one color attachment. + attachment_blend_state.push_back( + ToVKPipelineColorBlendAttachmentState(color_desc.second)); + } + vk::PipelineColorBlendStateCreateInfo blend_state; + blend_state.setAttachments(attachment_blend_state); + pipeline_info.setPColorBlendState(&blend_state); + + auto render_pass = + CreateCompatRenderPassForPipeline(device_holder->GetDevice(), // + desc // + ); + + if (!render_pass) { + VALIDATION_LOG << "Could not create render pass for pipeline."; + return nullptr; + } + + // Convention wisdom says that the base acceleration pipelines are never used + // by drivers for cache hits. Instead, the PSO cache is the preferred + // mechanism. + pipeline_info.setBasePipelineHandle(VK_NULL_HANDLE); + pipeline_info.setSubpass(0u); + pipeline_info.setRenderPass(render_pass.get()); + + //---------------------------------------------------------------------------- + /// Vertex Input Setup + /// + std::vector attr_descs; + std::vector buffer_descs; + + const auto& stage_inputs = desc.GetVertexDescriptor()->GetStageInputs(); + const auto& stage_buffer_layouts = + desc.GetVertexDescriptor()->GetStageLayouts(); + for (const ShaderStageIOSlot& stage_in : stage_inputs) { + vk::VertexInputAttributeDescription attr_desc; + attr_desc.setBinding(stage_in.binding); + attr_desc.setLocation(stage_in.location); + attr_desc.setFormat(ToVertexDescriptorFormat(stage_in)); + attr_desc.setOffset(stage_in.offset); + attr_descs.push_back(attr_desc); + } + for (const ShaderStageBufferLayout& layout : stage_buffer_layouts) { + vk::VertexInputBindingDescription binding_description; + binding_description.setBinding(layout.binding); + binding_description.setInputRate(vk::VertexInputRate::eVertex); + binding_description.setStride(layout.stride); + buffer_descs.push_back(binding_description); + } + + vk::PipelineVertexInputStateCreateInfo vertex_input_state; + vertex_input_state.setVertexAttributeDescriptions(attr_descs); + vertex_input_state.setVertexBindingDescriptions(buffer_descs); + + pipeline_info.setPVertexInputState(&vertex_input_state); + + //---------------------------------------------------------------------------- + /// Pipeline Layout a.k.a the descriptor sets and uniforms. + /// + std::vector desc_bindings; + + for (auto layout : desc.GetVertexDescriptor()->GetDescriptorSetLayouts()) { + auto vk_desc_layout = ToVKDescriptorSetLayoutBinding(layout); + desc_bindings.push_back(vk_desc_layout); + } + + vk::DescriptorSetLayoutCreateInfo descs_layout_info; + descs_layout_info.setBindings(desc_bindings); + + auto [descs_result, descs_layout] = + device_holder->GetDevice().createDescriptorSetLayoutUnique( + descs_layout_info); + if (descs_result != vk::Result::eSuccess) { + VALIDATION_LOG << "unable to create uniform descriptors"; + return nullptr; + } + + ContextVK::SetDebugName(device_holder->GetDevice(), descs_layout.get(), + "Descriptor Set Layout " + desc.GetLabel()); + + //---------------------------------------------------------------------------- + /// Create the pipeline layout. + /// + vk::PipelineLayoutCreateInfo pipeline_layout_info; + pipeline_layout_info.setSetLayouts(descs_layout.get()); + auto pipeline_layout = device_holder->GetDevice().createPipelineLayoutUnique( + pipeline_layout_info); + if (pipeline_layout.result != vk::Result::eSuccess) { + VALIDATION_LOG << "Could not create pipeline layout for pipeline " + << desc.GetLabel() << ": " + << vk::to_string(pipeline_layout.result); + return nullptr; + } + pipeline_info.setLayout(pipeline_layout.value.get()); + + //---------------------------------------------------------------------------- + /// Create the depth stencil state. + /// + auto depth_stencil_state = ToVKPipelineDepthStencilStateCreateInfo( + desc.GetDepthStencilAttachmentDescriptor(), + desc.GetFrontStencilAttachmentDescriptor(), + 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. + /// + auto pipeline = pso_cache->CreatePipeline(pipeline_info); + if (!pipeline) { + VALIDATION_LOG << "Could not create graphics pipeline: " << desc.GetLabel(); + return nullptr; + } + + if (supports_pipeline_creation_feedback) { + ReportPipelineCreationFeedback(desc, feedback); + } + + ContextVK::SetDebugName(device_holder->GetDevice(), *pipeline_layout.value, + "Pipeline Layout " + desc.GetLabel()); + ContextVK::SetDebugName(device_holder->GetDevice(), *pipeline, + "Pipeline " + desc.GetLabel()); + + auto pipeline_vk = std::unique_ptr(new PipelineVK( + device_holder, // + library, // + desc, // + std::move(pipeline), // + std::move(render_pass), // + std::move(pipeline_layout.value), // + std::move(descs_layout) // + )); + if (!pipeline_vk->IsValid()) { + VALIDATION_LOG << "Could not create a valid pipeline."; + return nullptr; + } + return pipeline_vk; +} + PipelineVK::PipelineVK(std::weak_ptr device_holder, std::weak_ptr library, const PipelineDescriptor& desc, @@ -23,13 +462,7 @@ PipelineVK::PipelineVK(std::weak_ptr device_holder, } PipelineVK::~PipelineVK() { - std::shared_ptr device_holder = device_holder_.lock(); - if (device_holder) { - descriptor_set_layout_.reset(); - layout_.reset(); - render_pass_.reset(); - pipeline_.reset(); - } else { + if (auto device = device_holder_.lock(); !device) { descriptor_set_layout_.release(); layout_.release(); render_pass_.release(); @@ -41,14 +474,10 @@ bool PipelineVK::IsValid() const { return is_valid_; } -const vk::Pipeline& PipelineVK::GetPipeline() const { +vk::Pipeline PipelineVK::GetPipeline() const { return *pipeline_; } -const vk::RenderPass& PipelineVK::GetRenderPass() const { - return *render_pass_; -} - const vk::PipelineLayout& PipelineVK::GetPipelineLayout() const { return *layout_; } diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.h b/impeller/renderer/backend/vulkan/pipeline_vk.h index c27d18643347f..dfe75cfdafcc1 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.h +++ b/impeller/renderer/backend/vulkan/pipeline_vk.h @@ -5,10 +5,14 @@ #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_VK_H_ #define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_VK_H_ +#include #include #include "impeller/base/backend_cast.h" +#include "impeller/base/thread.h" #include "impeller/renderer/backend/vulkan/device_holder.h" +#include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/pipeline_cache_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/pipeline.h" @@ -22,20 +26,15 @@ class PipelineVK final : public Pipeline, public BackendCast> { public: - PipelineVK(std::weak_ptr device_holder, - std::weak_ptr library, - const PipelineDescriptor& desc, - vk::UniquePipeline pipeline, - vk::UniqueRenderPass render_pass, - vk::UniquePipelineLayout layout, - vk::UniqueDescriptorSetLayout descriptor_set_layout); + static std::unique_ptr Create( + const PipelineDescriptor& desc, + const std::shared_ptr& device_holder, + const std::weak_ptr& weak_library); // |Pipeline| ~PipelineVK() override; - const vk::Pipeline& GetPipeline() const; - - const vk::RenderPass& GetRenderPass() const; + vk::Pipeline GetPipeline() const; const vk::PipelineLayout& GetPipelineLayout() const; @@ -49,8 +48,17 @@ class PipelineVK final vk::UniqueRenderPass render_pass_; vk::UniquePipelineLayout layout_; vk::UniqueDescriptorSetLayout descriptor_set_layout_; + bool is_valid_ = false; + PipelineVK(std::weak_ptr device_holder, + std::weak_ptr library, + const PipelineDescriptor& desc, + vk::UniquePipeline pipeline, + vk::UniqueRenderPass render_pass, + vk::UniquePipelineLayout layout, + vk::UniqueDescriptorSetLayout descriptor_set_layout); + // |Pipeline| bool IsValid() const override; diff --git a/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc b/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc new file mode 100644 index 0000000000000..5edd04441553f --- /dev/null +++ b/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc @@ -0,0 +1,187 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h" + +#include +#include + +#include "impeller/renderer/backend/vulkan/formats_vk.h" + +namespace impeller { + +constexpr auto kSelfDependencySrcStageMask = + vk::PipelineStageFlagBits::eColorAttachmentOutput; +constexpr auto kSelfDependencySrcAccessMask = + vk::AccessFlagBits::eColorAttachmentWrite; + +constexpr auto kSelfDependencyDstStageMask = + vk::PipelineStageFlagBits::eFragmentShader; +constexpr auto kSelfDependencyDstAccessMask = + vk::AccessFlagBits::eInputAttachmentRead; + +constexpr auto kSelfDependencyFlags = vk::DependencyFlagBits::eByRegion; + +RenderPassBuilderVK::RenderPassBuilderVK() = default; + +RenderPassBuilderVK::~RenderPassBuilderVK() = default; + +RenderPassBuilderVK& RenderPassBuilderVK::SetColorAttachment( + size_t index, + PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action) { + vk::AttachmentDescription desc; + desc.format = ToVKImageFormat(format); + desc.samples = ToVKSampleCount(sample_count); + desc.loadOp = ToVKAttachmentLoadOp(load_action); + desc.storeOp = ToVKAttachmentStoreOp(store_action); + desc.stencilLoadOp = vk::AttachmentLoadOp::eDontCare; + desc.stencilStoreOp = vk::AttachmentStoreOp::eDontCare; + desc.initialLayout = vk::ImageLayout::eGeneral; + desc.finalLayout = vk::ImageLayout::eGeneral; + colors_[index] = desc; + + desc.samples = vk::SampleCountFlagBits::e1; + resolves_[index] = desc; + + return *this; +} + +RenderPassBuilderVK& RenderPassBuilderVK::SetDepthStencilAttachment( + PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action) { + vk::AttachmentDescription desc; + desc.format = ToVKImageFormat(format); + desc.samples = ToVKSampleCount(sample_count); + desc.loadOp = ToVKAttachmentLoadOp(load_action); + desc.storeOp = ToVKAttachmentStoreOp(store_action); + desc.stencilLoadOp = desc.loadOp; // Not separable in Impeller. + desc.stencilStoreOp = desc.storeOp; // Not separable in Impeller. + desc.initialLayout = vk::ImageLayout::eGeneral; + desc.finalLayout = vk::ImageLayout::eGeneral; + depth_stencil_ = desc; + return *this; +} + +RenderPassBuilderVK& RenderPassBuilderVK::SetStencilAttachment( + PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action) { + vk::AttachmentDescription desc; + desc.format = ToVKImageFormat(format); + desc.samples = ToVKSampleCount(sample_count); + desc.loadOp = vk::AttachmentLoadOp::eDontCare; + desc.storeOp = vk::AttachmentStoreOp::eDontCare; + desc.stencilLoadOp = ToVKAttachmentLoadOp(load_action); + desc.stencilStoreOp = ToVKAttachmentStoreOp(store_action); + desc.initialLayout = vk::ImageLayout::eGeneral; + desc.finalLayout = vk::ImageLayout::eGeneral; + depth_stencil_ = desc; + return *this; +} + +vk::UniqueRenderPass RenderPassBuilderVK::Build( + const vk::Device& device) const { + FML_DCHECK(colors_.size() == resolves_.size()); + + // This must be less than `VkPhysicalDeviceLimits::maxColorAttachments` but we + // are not checking. + const auto color_attachments_count = + colors_.empty() ? 0u : colors_.rbegin()->first + 1u; + + std::vector attachments; + + std::vector color_refs(color_attachments_count, + kUnusedAttachmentReference); + std::vector resolve_refs(color_attachments_count, + kUnusedAttachmentReference); + vk::AttachmentReference depth_stencil_ref = kUnusedAttachmentReference; + + for (const auto& color : colors_) { + vk::AttachmentReference color_ref; + color_ref.attachment = attachments.size(); + color_ref.layout = vk::ImageLayout::eGeneral; + color_refs[color.first] = color_ref; + attachments.push_back(color.second); + + if (color.second.samples != vk::SampleCountFlagBits::e1) { + vk::AttachmentReference resolve_ref; + resolve_ref.attachment = attachments.size(); + resolve_ref.layout = vk::ImageLayout::eGeneral; + resolve_refs[color.first] = resolve_ref; + attachments.push_back(resolves_.at(color.first)); + } + } + + if (depth_stencil_.has_value()) { + depth_stencil_ref.attachment = attachments.size(); + depth_stencil_ref.layout = vk::ImageLayout::eGeneral; + attachments.push_back(depth_stencil_.value()); + } + + vk::SubpassDescription subpass0; + subpass0.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; + subpass0.setInputAttachments(color_refs); + subpass0.setColorAttachments(color_refs); + subpass0.setResolveAttachments(resolve_refs); + subpass0.setPDepthStencilAttachment(&depth_stencil_ref); + + vk::SubpassDependency self_dep; + self_dep.srcSubpass = 0u; // first subpass + self_dep.dstSubpass = 0u; // to itself + self_dep.srcStageMask = kSelfDependencySrcStageMask; + self_dep.srcAccessMask = kSelfDependencySrcAccessMask; + self_dep.dstStageMask = kSelfDependencyDstStageMask; + self_dep.dstAccessMask = kSelfDependencyDstAccessMask; + self_dep.dependencyFlags = kSelfDependencyFlags; + + vk::RenderPassCreateInfo render_pass_desc; + render_pass_desc.setAttachments(attachments); + render_pass_desc.setSubpasses(subpass0); + render_pass_desc.setDependencies(self_dep); + + auto [result, pass] = device.createRenderPassUnique(render_pass_desc); + if (result != vk::Result::eSuccess) { + VALIDATION_LOG << "Failed to create render pass: " << vk::to_string(result); + return {}; + } + return std::move(pass); +} + +void InsertBarrierForInputAttachmentRead(const vk::CommandBuffer& buffer, + const vk::Image& image) { + // This barrier must be a subset of the masks specified in the subpass + // dependency setup. + vk::ImageMemoryBarrier barrier; + barrier.srcAccessMask = kSelfDependencySrcAccessMask; + barrier.dstAccessMask = kSelfDependencyDstAccessMask; + barrier.oldLayout = vk::ImageLayout::eGeneral; + barrier.newLayout = vk::ImageLayout::eGeneral; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + + vk::ImageSubresourceRange image_levels; + image_levels.aspectMask = vk::ImageAspectFlagBits::eColor; + image_levels.baseArrayLayer = 0u; + image_levels.baseMipLevel = 0u; + image_levels.layerCount = VK_REMAINING_ARRAY_LAYERS; + image_levels.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange = image_levels; + + buffer.pipelineBarrier(kSelfDependencySrcStageMask, // + kSelfDependencyDstStageMask, // + kSelfDependencyFlags, // + {}, // + {}, // + barrier // + ); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/render_pass_builder_vk.h b/impeller/renderer/backend/vulkan/render_pass_builder_vk.h new file mode 100644 index 0000000000000..4ed9f1c9c5a39 --- /dev/null +++ b/impeller/renderer/backend/vulkan/render_pass_builder_vk.h @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RENDER_PASS_BUILDER_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RENDER_PASS_BUILDER_VK_H_ + +#include +#include + +#include "impeller/core/formats.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" + +namespace impeller { + +class RenderPassBuilderVK { + public: + RenderPassBuilderVK(); + + ~RenderPassBuilderVK(); + + RenderPassBuilderVK(const RenderPassBuilderVK&) = delete; + + RenderPassBuilderVK& operator=(const RenderPassBuilderVK&) = delete; + + RenderPassBuilderVK& SetColorAttachment(size_t index, + PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action); + + RenderPassBuilderVK& SetDepthStencilAttachment(PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action); + + RenderPassBuilderVK& SetStencilAttachment(PixelFormat format, + SampleCount sample_count, + LoadAction load_action, + StoreAction store_action); + + vk::UniqueRenderPass Build(const vk::Device& device) const; + + private: + std::map colors_; + std::map resolves_; + std::optional depth_stencil_; +}; + +//------------------------------------------------------------------------------ +/// @brief Inserts the appropriate barriers to ensure that subsequent +/// commands can read from the specified image (itself a framebuffer +/// attachment) as an input attachment. +/// +/// Unlike most barriers, this barrier may only be inserted within a +/// Vulkan render-pass. +/// +/// The type of barrier inserted depends on the subpass setup and +/// self-dependencies. Only use this utility method for inserting +/// barriers in render passes created by `RenderPassBuilderVK`. +/// +/// @param[in] buffer The buffer +/// @param[in] image The image +/// +void InsertBarrierForInputAttachmentRead(const vk::CommandBuffer& buffer, + const vk::Image& image); + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RENDER_PASS_BUILDER_VK_H_ diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 8e45b70d7bd01..a8ce872f6db47 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -20,13 +20,11 @@ #include "impeller/renderer/backend/vulkan/device_buffer_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/pipeline_vk.h" +#include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h" #include "impeller/renderer/backend/vulkan/sampler_vk.h" #include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" - -#include "vulkan/vulkan_enums.hpp" -#include "vulkan/vulkan_handles.hpp" -#include "vulkan/vulkan_to_string.hpp" +#include "impeller/renderer/backend/vulkan/vk.h" namespace impeller { @@ -35,7 +33,7 @@ namespace impeller { // manually changed. // // See: impeller/entity/shaders/blending/framebuffer_blend.frag -static constexpr size_t kMagicSubpassInputBinding = 64; +static constexpr size_t kMagicSubpassInputBinding = 64u; static vk::ClearColorValue VKClearValueFromColor(Color color) { vk::ClearColorValue value; @@ -79,176 +77,65 @@ static std::vector GetVKClearValues( return clears; } -static vk::AttachmentDescription CreateAttachmentDescription( - const Attachment& attachment, - const std::shared_ptr Attachment::*texture_ptr, - bool supports_framebuffer_fetch) { - const auto& texture = attachment.*texture_ptr; - if (!texture) { - return {}; - } - const auto& texture_vk = TextureVK::Cast(*texture); - const auto& desc = texture->GetTextureDescriptor(); - auto current_layout = texture_vk.GetLayout(); - - auto load_action = attachment.load_action; - auto store_action = attachment.store_action; - - if (current_layout == vk::ImageLayout::eUndefined) { - load_action = LoadAction::kClear; - } - - if (desc.storage_mode == StorageMode::kDeviceTransient) { - store_action = StoreAction::kDontCare; - } else if (texture_ptr == &Attachment::resolve_texture) { - store_action = StoreAction::kStore; - } - - // Always insert a barrier to transition to color attachment optimal. - if (current_layout != vk::ImageLayout::ePresentSrcKHR) { - // Note: This should incur a barrier. - current_layout = vk::ImageLayout::eGeneral; - } - - return CreateAttachmentDescription(desc.format, // - desc.sample_count, // - load_action, // - store_action, // - current_layout, - supports_framebuffer_fetch // - ); -} - -static void SetTextureLayout( - const Attachment& attachment, - const vk::AttachmentDescription& attachment_desc, - const std::shared_ptr& command_buffer, - const std::shared_ptr Attachment::*texture_ptr) { - const auto& texture = attachment.*texture_ptr; - if (!texture) { - return; - } - const auto& texture_vk = TextureVK::Cast(*texture); - - if (attachment_desc.initialLayout == vk::ImageLayout::eGeneral) { - BarrierVK barrier; - barrier.new_layout = vk::ImageLayout::eGeneral; - barrier.cmd_buffer = command_buffer->GetEncoder()->GetCommandBuffer(); - barrier.src_access = vk::AccessFlagBits::eShaderRead; - barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader; - barrier.dst_access = vk::AccessFlagBits::eColorAttachmentWrite | - vk::AccessFlagBits::eTransferWrite; - barrier.dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput | - vk::PipelineStageFlagBits::eTransfer; - - texture_vk.SetLayout(barrier); - } - - // Instead of transitioning layouts manually using barriers, we are going to - // make the subpass perform our transitions. - texture_vk.SetLayoutWithoutEncoding(attachment_desc.finalLayout); -} - SharedHandleVK RenderPassVK::CreateVKRenderPass( const ContextVK& context, - const std::shared_ptr& command_buffer, - bool supports_framebuffer_fetch) const { - std::vector attachments; - - std::vector color_refs; - std::vector resolve_refs; - vk::AttachmentReference depth_stencil_ref = kUnusedAttachmentReference; - - // Spec says: "Each element of the pColorAttachments array corresponds to an - // output location in the shader, i.e. if the shader declares an output - // variable decorated with a Location value of X, then it uses the attachment - // provided in pColorAttachments[X]. If the attachment member of any element - // of pColorAttachments is VK_ATTACHMENT_UNUSED." - // - // Just initialize all the elements as unused and fill in the valid bind - // points in the loop below. - color_refs.resize(render_target_.GetMaxColorAttacmentBindIndex() + 1u, - kUnusedAttachmentReference); - resolve_refs.resize(render_target_.GetMaxColorAttacmentBindIndex() + 1u, - kUnusedAttachmentReference); + const std::shared_ptr& command_buffer) const { + BarrierVK barrier; + barrier.new_layout = vk::ImageLayout::eGeneral; + barrier.cmd_buffer = command_buffer->GetEncoder()->GetCommandBuffer(); + barrier.src_access = vk::AccessFlagBits::eShaderRead; + barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader; + barrier.dst_access = vk::AccessFlagBits::eColorAttachmentWrite | + vk::AccessFlagBits::eTransferWrite; + barrier.dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput | + vk::PipelineStageFlagBits::eTransfer; + + RenderPassBuilderVK builder; for (const auto& [bind_point, color] : render_target_.GetColorAttachments()) { - color_refs[bind_point] = vk::AttachmentReference{ - static_cast(attachments.size()), - supports_framebuffer_fetch ? vk::ImageLayout::eGeneral - : vk::ImageLayout::eColorAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription( - color, &Attachment::texture, supports_framebuffer_fetch)); - SetTextureLayout(color, attachments.back(), command_buffer, - &Attachment::texture); + builder.SetColorAttachment( + bind_point, // + color.texture->GetTextureDescriptor().format, // + color.texture->GetTextureDescriptor().sample_count, // + color.load_action, // + color.store_action // + ); + TextureVK::Cast(*color.texture).SetLayout(barrier); if (color.resolve_texture) { - resolve_refs[bind_point] = vk::AttachmentReference{ - static_cast(attachments.size()), - supports_framebuffer_fetch - ? vk::ImageLayout::eGeneral - : vk::ImageLayout::eColorAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription( - color, &Attachment::resolve_texture, supports_framebuffer_fetch)); - SetTextureLayout(color, attachments.back(), command_buffer, - &Attachment::resolve_texture); + TextureVK::Cast(*color.resolve_texture).SetLayout(barrier); } } - auto depth = render_target_.GetDepthAttachment(); - if (depth.has_value()) { - depth_stencil_ref = vk::AttachmentReference{ - static_cast(attachments.size()), - vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription( - depth.value(), &Attachment::texture, supports_framebuffer_fetch)); - SetTextureLayout(depth.value(), attachments.back(), command_buffer, - &Attachment::texture); + if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) { + builder.SetDepthStencilAttachment( + depth->texture->GetTextureDescriptor().format, // + depth->texture->GetTextureDescriptor().sample_count, // + depth->load_action, // + depth->store_action // + ); + TextureVK::Cast(*depth->texture).SetLayout(barrier); } if (auto stencil = render_target_.GetStencilAttachment(); stencil.has_value()) { - depth_stencil_ref = vk::AttachmentReference{ - static_cast(attachments.size()), - vk::ImageLayout::eDepthStencilAttachmentOptimal}; - attachments.emplace_back(CreateAttachmentDescription( - stencil.value(), &Attachment::texture, supports_framebuffer_fetch)); - - // If the depth and stencil are stored in the same texture, then we've - // already inserted a memory barrier to transition this texture as part of - // the depth branch above. - if (depth.has_value() && depth->texture != stencil->texture) { - SetTextureLayout(stencil.value(), attachments.back(), command_buffer, - &Attachment::texture); - } - } - - vk::SubpassDescription subpass_desc; - subpass_desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; - subpass_desc.setColorAttachments(color_refs); - subpass_desc.setResolveAttachments(resolve_refs); - subpass_desc.setPDepthStencilAttachment(&depth_stencil_ref); - - std::vector subpass_dependencies; - std::vector subpass_color_ref; - subpass_color_ref.push_back(vk::AttachmentReference{ - static_cast(0), vk::ImageLayout::eColorAttachmentOptimal}); - if (supports_framebuffer_fetch) { - subpass_desc.setFlags(vk::SubpassDescriptionFlagBits:: - eRasterizationOrderAttachmentColorAccessARM); - subpass_desc.setInputAttachments(subpass_color_ref); + builder.SetStencilAttachment( + stencil->texture->GetTextureDescriptor().format, // + stencil->texture->GetTextureDescriptor().sample_count, // + stencil->load_action, // + stencil->store_action // + ); + TextureVK::Cast(*stencil->texture).SetLayout(barrier); } - vk::RenderPassCreateInfo render_pass_desc; - render_pass_desc.setAttachments(attachments); - render_pass_desc.setPSubpasses(&subpass_desc); - render_pass_desc.setSubpassCount(1u); + auto pass = builder.Build(context.GetDevice()); - auto [result, pass] = - context.GetDevice().createRenderPassUnique(render_pass_desc); - if (result != vk::Result::eSuccess) { - VALIDATION_LOG << "Failed to create render pass: " << vk::to_string(result); + if (!pass) { + VALIDATION_LOG << "Failed to create render pass for framebuffer."; return {}; } + + context.SetDebugName(pass.get(), debug_label_.c_str()); + return MakeSharedVK(std::move(pass)); } @@ -269,9 +156,7 @@ RenderPassVK::RenderPassVK(const std::shared_ptr& context, const auto& target_size = render_target_.GetRenderTargetSize(); - render_pass_ = CreateVKRenderPass( - vk_context, command_buffer_, - vk_context.GetCapabilities()->SupportsFramebufferFetch()); + render_pass_ = CreateVKRenderPass(vk_context, command_buffer_); if (!render_pass_) { VALIDATION_LOG << "Could not create renderpass."; is_valid_ = false; @@ -355,7 +240,8 @@ SharedHandleVK RenderPassVK::CreateVKFramebuffer( std::vector attachments; // This bit must be consistent to ensure compatibility with the pass created - // earlier. Follow this order: Color attachments, then depth, then stencil. + // earlier. Follow this order: Color attachments, then depth-stencil, then + // stencil. for (const auto& [_, color] : render_target_.GetColorAttachments()) { // The bind point doesn't matter here since that information is present in // the render pass. @@ -369,9 +255,8 @@ SharedHandleVK RenderPassVK::CreateVKFramebuffer( if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) { attachments.emplace_back( TextureVK::Cast(*depth->texture).GetRenderTargetView()); - } - if (auto stencil = render_target_.GetStencilAttachment(); - stencil.has_value()) { + } else if (auto stencil = render_target_.GetStencilAttachment(); + stencil.has_value()) { attachments.emplace_back( TextureVK::Cast(*stencil->texture).GetRenderTargetView()); } @@ -406,12 +291,14 @@ void RenderPassVK::SetPipeline( command_buffer_vk_.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline_vk.GetPipeline()); - if (pipeline->GetDescriptor().UsesSubpassInput()) { + pipeline_uses_input_attachments_ = + pipeline->GetDescriptor().GetVertexDescriptor()->UsesInputAttacments(); + + if (pipeline_uses_input_attachments_) { if (bound_image_offset_ >= kMaxBindings) { pipeline_valid_ = false; return; } - vk::DescriptorImageInfo image_info; image_info.imageLayout = vk::ImageLayout::eGeneral; image_info.sampler = VK_NULL_HANDLE; @@ -548,6 +435,11 @@ fml::Status RenderPassVK::Draw() { nullptr // offsets ); + if (pipeline_uses_input_attachments_) { + InsertBarrierForInputAttachmentRead( + command_buffer_vk_, TextureVK::Cast(*color_image_vk_).GetImage()); + } + if (has_index_buffer_) { command_buffer_vk_.drawIndexed(vertex_count_, // index count instance_count_, // instance count @@ -577,6 +469,7 @@ fml::Status RenderPassVK::Draw() { base_vertex_ = 0u; vertex_count_ = 0u; pipeline_valid_ = false; + pipeline_uses_input_attachments_ = false; return fml::Status(); } diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index 04739363d31a9..4836cf4c86c81 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -47,7 +47,7 @@ class RenderPassVK final : public RenderPass { bool has_index_buffer_ = false; bool has_label_ = false; bool pipeline_valid_ = false; - vk::Pipeline last_pipeline_; + bool pipeline_uses_input_attachments_ = false; vk::DescriptorSet descriptor_set_ = {}; vk::PipelineLayout pipeline_layout_ = {}; @@ -123,8 +123,7 @@ class RenderPassVK final : public RenderPass { SharedHandleVK CreateVKRenderPass( const ContextVK& context, - const std::shared_ptr& command_buffer, - bool has_subpass_dependency) const; + const std::shared_ptr& command_buffer) const; SharedHandleVK CreateVKFramebuffer( const ContextVK& context, diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc index 860fcf0e225f5..a35881438aa04 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -163,7 +163,7 @@ SwapchainImplVK::SwapchainImplVK( auto& vk_context = ContextVK::Cast(*context); - auto [caps_result, caps] = + const auto [caps_result, surface_caps] = vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface); if (caps_result != vk::Result::eSuccess) { VALIDATION_LOG << "Could not get surface capabilities: " @@ -188,7 +188,7 @@ SwapchainImplVK::SwapchainImplVK( vk_context.SetOffscreenFormat(ToPixelFormat(format.value().format)); const auto composite = - ChooseAlphaCompositionMode(caps.supportedCompositeAlpha); + ChooseAlphaCompositionMode(surface_caps.supportedCompositeAlpha); if (!composite.has_value()) { VALIDATION_LOG << "No composition mode supported."; return; @@ -209,22 +209,26 @@ SwapchainImplVK::SwapchainImplVK( swapchain_info.imageColorSpace = format.value().colorSpace; swapchain_info.presentMode = vk::PresentModeKHR::eFifo; swapchain_info.imageExtent = vk::Extent2D{ - std::clamp(caps.currentExtent.width, caps.minImageExtent.width, - caps.maxImageExtent.width), - std::clamp(caps.currentExtent.height, caps.minImageExtent.height, - caps.maxImageExtent.height), + std::clamp(surface_caps.currentExtent.width, + surface_caps.minImageExtent.width, + surface_caps.maxImageExtent.width), + std::clamp(surface_caps.currentExtent.height, + surface_caps.minImageExtent.height, + surface_caps.maxImageExtent.height), }; - swapchain_info.minImageCount = std::clamp( - caps.minImageCount + 1u, // preferred image count - caps.minImageCount, // min count cannot be zero - caps.maxImageCount == 0u ? caps.minImageCount + 1u - : caps.maxImageCount // max zero means no limit - ); + swapchain_info.minImageCount = + std::clamp(surface_caps.minImageCount + 1u, // preferred image count + surface_caps.minImageCount, // min count cannot be zero + surface_caps.maxImageCount == 0u + ? surface_caps.minImageCount + 1u + : surface_caps.maxImageCount // max zero means no limit + ); swapchain_info.imageArrayLayers = 1u; - // Swapchain images are primarily used as color attachments (via resolve) or - // blit targets. + // Swapchain images are primarily used as color attachments (via resolve), + // blit targets, or input attachments. swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | - vk::ImageUsageFlagBits::eTransferDst; + vk::ImageUsageFlagBits::eTransferDst | + vk::ImageUsageFlagBits::eInputAttachment; swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity; swapchain_info.compositeAlpha = composite.value(); // If we set the clipped value to true, Vulkan expects we will never read back diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 63d0918b78a7b..eda23adebc5ff 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -64,12 +64,13 @@ class Pipeline { std::function descriptor_callback) const; protected: - Pipeline(std::weak_ptr library, T desc); - - private: const std::weak_ptr library_; + const T desc_; + Pipeline(std::weak_ptr library, T desc); + + private: Pipeline(const Pipeline&) = delete; Pipeline& operator=(const Pipeline&) = delete; diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index 8b9dcf8fef7ae..9ae6710426f63 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -45,7 +45,6 @@ std::size_t PipelineDescriptor::GetHash() const { fml::HashCombineSeed(seed, cull_mode_); fml::HashCombineSeed(seed, primitive_type_); fml::HashCombineSeed(seed, polygon_mode_); - fml::HashCombineSeed(seed, use_subpass_input_); return seed; } @@ -66,8 +65,7 @@ bool PipelineDescriptor::IsEqual(const PipelineDescriptor& other) const { cull_mode_ == other.cull_mode_ && primitive_type_ == other.primitive_type_ && polygon_mode_ == other.polygon_mode_ && - specialization_constants_ == other.specialization_constants_ && - use_subpass_input_ == other.use_subpass_input_; + specialization_constants_ == other.specialization_constants_; } PipelineDescriptor& PipelineDescriptor::SetLabel(std::string label) { diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 460d7e1ee8d4c..3ea2ae6080c60 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -21,11 +21,6 @@ class VertexDescriptor; template class Pipeline; -enum class UseSubpassInput { - kYes, - kNo, -}; - class PipelineDescriptor final : public Comparable { public: PipelineDescriptor(); @@ -134,17 +129,6 @@ class PipelineDescriptor final : public Comparable { const std::vector& GetSpecializationConstants() const; - void SetUseSubpassInput(UseSubpassInput value) { use_subpass_input_ = value; } - - bool UsesSubpassInput() const { - switch (use_subpass_input_) { - case UseSubpassInput::kYes: - return true; - case UseSubpassInput::kNo: - return false; - } - } - private: std::string label_; SampleCount sample_count_ = SampleCount::kCount1; @@ -163,7 +147,6 @@ class PipelineDescriptor final : public Comparable { back_stencil_attachment_descriptor_; PrimitiveType primitive_type_ = PrimitiveType::kTriangle; PolygonMode polygon_mode_ = PolygonMode::kFill; - UseSubpassInput use_subpass_input_ = UseSubpassInput::kNo; std::vector specialization_constants_; }; diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index c5336fb7433e3..1e5c38344a662 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -22,6 +22,13 @@ #include "impeller/fixtures/instanced_draw.vert.h" #include "impeller/fixtures/mipmaps.frag.h" #include "impeller/fixtures/mipmaps.vert.h" +#include "impeller/fixtures/sepia.frag.h" +#include "impeller/fixtures/sepia.vert.h" +#include "impeller/fixtures/swizzle.frag.h" +#include "impeller/fixtures/test_texture.frag.h" +#include "impeller/fixtures/test_texture.vert.h" +#include "impeller/fixtures/texture.frag.h" +#include "impeller/fixtures/texture.vert.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground_test.h" #include "impeller/renderer/command.h" @@ -1299,6 +1306,220 @@ TEST_P(RendererTest, GetContext()->GetCapabilities()->GetDefaultDepthStencilFormat()); } +template +std::shared_ptr> CreateDefaultPipeline( + const std::shared_ptr& context) { + using TexturePipelineBuilder = PipelineBuilder; + auto pipeline_desc = + TexturePipelineBuilder::MakeDefaultPipelineDescriptor(*context); + if (!pipeline_desc.has_value()) { + return nullptr; + } + pipeline_desc->SetSampleCount(SampleCount::kCount4); + pipeline_desc->SetStencilAttachmentDescriptors(std::nullopt); + auto pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); + if (!pipeline || !pipeline->IsValid()) { + return nullptr; + } + return pipeline; +} + +TEST_P(RendererTest, CanSepiaToneWithSubpasses) { + // Define shader types + using TextureVS = TextureVertexShader; + using TextureFS = TextureFragmentShader; + + using SepiaVS = SepiaVertexShader; + using SepiaFS = SepiaFragmentShader; + + auto context = GetContext(); + ASSERT_TRUE(context); + + if (!context->GetCapabilities()->SupportsFramebufferFetch()) { + GTEST_SKIP_( + "This test uses framebuffer fetch and the backend doesn't support it."); + return; + } + + // Create pipelines. + auto texture_pipeline = CreateDefaultPipeline(context); + auto sepia_pipeline = CreateDefaultPipeline(context); + + ASSERT_TRUE(texture_pipeline); + ASSERT_TRUE(sepia_pipeline); + + // Vertex buffer builders. + VertexBufferBuilder texture_vtx_builder; + texture_vtx_builder.AddVertices({ + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {0.0, 1.0}}, // 4 + }); + + VertexBufferBuilder sepia_vtx_builder; + sepia_vtx_builder.AddVertices({ + {{100, 100, 0.0}}, // 1 + {{800, 100, 0.0}}, // 2 + {{800, 800, 0.0}}, // 3 + {{100, 100, 0.0}}, // 1 + {{800, 800, 0.0}}, // 3 + {{100, 800, 0.0}}, // 4 + }); + + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(boston); + + const auto& sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + SinglePassCallback callback = [&](RenderPass& pass) { + auto buffer = HostBuffer::Create(context->GetResourceAllocator()); + + // Draw the texture. + { + pass.SetPipeline(texture_pipeline); + pass.SetVertexBuffer(texture_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + TextureVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + TextureVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms)); + TextureFS::BindTextureContents(pass, boston, sampler); + if (!pass.Draw().ok()) { + return false; + } + } + + // Draw the sepia toner. + { + pass.SetPipeline(sepia_pipeline); + pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SepiaVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SepiaVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms)); + if (!pass.Draw().ok()) { + return false; + } + } + + return true; + }; + OpenPlaygroundHere(callback); +} + +TEST_P(RendererTest, CanSepiaToneThenSwizzleWithSubpasses) { + // Define shader types + using TextureVS = TextureVertexShader; + using TextureFS = TextureFragmentShader; + + using SwizzleVS = SepiaVertexShader; + using SwizzleFS = SwizzleFragmentShader; + + using SepiaVS = SepiaVertexShader; + using SepiaFS = SepiaFragmentShader; + + auto context = GetContext(); + ASSERT_TRUE(context); + + if (!context->GetCapabilities()->SupportsFramebufferFetch()) { + GTEST_SKIP_( + "This test uses framebuffer fetch and the backend doesn't support it."); + return; + } + + // Create pipelines. + auto texture_pipeline = CreateDefaultPipeline(context); + auto swizzle_pipeline = CreateDefaultPipeline(context); + auto sepia_pipeline = CreateDefaultPipeline(context); + + ASSERT_TRUE(texture_pipeline); + ASSERT_TRUE(swizzle_pipeline); + ASSERT_TRUE(sepia_pipeline); + + // Vertex buffer builders. + VertexBufferBuilder texture_vtx_builder; + texture_vtx_builder.AddVertices({ + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {0.0, 1.0}}, // 4 + }); + + VertexBufferBuilder sepia_vtx_builder; + sepia_vtx_builder.AddVertices({ + {{100, 100, 0.0}}, // 1 + {{800, 100, 0.0}}, // 2 + {{800, 800, 0.0}}, // 3 + {{100, 100, 0.0}}, // 1 + {{800, 800, 0.0}}, // 3 + {{100, 800, 0.0}}, // 4 + }); + + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(boston); + + const auto& sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + SinglePassCallback callback = [&](RenderPass& pass) { + auto buffer = HostBuffer::Create(context->GetResourceAllocator()); + + // Draw the texture. + { + pass.SetPipeline(texture_pipeline); + pass.SetVertexBuffer(texture_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + TextureVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + TextureVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms)); + TextureFS::BindTextureContents(pass, boston, sampler); + if (!pass.Draw().ok()) { + return false; + } + } + + // Draw the sepia toner. + { + pass.SetPipeline(sepia_pipeline); + pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SepiaVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SepiaVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms)); + if (!pass.Draw().ok()) { + return false; + } + } + + // Draw the swizzle. + { + pass.SetPipeline(swizzle_pipeline); + pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SwizzleVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SwizzleVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms)); + if (!pass.Draw().ok()) { + return false; + } + } + + return true; + }; + OpenPlaygroundHere(callback); +} + } // namespace testing } // namespace impeller diff --git a/impeller/renderer/vertex_descriptor.cc b/impeller/renderer/vertex_descriptor.cc index 1c912308859b4..a8e4ee3422de1 100644 --- a/impeller/renderer/vertex_descriptor.cc +++ b/impeller/renderer/vertex_descriptor.cc @@ -37,6 +37,8 @@ void VertexDescriptor::RegisterDescriptorSetLayouts( size_t count) { desc_set_layouts_.reserve(desc_set_layouts_.size() + count); for (size_t i = 0; i < count; i++) { + uses_input_attachments_ |= + desc_set_layout[i].descriptor_type == DescriptorType::kInputAttachment; desc_set_layouts_.emplace_back(desc_set_layout[i]); } } @@ -72,4 +74,8 @@ VertexDescriptor::GetDescriptorSetLayouts() const { return desc_set_layouts_; } +bool VertexDescriptor::UsesInputAttacments() const { + return uses_input_attachments_; +} + } // namespace impeller diff --git a/impeller/renderer/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h index 58addd4a82eb1..55c487e0cdf6a 100644 --- a/impeller/renderer/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -67,10 +67,13 @@ class VertexDescriptor final : public Comparable { // |Comparable| bool IsEqual(const VertexDescriptor& other) const override; + bool UsesInputAttacments() const; + private: std::vector inputs_; std::vector layouts_; std::vector desc_set_layouts_; + bool uses_input_attachments_ = false; VertexDescriptor(const VertexDescriptor&) = delete;