diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index cbb190fb11605..b9b1d51ac7282 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -147,6 +147,7 @@ ../../../flutter/impeller/playground ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/test ../../../flutter/impeller/renderer/capabilities_unittests.cc ../../../flutter/impeller/renderer/compute_subgroup_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5000803f207e8..09c825e26c410 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1520,6 +1520,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.cc + . ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc + ../../../flutter/LICENSE @@ -4195,6 +4197,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 96fe23a6c310d..4e451c4a233a7 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -10,6 +10,7 @@ impeller_component("vulkan_unittests") { sources = [ "blit_command_vk_unittests.cc", "context_vk_unittests.cc", + "pass_bindings_cache_unittests.cc", "test/mock_vulkan.cc", "test/mock_vulkan.h", ] @@ -51,6 +52,8 @@ impeller_component("vulkan") { "fence_waiter_vk.h", "formats_vk.cc", "formats_vk.h", + "pass_bindings_cache.cc", + "pass_bindings_cache.h", "pipeline_cache_vk.cc", "pipeline_cache_vk.h", "pipeline_library_vk.cc", diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.h b/impeller/renderer/backend/vulkan/command_encoder_vk.h index 32e176023d049..985e955fe0142 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.h +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.h @@ -18,13 +18,6 @@ namespace impeller { -namespace testing { -class BlitCommandVkTest_BlitCopyTextureToTextureCommandVK_Test; -class BlitCommandVkTest_BlitCopyTextureToBufferCommandVK_Test; -class BlitCommandVkTest_BlitCopyBufferToTextureCommandVK_Test; -class BlitCommandVkTest_BlitGenerateMipmapCommandVK_Test; -} // namespace testing - class ContextVK; class DeviceBuffer; class Buffer; @@ -37,6 +30,12 @@ class CommandEncoderVK { public: using SubmitCallback = std::function; + // Visible for testing. + CommandEncoderVK(const std::weak_ptr& device_holder, + const std::shared_ptr& queue, + const std::shared_ptr& pool, + std::shared_ptr fence_waiter); + ~CommandEncoderVK(); bool IsValid() const; @@ -68,14 +67,6 @@ class CommandEncoderVK { private: friend class ContextVK; - friend class ::impeller::testing:: - BlitCommandVkTest_BlitCopyTextureToTextureCommandVK_Test; - friend class ::impeller::testing:: - BlitCommandVkTest_BlitCopyTextureToBufferCommandVK_Test; - friend class ::impeller::testing:: - BlitCommandVkTest_BlitGenerateMipmapCommandVK_Test; - friend class ::impeller::testing:: - BlitCommandVkTest_BlitCopyBufferToTextureCommandVK_Test; std::weak_ptr device_holder_; std::shared_ptr queue_; @@ -83,11 +74,6 @@ class CommandEncoderVK { std::shared_ptr tracked_objects_; bool is_valid_ = false; - CommandEncoderVK(const std::weak_ptr& device_holder, - const std::shared_ptr& queue, - const std::shared_ptr& pool, - std::shared_ptr fence_waiter); - void Reset(); FML_DISALLOW_COPY_AND_ASSIGN(CommandEncoderVK); diff --git a/impeller/renderer/backend/vulkan/pass_bindings_cache.cc b/impeller/renderer/backend/vulkan/pass_bindings_cache.cc new file mode 100644 index 0000000000000..00d8774443824 --- /dev/null +++ b/impeller/renderer/backend/vulkan/pass_bindings_cache.cc @@ -0,0 +1,72 @@ +// 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/pass_bindings_cache.h" + +namespace impeller { +void PassBindingsCache::BindPipeline(vk::CommandBuffer command_buffer, + vk::PipelineBindPoint pipeline_bind_point, + vk::Pipeline pipeline) { + switch (pipeline_bind_point) { + case vk::PipelineBindPoint::eGraphics: + if (graphics_pipeline_.has_value() && + graphics_pipeline_.value() == pipeline) { + return; + } + graphics_pipeline_ = pipeline; + break; + case vk::PipelineBindPoint::eCompute: + if (compute_pipeline_.has_value() && + compute_pipeline_.value() == pipeline) { + return; + } + compute_pipeline_ = pipeline; + break; + default: + break; + } + command_buffer.bindPipeline(pipeline_bind_point, pipeline); +} + +void PassBindingsCache::SetStencilReference(vk::CommandBuffer command_buffer, + vk::StencilFaceFlags face_mask, + uint32_t reference) { + if (stencil_face_flags_.has_value() && + face_mask == stencil_face_flags_.value() && + reference == stencil_reference_) { + return; + } + stencil_face_flags_ = face_mask; + stencil_reference_ = reference; + command_buffer.setStencilReference(face_mask, reference); +} + +void PassBindingsCache::SetScissor(vk::CommandBuffer command_buffer, + uint32_t first_scissor, + uint32_t scissor_count, + const vk::Rect2D* scissors) { + if (first_scissor == 0 && scissor_count == 1) { + if (scissors_.has_value() && scissors_.value() == scissors[0]) { + return; + } + scissors_ = scissors[0]; + } + command_buffer.setScissor(first_scissor, scissor_count, scissors); +} + +void PassBindingsCache::SetViewport(vk::CommandBuffer command_buffer, + uint32_t first_viewport, + uint32_t viewport_count, + const vk::Viewport* viewports) { + if (first_viewport == 0 && viewport_count == 1) { + // Note that this is doing equality checks on floating point numbers. + if (viewport_.has_value() && viewport_.value() == viewports[0]) { + return; + } + viewport_ = viewports[0]; + } + command_buffer.setViewport(first_viewport, viewport_count, viewports); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pass_bindings_cache.h b/impeller/renderer/backend/vulkan/pass_bindings_cache.h new file mode 100644 index 0000000000000..2c7ed85445402 --- /dev/null +++ b/impeller/renderer/backend/vulkan/pass_bindings_cache.h @@ -0,0 +1,46 @@ +// 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. + +#pragma once + +#include + +#include "flutter/impeller/renderer/backend/vulkan/vk.h" + +namespace impeller { + +class PassBindingsCache { + public: + void BindPipeline(vk::CommandBuffer command_buffer, + vk::PipelineBindPoint pipeline_bind_point, + vk::Pipeline pipeline); + + void SetStencilReference(vk::CommandBuffer command_buffer, + vk::StencilFaceFlags face_mask, + uint32_t reference); + + void SetScissor(vk::CommandBuffer command_buffer, + uint32_t first_scissor, + uint32_t scissor_count, + const vk::Rect2D* scissors); + + void SetViewport(vk::CommandBuffer command_buffer, + uint32_t first_viewport, + uint32_t viewport_count, + const vk::Viewport* viewports); + + private: + // bindPipeline + std::optional graphics_pipeline_; + std::optional compute_pipeline_; + // setStencilReference + std::optional stencil_face_flags_; + uint32_t stencil_reference_ = 0; + // setScissor + std::optional scissors_; + // setViewport + std::optional viewport_; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc b/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc new file mode 100644 index 0000000000000..feb788fe3afbe --- /dev/null +++ b/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc @@ -0,0 +1,94 @@ +// 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 "flutter/testing/testing.h" +#include "impeller/renderer/backend/vulkan/command_encoder_vk.h" +#include "impeller/renderer/backend/vulkan/pass_bindings_cache.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +namespace { +int32_t CountStringViewInstances(const std::vector& strings, + std::string_view target) { + int32_t count = 0; + for (const std::string& str : strings) { + if (str == target) { + count++; + } + } + return count; +} +} // namespace + +TEST(PassBindingsCacheTest, bindPipeline) { + auto context = CreateMockVulkanContext(); + PassBindingsCache cache; + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDeviceHolder(), + context->GetGraphicsQueue(), pool, + context->GetFenceWaiter()); + auto buffer = encoder.GetCommandBuffer(); + VkPipeline vk_pipeline = reinterpret_cast(0xfeedface); + vk::Pipeline pipeline(vk_pipeline); + cache.BindPipeline(buffer, vk::PipelineBindPoint::eGraphics, pipeline); + cache.BindPipeline(buffer, vk::PipelineBindPoint::eGraphics, pipeline); + std::shared_ptr> functions = + GetMockVulkanFunctions(context->GetDevice()); + EXPECT_EQ(CountStringViewInstances(*functions, "vkCmdBindPipeline"), 1); +} + +TEST(PassBindingsCacheTest, setStencilReference) { + auto context = CreateMockVulkanContext(); + PassBindingsCache cache; + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDeviceHolder(), + context->GetGraphicsQueue(), pool, + context->GetFenceWaiter()); + auto buffer = encoder.GetCommandBuffer(); + cache.SetStencilReference( + buffer, vk::StencilFaceFlagBits::eVkStencilFrontAndBack, 123); + cache.SetStencilReference( + buffer, vk::StencilFaceFlagBits::eVkStencilFrontAndBack, 123); + std::shared_ptr> functions = + GetMockVulkanFunctions(context->GetDevice()); + EXPECT_EQ(CountStringViewInstances(*functions, "vkCmdSetStencilReference"), + 1); +} + +TEST(PassBindingsCacheTest, setScissor) { + auto context = CreateMockVulkanContext(); + PassBindingsCache cache; + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDeviceHolder(), + context->GetGraphicsQueue(), pool, + context->GetFenceWaiter()); + auto buffer = encoder.GetCommandBuffer(); + vk::Rect2D scissors; + cache.SetScissor(buffer, 0, 1, &scissors); + cache.SetScissor(buffer, 0, 1, &scissors); + std::shared_ptr> functions = + GetMockVulkanFunctions(context->GetDevice()); + EXPECT_EQ(CountStringViewInstances(*functions, "vkCmdSetScissor"), 1); +} + +TEST(PassBindingsCacheTest, setViewport) { + auto context = CreateMockVulkanContext(); + PassBindingsCache cache; + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDeviceHolder(), + context->GetGraphicsQueue(), pool, + context->GetFenceWaiter()); + auto buffer = encoder.GetCommandBuffer(); + vk::Viewport viewports; + cache.SetViewport(buffer, 0, 1, &viewports); + cache.SetViewport(buffer, 0, 1, &viewports); + std::shared_ptr> functions = + GetMockVulkanFunctions(context->GetDevice()); + EXPECT_EQ(CountStringViewInstances(*functions, "vkCmdSetViewport"), 1); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 9806fb675a460..2a782757f8cc0 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -417,6 +417,7 @@ static bool AllocateAndBindDescriptorSets(const ContextVK& context, static void SetViewportAndScissor(const Command& command, const vk::CommandBuffer& cmd_buffer, + PassBindingsCache& cmd_buffer_cache, const ISize& target_size) { // Set the viewport. const auto& vp = command.viewport.value_or( @@ -427,7 +428,7 @@ static void SetViewportAndScissor(const Command& command, .setY(vp.rect.size.height) .setMinDepth(0.0f) .setMaxDepth(1.0f); - cmd_buffer.setViewport(0, 1, &viewport); + cmd_buffer_cache.SetViewport(cmd_buffer, 0, 1, &viewport); // Set the scissor rect. const auto& sc = command.scissor.value_or(IRect::MakeSize(target_size)); @@ -435,12 +436,13 @@ static void SetViewportAndScissor(const Command& command, vk::Rect2D() .setOffset(vk::Offset2D(sc.origin.x, sc.origin.y)) .setExtent(vk::Extent2D(sc.size.width, sc.size.height)); - cmd_buffer.setScissor(0, 1, &scissor); + cmd_buffer_cache.SetScissor(cmd_buffer, 0, 1, &scissor); } static bool EncodeCommand(const Context& context, const Command& command, CommandEncoderVK& encoder, + PassBindingsCache& command_buffer_cache, const ISize& target_size) { if (command.vertex_count == 0u || command.instance_count == 0u) { return true; @@ -466,15 +468,15 @@ static bool EncodeCommand(const Context& context, return false; } - cmd_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, - pipeline_vk.GetPipeline()); + command_buffer_cache.BindPipeline( + cmd_buffer, vk::PipelineBindPoint::eGraphics, pipeline_vk.GetPipeline()); // Set the viewport and scissors. - SetViewportAndScissor(command, cmd_buffer, target_size); + SetViewportAndScissor(command, cmd_buffer, command_buffer_cache, target_size); // Set the stencil reference. - cmd_buffer.setStencilReference( - vk::StencilFaceFlagBits::eVkStencilFrontAndBack, + command_buffer_cache.SetStencilReference( + cmd_buffer, vk::StencilFaceFlagBits::eVkStencilFrontAndBack, command.stencil_reference); // Configure vertex and index and buffers for binding. @@ -619,7 +621,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { continue; } - if (!EncodeCommand(context, command, *encoder, target_size)) { + if (!EncodeCommand(context, command, *encoder, pass_bindings_cache_, + target_size)) { return false; } } diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index 1f267cea5211b..b952b7db146ea 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/pass_bindings_cache.h" #include "impeller/renderer/backend/vulkan/shared_object_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" @@ -26,6 +27,7 @@ class RenderPassVK final : public RenderPass { std::weak_ptr encoder_; std::string debug_label_; bool is_valid_ = false; + mutable PassBindingsCache pass_bindings_cache_; RenderPassVK(const std::shared_ptr& context, const RenderTarget& target, diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index aac80e1abac72..d4faece695e4d 100644 --- a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -9,9 +9,24 @@ namespace testing { namespace { +struct MockCommandBuffer { + explicit MockCommandBuffer( + std::shared_ptr> called_functions) + : called_functions_(std::move(called_functions)) {} + std::shared_ptr> called_functions_; +}; + struct MockDevice { MockDevice() : called_functions_(new std::vector()) {} + MockCommandBuffer* NewCommandBuffer() { + std::unique_ptr buffer = + std::make_unique(called_functions_); + MockCommandBuffer* result = buffer.get(); + command_buffers_.emplace_back(std::move(buffer)); + return result; + } std::shared_ptr> called_functions_; + std::vector> command_buffers_; }; void noop() {} @@ -149,7 +164,9 @@ VkResult vkAllocateCommandBuffers( VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers) { - *pCommandBuffers = reinterpret_cast(0x0b0ffe12); + MockDevice* mock_device = reinterpret_cast(device); + *pCommandBuffers = + reinterpret_cast(mock_device->NewCommandBuffer()); return VK_SUCCESS; } @@ -295,6 +312,40 @@ void vkDestroyPipelineCache(VkDevice device, mock_device->called_functions_->push_back("vkDestroyPipelineCache"); } +void vkCmdBindPipeline(VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline) { + MockCommandBuffer* mock_command_buffer = + reinterpret_cast(commandBuffer); + mock_command_buffer->called_functions_->push_back("vkCmdBindPipeline"); +} + +void vkCmdSetStencilReference(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t reference) { + MockCommandBuffer* mock_command_buffer = + reinterpret_cast(commandBuffer); + mock_command_buffer->called_functions_->push_back("vkCmdSetStencilReference"); +} + +void vkCmdSetScissor(VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors) { + MockCommandBuffer* mock_command_buffer = + reinterpret_cast(commandBuffer); + mock_command_buffer->called_functions_->push_back("vkCmdSetScissor"); +} + +void vkCmdSetViewport(VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports) { + MockCommandBuffer* mock_command_buffer = + reinterpret_cast(commandBuffer); + mock_command_buffer->called_functions_->push_back("vkCmdSetViewport"); +} + PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, const char* pName) { if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) { @@ -365,6 +416,14 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, return (PFN_vkVoidFunction)vkDestroyShaderModule; } else if (strcmp("vkDestroyPipelineCache", pName) == 0) { return (PFN_vkVoidFunction)vkDestroyPipelineCache; + } else if (strcmp("vkCmdBindPipeline", pName) == 0) { + return (PFN_vkVoidFunction)vkCmdBindPipeline; + } else if (strcmp("vkCmdSetStencilReference", pName) == 0) { + return (PFN_vkVoidFunction)vkCmdSetStencilReference; + } else if (strcmp("vkCmdSetScissor", pName) == 0) { + return (PFN_vkVoidFunction)vkCmdSetScissor; + } else if (strcmp("vkCmdSetViewport", pName) == 0) { + return (PFN_vkVoidFunction)vkCmdSetViewport; } return noop; }