diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 3bfe85a0fb072..3a69d7db48a8b 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -34,10 +34,6 @@ config("impeller_public_config") { defines += [ "IMPELLER_ENABLE_VULKAN=1" ] } - if (impeller_enable_vulkan_playgrounds) { - defines += [ "IMPELLER_ENABLE_VULKAN_PLAYGROUNDS=1" ] - } - if (impeller_trace_all_gl_calls) { defines += [ "IMPELLER_TRACE_ALL_GL_CALLS" ] } diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 634ae0b8e6fa7..24940aa98fa3d 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -223,64 +223,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 94d4af747750c..883b04f3674eb 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -799,15 +799,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 520d2ee9ee3db..67bce9cc6a1fb 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2577,39 +2577,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 9165f06542a2a..db4dde8fbfee7 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/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index d05004f4eb43a..b42c11011724d 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -175,10 +175,38 @@ void PlaygroundImplVK::InitGlobalVulkanInstance() { application_info.setPEngineName("PlaygroundImplVK"); application_info.setPApplicationName("PlaygroundImplVK"); - auto instance_result = - vk::createInstanceUnique(vk::InstanceCreateInfo({}, &application_info)); + CapabilitiesVK caps(false); + auto enabled_layers = caps.GetEnabledLayers(); + auto enabled_extensions = caps.GetEnabledInstanceExtensions(); + + FML_CHECK(enabled_layers.has_value() && enabled_extensions.has_value()); + + std::vector enabled_layers_c; + std::vector enabled_extensions_c; + + for (const auto& layer : enabled_layers.value()) { + enabled_layers_c.push_back(layer.c_str()); + } + + for (const auto& ext : enabled_extensions.value()) { + enabled_extensions_c.push_back(ext.c_str()); + } + + vk::InstanceCreateInfo instance_info; + instance_info.setPEnabledLayerNames(enabled_layers_c); + instance_info.setPEnabledExtensionNames(enabled_extensions_c); + instance_info.setPApplicationInfo(&application_info); + + if (std::find(enabled_extensions->begin(), enabled_extensions->end(), + "VK_KHR_portability_enumeration") != + enabled_extensions->end()) { + instance_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR; + } + + auto instance_result = vk::createInstanceUnique(instance_info); FML_CHECK(instance_result.result == vk::Result::eSuccess) - << "Unable to initialize global Vulkan instance"; + << "Unable to initialize global Vulkan instance: " + << vk::to_string(instance_result.result); global_instance_ = std::move(instance_result.value); } diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 1c6dbfa694b49..f2a51ba4d7715 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -102,7 +102,7 @@ bool Playground::SupportsBackend(PlaygroundBackend backend) { return false; #endif // IMPELLER_ENABLE_OPENGLES case PlaygroundBackend::kVulkan: -#if IMPELLER_ENABLE_VULKAN && IMPELLER_ENABLE_VULKAN_PLAYGROUNDS +#if IMPELLER_ENABLE_VULKAN return true; #else // IMPELLER_ENABLE_VULKAN return false; diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index be72d061fb56c..2567b243b8d57 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -78,6 +78,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 f6e2bf67e4858..bc7b8ff36dfd1 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 = {}; @@ -415,8 +410,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 51be4d8eb9521..58a57dca1e003 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; uint32_t frame_count_ = 0; 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/binding_helpers_vk.cc b/impeller/renderer/backend/vulkan/binding_helpers_vk.cc index 19fe5350d602b..57c1d973ce0a9 100644 --- a/impeller/renderer/backend/vulkan/binding_helpers_vk.cc +++ b/impeller/renderer/backend/vulkan/binding_helpers_vk.cc @@ -18,13 +18,40 @@ namespace impeller { -// Warning: if any of the constant values or layouts are changed in the -// framebuffer fetch shader, then this input binding may need to be -// manually changed. -static constexpr size_t kMagicSubpassInputBinding = 64; +static bool BindInputAttachments( + const PipelineDescriptor& desc, + vk::DescriptorSet& vk_desc_set, + const std::shared_ptr& color_attachment0, + std::vector& images, + std::vector& writes) { + if (!desc.GetVertexDescriptor()->UsesInputAttacments()) { + return true; + } + + // This routine is a bit of a lark at the moment since there is an assumption + // that there can only be one input attachment at a pre-determined binding. No + // matter the actual index specification in the shader. + + vk::DescriptorImageInfo image_info; + image_info.imageLayout = vk::ImageLayout::eGeneral; + // Loads from input attachments in a subpass are direct pixel loads of values + // from previous subpasses. As such, there can be no sampling. + image_info.sampler = VK_NULL_HANDLE; + image_info.imageView = TextureVK::Cast(*color_attachment0).GetImageView(); + images.push_back(image_info); + + vk::WriteDescriptorSet write_set; + write_set.dstSet = vk_desc_set; + write_set.dstBinding = 64u; + write_set.descriptorCount = 1u; + write_set.descriptorType = vk::DescriptorType::eInputAttachment; + write_set.pImageInfo = &images.back(); + writes.push_back(write_set); + + return true; +} static bool BindImages(const Bindings& bindings, - Allocator& allocator, const std::shared_ptr& encoder, vk::DescriptorSet& vk_desc_set, std::vector& images, @@ -53,7 +80,6 @@ static bool BindImages(const Bindings& bindings, write_set.descriptorCount = 1u; write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler; write_set.pImageInfo = &images.back(); - writes.push_back(write_set); } @@ -124,7 +150,7 @@ fml::StatusOr> AllocateAndBindDescriptorSets( const ContextVK& context, const std::shared_ptr& encoder, const std::vector& commands, - const TextureVK& input_attachment) { + const std::shared_ptr& color_attachment0) { if (commands.empty()) { return std::vector{}; } @@ -132,9 +158,10 @@ fml::StatusOr> AllocateAndBindDescriptorSets( // Step 1: Determine the total number of buffer and sampler descriptor // sets required. Collect this information along with the layout information // to allocate a correctly sized descriptor pool. - size_t buffer_count = 0; - size_t samplers_count = 0; - size_t subpass_count = 0; + size_t buffer_count = 0u; + size_t samplers_count = 0u; + size_t input_attachements_count = 0u; + std::vector layouts; layouts.reserve(commands.size()); @@ -142,14 +169,17 @@ fml::StatusOr> AllocateAndBindDescriptorSets( buffer_count += command.vertex_bindings.buffers.size(); buffer_count += command.fragment_bindings.buffers.size(); samplers_count += command.fragment_bindings.sampled_images.size(); - subpass_count += - command.pipeline->GetDescriptor().UsesSubpassInput() ? 1 : 0; + input_attachements_count += command.pipeline->GetDescriptor() + .GetVertexDescriptor() + ->UsesInputAttacments() + ? 1u + : 0u; layouts.emplace_back( PipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout()); } auto descriptor_result = encoder->AllocateDescriptorSets( - buffer_count, samplers_count, subpass_count, layouts); + buffer_count, samplers_count, input_attachements_count, layouts); if (!descriptor_result.ok()) { return descriptor_result.status(); } @@ -158,14 +188,14 @@ fml::StatusOr> AllocateAndBindDescriptorSets( return fml::Status(); } - // Step 2: Update the descriptors for all image and buffer descriptors used - // in the render pass. + // Step 2: Update the descriptors for all image, buffer, and input-attachment + // descriptors used in the render pass. std::vector images; std::vector buffers; std::vector writes; - images.reserve(samplers_count + subpass_count); + images.reserve(samplers_count + input_attachements_count); buffers.reserve(buffer_count); - writes.reserve(samplers_count + buffer_count + subpass_count); + writes.reserve(samplers_count + buffer_count + input_attachements_count); auto& allocator = *context.GetResourceAllocator(); auto desc_index = 0u; @@ -177,28 +207,15 @@ fml::StatusOr> AllocateAndBindDescriptorSets( descriptor_sets[desc_index], desc_set, buffers, writes) || !BindBuffers(command.fragment_bindings, allocator, encoder, descriptor_sets[desc_index], desc_set, buffers, writes) || - !BindImages(command.fragment_bindings, allocator, encoder, - descriptor_sets[desc_index], images, writes)) { + !BindImages(command.fragment_bindings, encoder, + descriptor_sets[desc_index], images, writes) || + !BindInputAttachments(command.pipeline->GetDescriptor(), + descriptor_sets[desc_index], color_attachment0, + images, writes)) { return fml::Status(fml::StatusCode::kUnknown, "Failed to bind texture or buffer."); } - if (command.pipeline->GetDescriptor().UsesSubpassInput()) { - vk::DescriptorImageInfo image_info; - image_info.imageLayout = vk::ImageLayout::eGeneral; - image_info.sampler = VK_NULL_HANDLE; - image_info.imageView = input_attachment.GetImageView(); - images.push_back(image_info); - - vk::WriteDescriptorSet write_set; - write_set.dstSet = descriptor_sets[desc_index]; - write_set.dstBinding = kMagicSubpassInputBinding; - write_set.descriptorCount = 1u; - write_set.descriptorType = vk::DescriptorType::eInputAttachment; - write_set.pImageInfo = &images.back(); - - writes.push_back(write_set); - } desc_index += 1; } @@ -228,8 +245,8 @@ fml::StatusOr> AllocateAndBindDescriptorSets( layouts.emplace_back( ComputePipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout()); } - auto descriptor_result = - encoder->AllocateDescriptorSets(buffer_count, samplers_count, 0, layouts); + auto descriptor_result = encoder->AllocateDescriptorSets( + buffer_count, samplers_count, 0u, layouts); if (!descriptor_result.ok()) { return descriptor_result.status(); } @@ -253,8 +270,8 @@ fml::StatusOr> AllocateAndBindDescriptorSets( if (!BindBuffers(command.bindings, allocator, encoder, descriptor_sets[desc_index], desc_set, buffers, writes) || - !BindImages(command.bindings, allocator, encoder, - descriptor_sets[desc_index], images, writes)) { + !BindImages(command.bindings, encoder, descriptor_sets[desc_index], + images, writes)) { return fml::Status(fml::StatusCode::kUnknown, "Failed to bind texture or buffer."); } diff --git a/impeller/renderer/backend/vulkan/binding_helpers_vk.h b/impeller/renderer/backend/vulkan/binding_helpers_vk.h index b232d360cf4d4..6279507128087 100644 --- a/impeller/renderer/backend/vulkan/binding_helpers_vk.h +++ b/impeller/renderer/backend/vulkan/binding_helpers_vk.h @@ -19,7 +19,7 @@ fml::StatusOr> AllocateAndBindDescriptorSets( const ContextVK& context, const std::shared_ptr& encoder, const std::vector& commands, - const TextureVK& input_attachment); + const std::shared_ptr& color_attachment0); fml::StatusOr> AllocateAndBindDescriptorSets( const ContextVK& context, 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/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 31ab804027da9..094a56cf6455d 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -298,14 +298,14 @@ fml::StatusOr> CommandEncoderVK::AllocateDescriptorSets( uint32_t buffer_count, uint32_t sampler_count, - uint32_t subpass_count, + uint32_t input_attachments_count, const std::vector& layouts) { if (!IsValid()) { return fml::Status(fml::StatusCode::kUnknown, "command encoder invalid"); } return tracked_objects_->GetDescriptorPool().AllocateDescriptorSets( - buffer_count, sampler_count, subpass_count, layouts); + buffer_count, sampler_count, input_attachments_count, layouts); } void CommandEncoderVK::PushDebugGroup(const char* label) const { diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.h b/impeller/renderer/backend/vulkan/command_encoder_vk.h index 4c682e8284884..30850fccbbe17 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.h +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.h @@ -85,7 +85,7 @@ class CommandEncoderVK { fml::StatusOr> AllocateDescriptorSets( uint32_t buffer_count, uint32_t sampler_count, - uint32_t subpass_count, + uint32_t input_attachments_count, const std::vector& layouts); private: diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 6c1804dd74022..b8829016bb214 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -562,4 +562,9 @@ std::shared_ptr ContextVK::GetGPUTracer() const { return gpu_tracer_; } +std::shared_ptr ContextVK::GetDescriptorPoolRecycler() + const { + return descriptor_pool_recycler_; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index ac059ebed237c..a78413dff8a82 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -159,9 +159,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 GetGPUTracer() const; diff --git a/impeller/renderer/backend/vulkan/debug_report_vk.cc b/impeller/renderer/backend/vulkan/debug_report_vk.cc index 39068e056e0ec..e90aa8d9d0ccd 100644 --- a/impeller/renderer/backend/vulkan/debug_report_vk.cc +++ b/impeller/renderer/backend/vulkan/debug_report_vk.cc @@ -103,10 +103,29 @@ DebugReportVK::Result DebugReportVK::OnDebugCallback( vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* data) { + // TODO(csg): 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; + } + + // Issue in MoltenVK where the ID number is zero and the message warns of + // emulation. + if (data->messageIdNumber == 0 && // + data->pMessage != nullptr && // + strstr(data->pMessage, "MTLCounterSampleBuffer") != nullptr // + ) { return Result::kContinue; } diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc b/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc index 9d9461453c11f..b791547f12309 100644 --- a/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc +++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc @@ -14,7 +14,7 @@ namespace impeller { -// Holds the command pool in a background thread, recyling it when not in use. +// Holds the command pool in a background thread, recycling it when not in use. class BackgroundDescriptorPoolVK final { public: BackgroundDescriptorPoolVK(BackgroundDescriptorPoolVK&&) = default; @@ -83,14 +83,14 @@ fml::StatusOr> DescriptorPoolVK::AllocateDescriptorSets( uint32_t buffer_count, uint32_t sampler_count, - uint32_t subpass_count, + uint32_t input_attachments_count, const std::vector& layouts) { std::shared_ptr strong_context = context_.lock(); if (!strong_context) { return fml::Status(fml::StatusCode::kUnknown, "No device"); } auto minimum_capacity = - std::max(std::max(sampler_count, buffer_count), subpass_count); + std::max({sampler_count, buffer_count, input_attachments_count}); auto [new_pool, capacity] = strong_context->GetDescriptorPoolRecycler()->Get(minimum_capacity); if (!new_pool) { diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk.h b/impeller/renderer/backend/vulkan/descriptor_pool_vk.h index f4965ec224f6a..a717aa53da2ee 100644 --- a/impeller/renderer/backend/vulkan/descriptor_pool_vk.h +++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk.h @@ -33,7 +33,7 @@ class DescriptorPoolVK { fml::StatusOr> AllocateDescriptorSets( uint32_t buffer_count, uint32_t sampler_count, - uint32_t subpass_count, + uint32_t input_attachments_count, const std::vector& layouts); private: diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc b/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc index 8749f52af4daa..830192d5d5b3f 100644 --- a/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc @@ -66,7 +66,7 @@ TEST(DescriptorPoolRecyclerVKTest, ReclaimMakesDescriptorPoolAvailable) { { // Fetch a pool (which will be created). auto pool = DescriptorPoolVK(context); - pool.AllocateDescriptorSets(1024, 1024, 1024, {}); + pool.AllocateDescriptorSets(1024, 1024, 1024u, {}); } // There is a chance that the first death rattle item below is destroyed in 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 bf0ce5fb6a760..4c0c20890b6aa 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" @@ -665,6 +667,51 @@ constexpr vk::ImageAspectFlags ToImageAspectFlags(PixelFormat format) { FML_UNREACHABLE(); } +struct SubpassCursorVK { + size_t index = 0u; + size_t count = 1u; + + constexpr SubpassCursorVK() = default; + + constexpr SubpassCursorVK(size_t p_index, size_t p_count) + : index(p_index), count(p_count) {} + + constexpr bool IsValid() const { return index < count; } + + constexpr bool IsLoneSubpass() const { return index == 0u && count == 1u; } + + constexpr bool IsFinalSubpass() const { return index == count - 1u; } + + constexpr bool operator==(const SubpassCursorVK& o) const { + return index == o.index && count == o.count; + } + + struct Hash { + constexpr std::size_t operator()(const SubpassCursorVK& o) const { + return fml::HashCombine(o.index, o.count); + } + }; + + struct Equal { + constexpr bool operator()(const SubpassCursorVK& lhs, + const SubpassCursorVK& rhs) const { + return lhs.index == rhs.index && lhs.count == rhs.count; + } + }; +}; + +static constexpr auto kLoneSupassCursor = SubpassCursorVK{}; + } // namespace impeller +namespace std { + +inline std::ostream& operator<<(std::ostream& out, + const impeller::SubpassCursorVK& cursor) { + out << "{" << cursor.index << ", " << cursor.count << "}"; + return out; +} + +} // namespace std + #endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_FORMATS_VK_H_ diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 5149158a6904a..d4f080b0d02d3 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,12 @@ 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, // + kLoneSupassCursor // + )); }); return pipeline_future; @@ -741,4 +272,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..007802472f740 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -4,32 +4,481 @@ #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, + size_t subpass_count) { + RenderPassBuilderVK builder; + + builder.SetSubpassCount(subpass_count); + + 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, + std::weak_ptr weak_library, + SubpassCursorVK subpass_cursor) { + TRACE_EVENT0("flutter", "PipelineVK::Create"); + + auto library = weak_library.lock(); + + if (!device_holder || !library) { + return nullptr; + } + + // There seems to be no validation error for this simple error. And this + // condition is easy to get into if the right subpass steps are not added. + if (!subpass_cursor.IsValid()) { + VALIDATION_LOG << "Subpass cursor for pipeline creation was invalid: " + << subpass_cursor; + 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, // + subpass_cursor.count // + ); + + 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(subpass_cursor.index); + 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), // + subpass_cursor // + )); + 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, vk::UniquePipeline pipeline, vk::UniqueRenderPass render_pass, vk::UniquePipelineLayout layout, - vk::UniqueDescriptorSetLayout descriptor_set_layout) + vk::UniqueDescriptorSetLayout descriptor_set_layout, + SubpassCursorVK subpass_cursor) : Pipeline(std::move(library), desc), device_holder_(std::move(device_holder)), pipeline_(std::move(pipeline)), render_pass_(std::move(render_pass)), layout_(std::move(layout)), - descriptor_set_layout_(std::move(descriptor_set_layout)) { + descriptor_set_layout_(std::move(descriptor_set_layout)), + subpass_cursor_(subpass_cursor) { is_valid_ = pipeline_ && render_pass_ && layout_ && descriptor_set_layout_; } 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,12 +490,11 @@ bool PipelineVK::IsValid() const { return is_valid_; } -const vk::Pipeline& PipelineVK::GetPipeline() const { - return *pipeline_; -} - -const vk::RenderPass& PipelineVK::GetRenderPass() const { - return *render_pass_; +vk::Pipeline PipelineVK::GetPipeline(SubpassCursorVK cursor) const { + if (subpass_cursor_ == cursor) { + return *pipeline_; + } + return CreateOrGetVariantForSubpass(cursor).get()->GetPipeline(cursor); } const vk::PipelineLayout& PipelineVK::GetPipelineLayout() const { @@ -57,4 +505,59 @@ const vk::DescriptorSetLayout& PipelineVK::GetDescriptorSetLayout() const { return *descriptor_set_layout_; } +std::shared_future> +PipelineVK::CreateOrGetVariantForSubpass(SubpassCursorVK cursor) const { + Lock lock(subpass_pipelines_mutex_); + auto found = subpass_pipelines_.find(cursor); + if (found != subpass_pipelines_.end()) { + return found->second; + } + std::promise> promise; + auto future = + std::shared_future>{promise.get_future()}; + subpass_pipelines_[cursor] = future; + + auto task = [promise = std::move(promise), // + desc = desc_, // + device = device_holder_, // + library = library_, // + cursor // + ]() mutable { + TRACE_EVENT0("impeller", "CreateSubpassPipelineVariantAsync"); + auto pipeline = PipelineVK::Create(desc, // + device.lock(), // + library, // + cursor // + ); + promise.set_value(std::shared_ptr{std::move(pipeline)}); + }; + + auto library = library_.lock(); + if (!library) { + promise.set_value(nullptr); + return future; + } + + PipelineLibraryVK::Cast(*library).GetWorkerTaskRunner()->PostTask( + fml::MakeCopyable(std::move(task))); + + return future; +} + +void PipelineVK::PreloadPipeline(SubpassCursorVK cursor) const { + if (subpass_cursor_ == cursor) { + return; + } + // Create a future but don't await on it. The task has been enqueued. + CreateOrGetVariantForSubpass(cursor); +} + +bool PipelineVK::HasPreloadedPipeline(SubpassCursorVK cursor) const { + if (subpass_cursor_ == cursor) { + return true; + } + Lock lock(subpass_pipelines_mutex_); + return subpass_pipelines_.find(cursor) != subpass_pipelines_.end(); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.h b/impeller/renderer/backend/vulkan/pipeline_vk.h index ffe245152c4da..fceb2a2ff2dbd 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" @@ -18,20 +22,20 @@ 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, + std::weak_ptr weak_library, + SubpassCursorVK subpass_cursor); // |Pipeline| ~PipelineVK() override; - const vk::Pipeline& GetPipeline() const; + void PreloadPipeline(SubpassCursorVK cursor) const; + + bool HasPreloadedPipeline(SubpassCursorVK cursor) const; - const vk::RenderPass& GetRenderPass() const; + vk::Pipeline GetPipeline(SubpassCursorVK cursor) const; const vk::PipelineLayout& GetPipelineLayout() const; @@ -40,19 +44,42 @@ class PipelineVK final private: friend class PipelineLibraryVK; + using SubpassPipelines = + std::unordered_map>, + SubpassCursorVK::Hash, + SubpassCursorVK::Equal>; + std::weak_ptr device_holder_; vk::UniquePipeline pipeline_; vk::UniqueRenderPass render_pass_; vk::UniquePipelineLayout layout_; vk::UniqueDescriptorSetLayout descriptor_set_layout_; + SubpassCursorVK subpass_cursor_; + mutable Mutex subpass_pipelines_mutex_; + mutable SubpassPipelines subpass_pipelines_ IPLR_GUARDED_BY( + subpass_pipelines_mutex_); + 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, + SubpassCursorVK subpass_cursor); + // |Pipeline| bool IsValid() const override; PipelineVK(const PipelineVK&) = delete; PipelineVK& operator=(const PipelineVK&) = delete; + + std::shared_future> CreateOrGetVariantForSubpass( + SubpassCursorVK cursor) const; }; } // namespace impeller 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..4f1d2b73586f5 --- /dev/null +++ b/impeller/renderer/backend/vulkan/render_pass_builder_vk.cc @@ -0,0 +1,192 @@ +// 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 { + +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 { + if (colors_.empty()) { + return {}; + } + + FML_DCHECK(colors_.size() == resolves_.size()); + + // This must be less than `VkPhysicalDeviceLimits::maxColorAttachments` but we + // are not checking. + const auto color_attachments_count = 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()) { + vk::AttachmentReference depth_stencil_ref; + depth_stencil_ref.attachment = attachments.size(); + depth_stencil_ref.layout = vk::ImageLayout::eGeneral; + attachments.push_back(depth_stencil_.value()); + } + + std::vector subpasses; + subpasses.reserve(subpass_count_); + + std::vector subpass_dependencies; + subpass_dependencies.reserve(subpass_count_); + + for (size_t i = 0; i < subpass_count_; i++) { + // Setup the subpass. + { + vk::SubpassDescription subpass; + subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; + subpass.setInputAttachments(color_refs); + subpass.setColorAttachments(color_refs); + subpass.setResolveAttachments(resolve_refs); + subpass.setPDepthStencilAttachment(&depth_stencil_ref); + subpasses.emplace_back(std::move(subpass)); + } + + // Wire up the dependencies from the subpass before and after this subpass. + { + const bool is_first_subpass = i == 0u; + const bool is_last_subpass = i == subpass_count_ - 1u; + const bool is_sole_subpass = is_first_subpass && is_last_subpass; + + if (is_sole_subpass) { + // Don't bother adding dependencies from the external passes if this is + // the only subpass. The defaults are fine. + break; + } + + { + vk::SubpassDependency dep; + + dep.srcSubpass = is_first_subpass ? VK_SUBPASS_EXTERNAL : i - 1; + dep.dstSubpass = i; + + // All dependencies are framebuffer local. + dep.dependencyFlags = vk::DependencyFlagBits::eByRegion; + + // All commands before this barrier must proceed till at least the + // color-attachment writes in the color-attachment output pipeline + // stage are executed. And, all commands after this barrier may + // continue till they encounter an input-attachment read in the + // fragment shader pipeline stage. + dep.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; + dep.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + dep.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader; + dep.dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead; + + subpass_dependencies.emplace_back(std::move(dep)); + } + } + } + + vk::RenderPassCreateInfo render_pass_desc; + render_pass_desc.setAttachments(attachments); + render_pass_desc.setSubpasses(subpasses); + 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: " << vk::to_string(result); + return {}; + } + return std::move(pass); +} + +RenderPassBuilderVK& RenderPassBuilderVK::SetSubpassCount(size_t subpasses) { + subpass_count_ = std::max(1u, subpasses); + return *this; +} + +} // 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..1308b4d62b4bc --- /dev/null +++ b/impeller/renderer/backend/vulkan/render_pass_builder_vk.h @@ -0,0 +1,56 @@ +// 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& SetSubpassCount(size_t subpasses); + + 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_; + size_t subpass_count_ = 1u; +}; + +} // 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 67066c0878d0c..c1971cf7cfe31 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -4,8 +4,10 @@ #include "impeller/renderer/backend/vulkan/render_pass_vk.h" +#include #include #include +#include #include #include "flutter/fml/trace_event.h" @@ -19,6 +21,7 @@ #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/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/command.h" @@ -28,171 +31,68 @@ namespace impeller { -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 && - current_layout != vk::ImageLayout::eUndefined) { - // 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); + size_t subpass_count) 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; + + builder.SetSubpassCount(subpass_count); 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); } } if (auto depth = render_target_.GetDepthAttachment(); 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); + 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)); - 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); - } - - vk::RenderPassCreateInfo render_pass_desc; - render_pass_desc.setAttachments(attachments); - render_pass_desc.setPSubpasses(&subpass_desc); - render_pass_desc.setSubpassCount(1u); - - 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); + builder.SetStencilAttachment( + stencil->texture->GetTextureDescriptor().format, // + stencil->texture->GetTextureDescriptor().sample_count, // + stencil->load_action, // + stencil->store_action // + ); + TextureVK::Cast(*stencil->texture).SetLayout(barrier); + } + + auto pass = builder.Build(context.GetDevice()); + + 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)); } @@ -369,7 +269,8 @@ static bool EncodeCommand(const Context& context, CommandEncoderVK& encoder, PassBindingsCache& command_buffer_cache, const ISize& target_size, - const vk::DescriptorSet vk_desc_set) { + const vk::DescriptorSet vk_desc_set, + SubpassCursorVK subpass_cursor) { #ifdef IMPELLER_DEBUG fml::ScopedCleanupClosure pop_marker( [&encoder]() { encoder.PopDebugGroup(); }); @@ -391,8 +292,9 @@ static bool EncodeCommand(const Context& context, nullptr // offsets ); - command_buffer_cache.BindPipeline( - cmd_buffer, vk::PipelineBindPoint::eGraphics, pipeline_vk.GetPipeline()); + command_buffer_cache.BindPipeline(cmd_buffer, + vk::PipelineBindPoint::eGraphics, + pipeline_vk.GetPipeline(subpass_cursor)); // Set the viewport and scissors. SetViewportAndScissor(command, cmd_buffer, command_buffer_cache, target_size); @@ -468,6 +370,69 @@ static bool EncodeCommand(const Context& context, return true; } +static size_t CountSubpassesForCommandStream( + const std::vector& commands) { + if (commands.empty()) { + return 1u; + } + auto commands_with_input_attachments = + std::accumulate(commands.cbegin(), commands.cend(), 0u, + [](size_t count, const auto& command) { + return count + (command.pipeline->GetDescriptor() + .GetVertexDescriptor() + ->UsesInputAttacments() + ? 1u + : 0u); + }); + // If there are no commands with input attachments use, then we only need one + // lone subpass. This should be the most common case. + if (commands_with_input_attachments == 0) { + return 1u; + } + const bool first_command_uses_input_attachments = + commands.cbegin() + ->pipeline->GetDescriptor() + .GetVertexDescriptor() + ->UsesInputAttacments(); + return first_command_uses_input_attachments + ? commands_with_input_attachments + : commands_with_input_attachments + 1u; +} + +static bool ShouldAdvanceSubpass(const Command& command, size_t command_index) { + const bool command_uses_input_attachments = command.pipeline->GetDescriptor() + .GetVertexDescriptor() + ->UsesInputAttacments(); + + if (!command_uses_input_attachments) { + return false; + } + + if (command_index == 0 && command_uses_input_attachments) { + return false; + } + + return true; +} + +static void PreloadSubpassPipelineForCommandStream( + const std::vector& commands, + size_t subpass_count) { + if (subpass_count <= 1u) { + return; + } + size_t subpass_index = 0u; + for (size_t command_index = 0; command_index < commands.size(); + command_index++) { + const auto& command = commands[command_index]; + if (ShouldAdvanceSubpass(command, command_index)) { + subpass_index++; + } + PipelineVK::Cast(*command.pipeline) + .PreloadPipeline(SubpassCursorVK{subpass_index, subpass_count}); + } +} + bool RenderPassVK::OnEncodeCommands(const Context& context) const { TRACE_EVENT0("impeller", "RenderPassVK::OnEncodeCommands"); if (!IsValid()) { @@ -509,9 +474,13 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { const auto& target_size = render_target_.GetRenderTargetSize(); - auto render_pass = CreateVKRenderPass( - vk_context, command_buffer, - vk_context.GetCapabilities()->SupportsFramebufferFetch()); + SubpassCursorVK subpass_cursor; + subpass_cursor.count = CountSubpassesForCommandStream(commands_); + + PreloadSubpassPipelineForCommandStream(commands_, subpass_cursor.count); + + auto render_pass = + CreateVKRenderPass(vk_context, command_buffer, subpass_cursor.count); if (!render_pass) { VALIDATION_LOG << "Could not create renderpass."; return false; @@ -537,10 +506,11 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { static_cast(target_size.height); pass_info.setClearValues(clear_values); - const auto& color_image_vk = TextureVK::Cast( - *render_target_.GetColorAttachments().find(0u)->second.texture); + // Only attachment at index 0 may be used as a subpass input at this time. + const auto& color_attachment0 = + render_target_.GetColorAttachments().at(0u).texture; auto desc_sets_result = AllocateAndBindDescriptorSets( - vk_context, encoder, commands_, color_image_vk); + vk_context, encoder, commands_, color_attachment0); if (!desc_sets_result.ok()) { return false; } @@ -553,14 +523,31 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { fml::ScopedCleanupClosure end_render_pass( [cmd_buffer]() { cmd_buffer.endRenderPass(); }); - auto desc_index = 0u; + size_t command_index = 0u; for (const auto& command : commands_) { - if (!EncodeCommand(context, command, *encoder, pass_bindings_cache_, - target_size, desc_sets[desc_index])) { + if (ShouldAdvanceSubpass(command, command_index)) { + subpass_cursor.index++; + encoder->GetCommandBuffer().nextSubpass(vk::SubpassContents::eInline); + } + FML_DCHECK(subpass_cursor.IsValid()); + FML_DCHECK(PipelineVK::Cast(*command.pipeline) + .HasPreloadedPipeline(subpass_cursor)) + << "Insufficient subpass pipeline preloading. Functionally correct " + "but misses using available concurrency."; + if (!EncodeCommand(context, // + command, // + *encoder, // + pass_bindings_cache_, // + target_size, // + desc_sets[command_index], // + subpass_cursor // + )) { return false; } - desc_index += 1; + command_index += 1; } + FML_DCHECK(subpass_cursor.IsValid()); + FML_DCHECK(subpass_cursor.IsFinalSubpass()); } return true; diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index a173aafe35736..cb9f58a88e4ca 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -45,7 +45,7 @@ class RenderPassVK final : public RenderPass { SharedHandleVK CreateVKRenderPass( const ContextVK& context, const std::shared_ptr& command_buffer, - bool has_subpass_dependency) const; + size_t subpass_count) 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 ac31b6eea5be8..4ee44971dd2ec 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -219,7 +219,8 @@ SwapchainImplVK::SwapchainImplVK( // Swapchain images are primarily used as color attachments (via resolve) or // blit targets. 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 dcc40359f9d27..a061b859db542 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" @@ -1314,6 +1321,216 @@ TEST_P(RendererTest, CanLookupRenderTargetProperties) { render_target.GetRenderTargetSize()); } +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); + + // 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); + + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + SinglePassCallback callback = [&](RenderPass& pass) { + auto buffer = HostBuffer::Create(context->GetResourceAllocator()); + + // Draw the texture. + { + Command texture_cmd; + texture_cmd.pipeline = texture_pipeline; + texture_cmd.BindVertices(texture_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + TextureVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + TextureVS::BindUniformBuffer(texture_cmd, + buffer->EmplaceUniform(uniforms)); + TextureFS::BindTextureContents(texture_cmd, boston, sampler); + if (!pass.AddCommand(std::move(texture_cmd))) { + return false; + } + } + + // Draw the sepia toner. + { + Command sepia_cmd; + sepia_cmd.pipeline = sepia_pipeline; + sepia_cmd.BindVertices(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SepiaVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SepiaVS::BindUniformBuffer(sepia_cmd, buffer->EmplaceUniform(uniforms)); + if (!pass.AddCommand(std::move(sepia_cmd))) { + 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); + + // 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); + + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + SinglePassCallback callback = [&](RenderPass& pass) { + auto buffer = HostBuffer::Create(context->GetResourceAllocator()); + + // Draw the texture. + { + Command texture_cmd; + texture_cmd.pipeline = texture_pipeline; + texture_cmd.BindVertices(texture_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + TextureVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + TextureVS::BindUniformBuffer(texture_cmd, + buffer->EmplaceUniform(uniforms)); + TextureFS::BindTextureContents(texture_cmd, boston, sampler); + if (!pass.AddCommand(std::move(texture_cmd))) { + return false; + } + } + + // Draw the sepia toner. + { + Command sepia_cmd; + sepia_cmd.pipeline = sepia_pipeline; + sepia_cmd.BindVertices(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SepiaVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SepiaVS::BindUniformBuffer(sepia_cmd, buffer->EmplaceUniform(uniforms)); + if (!pass.AddCommand(std::move(sepia_cmd))) { + return false; + } + } + + // Draw the swizzle. + { + Command swizzle_cmd; + swizzle_cmd.pipeline = swizzle_pipeline; + swizzle_cmd.BindVertices(sepia_vtx_builder.CreateVertexBuffer( + *context->GetResourceAllocator())); + SwizzleVS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + SwizzleVS::BindUniformBuffer(swizzle_cmd, + buffer->EmplaceUniform(uniforms)); + if (!pass.AddCommand(std::move(swizzle_cmd))) { + 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; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 6541c3b12173b..ffc2c40667cdc 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -25,13 +25,6 @@ declare_args() { impeller_enable_vulkan = (is_linux || is_win || is_android || enable_unittests) && target_os != "fuchsia" - # Whether playgrounds should run with Vulkan. - # - # impeller_enable_vulkan may be true in build environments that run tests but - # do not have a Vulkan ICD present. - impeller_enable_vulkan_playgrounds = - (is_linux || is_win || is_android) && target_os != "fuchsia" - # Whether to use a prebuilt impellerc. # If this is the empty string, impellerc will be built. # If it is non-empty, it should be the absolute path to impellerc.