From 40d5510bbea672d8594d696c06b07099658dc51f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 05:28:40 -0700 Subject: [PATCH 01/14] Add blit pass --- impeller/renderer/BUILD.gn | 4 + impeller/renderer/backend/gles/BUILD.gn | 2 + .../renderer/backend/gles/blit_pass_gles.cc | 111 +++++++++++++++ .../renderer/backend/gles/blit_pass_gles.h | 40 ++++++ .../backend/gles/command_buffer_gles.cc | 13 ++ .../backend/gles/command_buffer_gles.h | 3 + impeller/renderer/backend/gles/formats_gles.h | 13 ++ .../renderer/backend/gles/proc_table_gles.h | 2 + .../renderer/backend/gles/texture_gles.cc | 27 ++++ impeller/renderer/backend/gles/texture_gles.h | 2 + impeller/renderer/backend/metal/BUILD.gn | 2 + .../renderer/backend/metal/blit_pass_mtl.h | 43 ++++++ .../renderer/backend/metal/blit_pass_mtl.mm | 127 ++++++++++++++++++ .../backend/metal/command_buffer_mtl.h | 3 + .../backend/metal/command_buffer_mtl.mm | 14 ++ impeller/renderer/backend/vulkan/BUILD.gn | 2 + .../renderer/backend/vulkan/blit_pass_vk.cc | 11 ++ .../renderer/backend/vulkan/blit_pass_vk.h | 35 +++++ .../backend/vulkan/command_buffer_vk.cc | 4 + .../backend/vulkan/command_buffer_vk.h | 3 + impeller/renderer/blit_command.cc | 11 ++ impeller/renderer/blit_command.h | 30 +++++ impeller/renderer/blit_pass.cc | 78 +++++++++++ impeller/renderer/blit_pass.h | 95 +++++++++++++ impeller/renderer/command_buffer.cc | 9 ++ impeller/renderer/command_buffer.h | 14 +- 26 files changed, 696 insertions(+), 2 deletions(-) create mode 100644 impeller/renderer/backend/gles/blit_pass_gles.cc create mode 100644 impeller/renderer/backend/gles/blit_pass_gles.h create mode 100644 impeller/renderer/backend/metal/blit_pass_mtl.h create mode 100644 impeller/renderer/backend/metal/blit_pass_mtl.mm create mode 100644 impeller/renderer/backend/vulkan/blit_pass_vk.cc create mode 100644 impeller/renderer/backend/vulkan/blit_pass_vk.h create mode 100644 impeller/renderer/blit_command.cc create mode 100644 impeller/renderer/blit_command.h create mode 100644 impeller/renderer/blit_pass.cc create mode 100644 impeller/renderer/blit_pass.h diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index b61d4462de43c..ab659f9da81f2 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -8,6 +8,10 @@ impeller_component("renderer") { sources = [ "allocator.cc", "allocator.h", + "blit_command.cc", + "blit_command.h", + "blit_pass.cc", + "blit_pass.h", "buffer.cc", "buffer.h", "buffer_view.cc", diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 85f0a377bf4c9..4fdc6e6de15da 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -16,6 +16,8 @@ impeller_component("gles") { sources = [ "allocator_gles.cc", "allocator_gles.h", + "blit_pass_gles.cc", + "blit_pass_gles.h", "buffer_bindings_gles.cc", "buffer_bindings_gles.h", "capabilities_gles.cc", diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc new file mode 100644 index 0000000000000..e8e55d753ebf5 --- /dev/null +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -0,0 +1,111 @@ +// 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/gles/blit_pass_gles.h" + +#include + +#include "GLES2/gl2.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/config.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/device_buffer_gles.h" +#include "impeller/renderer/backend/gles/formats_gles.h" +#include "impeller/renderer/backend/gles/pipeline_gles.h" +#include "impeller/renderer/backend/gles/texture_gles.h" +#include "impeller/renderer/formats.h" + +namespace impeller { + +BlitPassGLES::BlitPassGLES(ReactorGLES::Ref reactor) + : reactor_(std::move(reactor)), + is_valid_(reactor_ && reactor_->IsValid()) {} + +// |BlitPass| +BlitPassGLES::~BlitPassGLES() = default; + +// |BlitPass| +bool BlitPassGLES::IsValid() const { + return is_valid_; +} + +// |BlitPass| +void BlitPassGLES::OnSetLabel(std::string label) { + label_ = std::move(label); +} + +[[nodiscard]] bool EncodeCommandsInReactor( + const std::shared_ptr& transients_allocator, + const ReactorGLES& reactor, + const std::vector& commands, + const std::string& label) { + TRACE_EVENT0("impeller", __FUNCTION__); + + if (commands.empty()) { + return true; + } + + const auto& gl = reactor.GetProcTable(); + + fml::ScopedCleanupClosure pop_pass_debug_marker( + [&gl]() { gl.PopDebugGroup(); }); + if (!label.empty()) { + gl.PushDebugGroup(label); + } else { + pop_pass_debug_marker.Release(); + } + + for (const auto& command : commands) { + fml::ScopedCleanupClosure pop_cmd_debug_marker( + [&gl]() { gl.PopDebugGroup(); }); + if (!command.label.empty()) { + gl.PushDebugGroup(command.label); + } else { + pop_cmd_debug_marker.Release(); + } + + if (auto* copy_command = + std::get_if(&command.data)) { + if (!gl.BindFramebuffer.IsAvailable()) { + // TODO(bdero): Emulate the blit using a raster draw call here. + return true; + } + gl.Disable(GL_SCISSOR_TEST); + gl.Disable(GL_DEPTH_TEST); + gl.Disable(GL_STENCIL_TEST); + auto source = TextureGLES::Cast(copy_command->source.get()); + + } + + else if (auto* mipmap_command = + std::get_if(&command.data)) { + auto texture = TextureGLES::Cast(mipmap_command->texture.get()); + if (!texture->GenerateMipmaps()) { + return false; + } + } + } + + return true; +} + +// |BlitPass| +bool BlitPassGLES::EncodeCommands( + const std::shared_ptr& transients_allocator) const { + if (!IsValid()) { + return false; + } + if (commands_.empty()) { + return true; + } + + return reactor_->AddOperation([transients_allocator, commands = commands_, + label = label_](const auto& reactor) { + auto result = + EncodeCommandsInReactor(transients_allocator, reactor, commands, label); + FML_CHECK(result) << "Must be able to encode GL commands without error."; + }); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h new file mode 100644 index 0000000000000..99b18622517be --- /dev/null +++ b/impeller/renderer/backend/gles/blit_pass_gles.h @@ -0,0 +1,40 @@ +// 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 "flutter/fml/macros.h" +#include "flutter/impeller/renderer/backend/gles/reactor_gles.h" +#include "flutter/impeller/renderer/blit_pass.h" + +namespace impeller { + +class BlitPassGLES final : public BlitPass { + public: + // |BlitPass| + ~BlitPassGLES() override; + + private: + friend class CommandBufferGLES; + + ReactorGLES::Ref reactor_; + std::string label_; + bool is_valid_ = false; + + explicit BlitPassGLES(ReactorGLES::Ref reactor); + + // |BlitPass| + bool IsValid() const override; + + // |BlitPass| + void OnSetLabel(std::string label) override; + + // |BlitPass| + bool EncodeCommands( + const std::shared_ptr& transients_allocator) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.cc b/impeller/renderer/backend/gles/command_buffer_gles.cc index 57e86a937e82c..7bb99354c079c 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.cc +++ b/impeller/renderer/backend/gles/command_buffer_gles.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/gles/command_buffer_gles.h" #include "impeller/base/config.h" +#include "impeller/renderer/backend/gles/blit_pass_gles.h" #include "impeller/renderer/backend/gles/render_pass_gles.h" namespace impeller { @@ -54,4 +55,16 @@ std::shared_ptr CommandBufferGLES::OnCreateRenderPass( return pass; } +// |CommandBuffer| +std::shared_ptr CommandBufferGLES::OnCreateBlitPass() const { + if (!IsValid()) { + return nullptr; + } + auto pass = std::shared_ptr(new BlitPassGLES(reactor_)); + if (!pass->IsValid()) { + return nullptr; + } + return pass; +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.h b/impeller/renderer/backend/gles/command_buffer_gles.h index e1a3592ad048e..22b26464379d7 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.h +++ b/impeller/renderer/backend/gles/command_buffer_gles.h @@ -37,6 +37,9 @@ class CommandBufferGLES final : public CommandBuffer { std::shared_ptr OnCreateRenderPass( RenderTarget target) const override; + // |CommandBuffer| + std::shared_ptr OnCreateBlitPass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferGLES); }; diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h index bde996e3f8a6f..e20e2fd5bc4b4 100644 --- a/impeller/renderer/backend/gles/formats_gles.h +++ b/impeller/renderer/backend/gles/formats_gles.h @@ -4,6 +4,7 @@ #include +#include "GLES2/gl2ext.h" #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "impeller/renderer/backend/gles/gles.h" @@ -163,6 +164,18 @@ constexpr std::optional ToVertexAttribType(ShaderType type) { FML_UNREACHABLE(); } +constexpr GLenum ToTextureType(TextureType type) { + switch (type) { + case TextureType::kTexture2D: + return GL_TEXTURE_2D; + case TextureType::kTexture2DMultisample: + return GL_TEXTURE_2D_MULTISAMPLE; + case TextureType::kTextureCube: + return GL_TEXTURE_CUBE_MAP; + } + FML_UNREACHABLE(); +} + constexpr std::optional ToTextureTarget(TextureType type) { switch (type) { case TextureType::kTexture2D: diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index 222dd7d2f1f80..c0a8769e85488 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -93,6 +93,7 @@ struct GLProc { PROC(BindTexture); \ PROC(BlendEquationSeparate); \ PROC(BlendFuncSeparate); \ + PROC(BlitFramebuffer); \ PROC(BufferData); \ PROC(CheckFramebufferStatus); \ PROC(Clear); \ @@ -123,6 +124,7 @@ struct GLProc { PROC(FramebufferTexture2D); \ PROC(FrontFace); \ PROC(GenBuffers); \ + PROC(GenerateMipmap); \ PROC(GenFramebuffers); \ PROC(GenRenderbuffers); \ PROC(GenTextures); \ diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index 752d80c854bfe..634fcfcdcdf7e 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -12,6 +12,7 @@ #include "impeller/base/config.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/gles/formats_gles.h" +#include "impeller/renderer/formats.h" namespace impeller { @@ -375,6 +376,32 @@ bool TextureGLES::Bind() const { return true; } +bool TextureGLES::GenerateMipmaps() const { + if (!IsValid()) { + return false; + } + + auto type = GetTextureDescriptor().type; + if (type != TextureType::kTexture2D && type != TextureType::kTextureCube) { + VALIDATION_LOG << "Could not generate mipmaps for texture type. Only " + "Texture2D and TextureCube are supported for GLES."; + return false; + } + + if (!Bind()) { + return false; + } + + auto handle = reactor_->GetGLHandle(handle_); + if (!handle.has_value()) { + return false; + } + + const auto& gl = reactor_->GetProcTable(); + gl.GenerateMipmap(ToTextureType(type)); + return true; +} + TextureGLES::Type TextureGLES::GetType() const { return type_; } diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index 24b07a7594518..cef3aec6a9f67 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -35,6 +35,8 @@ class TextureGLES final : public Texture, [[nodiscard]] bool Bind() const; + [[nodiscard]] bool GenerateMipmaps() const; + enum class AttachmentPoint { kColor0, kDepth, diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn index 4458a07b41105..fc364afab6947 100644 --- a/impeller/renderer/backend/metal/BUILD.gn +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("metal") { sources = [ "allocator_mtl.h", "allocator_mtl.mm", + "blit_pass_mtl.h", + "blit_pass_mtl.mm", "command_buffer_mtl.h", "command_buffer_mtl.mm", "context_mtl.h", diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h new file mode 100644 index 0000000000000..af1624c90e9f8 --- /dev/null +++ b/impeller/renderer/backend/metal/blit_pass_mtl.h @@ -0,0 +1,43 @@ +// 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/fml/macros.h" +#include "impeller/renderer/blit_pass.h" + +namespace impeller { + +class BlitPassMTL final : public BlitPass { + public: + // |RenderPass| + ~BlitPassMTL() override; + + private: + friend class CommandBufferMTL; + + id buffer_ = nil; + std::string label_; + bool is_valid_ = false; + + explicit BlitPassMTL(id buffer); + + // |RenderPass| + bool IsValid() const override; + + // |RenderPass| + void OnSetLabel(std::string label) override; + + // |RenderPass| + bool EncodeCommands( + const std::shared_ptr& transients_allocator) const override; + + bool EncodeCommands(id pass) const; + + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm new file mode 100644 index 0000000000000..a7e0380415b8a --- /dev/null +++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm @@ -0,0 +1,127 @@ +// 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/metal/blit_pass_mtl.h" +#include +#include + +#include "flutter/fml/closure.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/metal/device_buffer_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/pipeline_mtl.h" +#include "impeller/renderer/backend/metal/sampler_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/renderer/blit_command.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +BlitPassMTL::BlitPassMTL(id buffer) : buffer_(buffer) { + if (!buffer_) { + return; + } + is_valid_ = true; +} + +BlitPassMTL::~BlitPassMTL() = default; + +bool BlitPassMTL::IsValid() const { + return is_valid_; +} + +void BlitPassMTL::OnSetLabel(std::string label) { + if (label.empty()) { + return; + } + label_ = std::move(label); +} + +bool BlitPassMTL::EncodeCommands( + const std::shared_ptr& transients_allocator) const { + TRACE_EVENT0("impeller", "BlitPassMTL::EncodeCommands"); + if (!IsValid()) { + return false; + } + + auto blit_command_encoder = [buffer_ blitCommandEncoder]; + + if (!blit_command_encoder) { + return false; + } + + if (!label_.empty()) { + [blit_command_encoder setLabel:@(label_.c_str())]; + } + + // Success or failure, the pass must end. The buffer can only process one pass + // at a time. + fml::ScopedCleanupClosure auto_end( + [blit_command_encoder]() { [blit_command_encoder endEncoding]; }); + + return EncodeCommands(blit_command_encoder); +} + +bool BlitPassMTL::EncodeCommands(id encoder) const { + fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; + for (const auto& command : commands_) { + fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); + if (!command.label.empty()) { + [encoder pushDebugGroup:@(command.label.c_str())]; + } else { + auto_pop_debug_marker.Release(); + } + + if (auto* copy_command = + std::get_if(&command.data)) { + auto source = TextureMTL::Cast(*copy_command->source).GetMTLTexture(); + if (!source) { + return false; + } + + auto destination = + TextureMTL::Cast(*copy_command->source).GetMTLTexture(); + if (!destination) { + return false; + } + + auto source_origin = + MTLOriginMake(copy_command->source_region.origin.x, + copy_command->source_region.origin.y, 0); + auto source_size = + MTLSizeMake(copy_command->source_region.size.width, + copy_command->source_region.size.height, 1); + auto destination_origin = + MTLOriginMake(copy_command->destination_origin.x, + copy_command->destination_origin.y, 0); + + [encoder copyFromTexture:source + sourceSlice:0 + sourceLevel:0 + sourceOrigin:source_origin + sourceSize:source_size + toTexture:destination + destinationSlice:0 + destinationLevel:0 + destinationOrigin:destination_origin]; + } + + else if (auto* mipmap_command = + std::get_if(&command.data)) { + auto texture = TextureMTL::Cast(*mipmap_command->texture).GetMTLTexture(); + if (!texture) { + return false; + } + + [encoder generateMipmapsForTexture:texture]; + } + } + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index 5c06cfd3012b1..6c038d675376a 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -38,6 +38,9 @@ class CommandBufferMTL final : public CommandBuffer { std::shared_ptr OnCreateRenderPass( RenderTarget target) const override; + // |CommandBuffer| + std::shared_ptr OnCreateBlitPass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL); }; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index e7d6c56199449..90bcbf1cf9256 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -4,6 +4,7 @@ #include "impeller/renderer/backend/metal/command_buffer_mtl.h" +#include "impeller/renderer/backend/metal/blit_pass_mtl.h" #include "impeller/renderer/backend/metal/render_pass_mtl.h" namespace impeller { @@ -202,4 +203,17 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { return pass; } +std::shared_ptr CommandBufferMTL::OnCreateBlitPass() const { + if (!buffer_) { + return nullptr; + } + + auto pass = std::shared_ptr(new BlitPassMTL(buffer_)); + if (!pass->IsValid()) { + return nullptr; + } + + return pass; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 34c3787ce289d..bde65b844f691 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("vulkan") { sources = [ "allocator_vk.cc", "allocator_vk.h", + "blit_pass_vk.cc", + "blit_pass_vk.h", "capabilities_vk.cc", "capabilities_vk.h", "command_buffer_vk.cc", diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.cc b/impeller/renderer/backend/vulkan/blit_pass_vk.cc new file mode 100644 index 0000000000000..03f0510ada630 --- /dev/null +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.cc @@ -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. + +#include "impeller/renderer/backend/vulkan/blit_pass_vk.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h new file mode 100644 index 0000000000000..efa07c16c05bb --- /dev/null +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h @@ -0,0 +1,35 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/renderer/blit_pass.h" + +namespace impeller { + +class BlitPassVK final : public BlitPass { + public: + // |BlitPass| + ~BlitPassVK() override; + + private: + friend class CommandBufferVK; + + BlitPassVK(); + + // |BlitPass| + bool IsValid() const override; + + // |BlitPass| + void OnSetLabel(std::string label) override; + + // |BlitPass| + bool EncodeCommands( + const std::shared_ptr& transients_allocator) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassVK); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.cc b/impeller/renderer/backend/vulkan/command_buffer_vk.cc index aac4ee840de61..0a33906c1f2de 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.cc +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.cc @@ -30,4 +30,8 @@ std::shared_ptr CommandBufferVK::OnCreateRenderPass( FML_UNREACHABLE(); } +std::shared_ptr CommandBufferVK::OnCreateBlitPass() const { + FML_UNREACHABLE(); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.h b/impeller/renderer/backend/vulkan/command_buffer_vk.h index 14d864bcb413f..cb166f8689ecb 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.h +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.h @@ -32,6 +32,9 @@ class CommandBufferVK final : public CommandBuffer { std::shared_ptr OnCreateRenderPass( RenderTarget target) const override; + // |CommandBuffer| + std::shared_ptr OnCreateBlitPass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferVK); }; diff --git a/impeller/renderer/blit_command.cc b/impeller/renderer/blit_command.cc new file mode 100644 index 0000000000000..0e01e8dccf565 --- /dev/null +++ b/impeller/renderer/blit_command.cc @@ -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. + +#include "impeller/renderer/blit_command.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/blit_command.h b/impeller/renderer/blit_command.h new file mode 100644 index 0000000000000..8fe3317fac5ab --- /dev/null +++ b/impeller/renderer/blit_command.h @@ -0,0 +1,30 @@ +// 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 "impeller/renderer/texture.h" + +namespace impeller { + +struct BlitCommand { + struct CopyTextureToTexture { + std::shared_ptr source; + std::shared_ptr destination; + IRect source_region; + IPoint destination_origin; + }; + + struct GenerateMipmaps { + std::shared_ptr texture; + }; + + using Variant = std::variant; + + std::string label; + Variant data; +}; +} // namespace impeller diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc new file mode 100644 index 0000000000000..480c573959aac --- /dev/null +++ b/impeller/renderer/blit_pass.cc @@ -0,0 +1,78 @@ +// 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/blit_pass.h" +#include + +#include "impeller/base/strings.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/blit_command.h" +#include "impeller/renderer/host_buffer.h" + +namespace impeller { + +BlitPass::BlitPass() : transients_buffer_(HostBuffer::Create()) {} + +BlitPass::~BlitPass() = default; + +HostBuffer& BlitPass::GetTransientsBuffer() { + return *transients_buffer_; +} + +void BlitPass::SetLabel(std::string label) { + if (label.empty()) { + return; + } + transients_buffer_->SetLabel(SPrintF("%s Transients", label.c_str())); + OnSetLabel(std::move(label)); +} + +bool BlitPass::AddCopy(std::shared_ptr source, + std::shared_ptr destination, + std::optional source_region, + IPoint destination_origin, + std::string label) { + if (!source) { + VALIDATION_LOG << "Attempted to add an invalid copy with no source."; + return false; + } + if (!destination) { + VALIDATION_LOG << "Attempted to add an invalid copy with no destination."; + return false; + } + + commands_.emplace_back(BlitCommand{ + .label = label, + .data = + BlitCommand::CopyTextureToTexture{ + .source = source, + .destination = destination, + .source_region = source_region.has_value() + ? source_region.value() + : IRect::MakeSize(source->GetSize()), + .destination_origin = destination_origin, + }, + }); + return true; +} + +bool BlitPass::AddGenerateMipmaps(std::shared_ptr texture, + std::string label) { + if (!texture) { + VALIDATION_LOG << "Attempted to add an invalid mipmap generation command " + "with no texture."; + return false; + } + + commands_.emplace_back(BlitCommand{ + .label = label, + .data = + BlitCommand::GenerateMipmaps{ + .texture = texture, + }, + }); + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h new file mode 100644 index 0000000000000..08feb3c071d7f --- /dev/null +++ b/impeller/renderer/blit_pass.h @@ -0,0 +1,95 @@ +// 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 + +#include "impeller/renderer/blit_command.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class HostBuffer; +class Allocator; + +//------------------------------------------------------------------------------ +/// @brief Blit passes encode blit into the underlying command buffer. +/// +/// Blit passes can be obtained from the command buffer in which +/// the pass is meant to encode commands into. +/// +/// @see `CommandBuffer` +/// +class BlitPass { + public: + virtual ~BlitPass(); + + virtual bool IsValid() const = 0; + + void SetLabel(std::string label); + + HostBuffer& GetTransientsBuffer(); + + //---------------------------------------------------------------------------- + /// @brief Record a command to copy the contents of one texture to + /// another texture. + /// No work is encoded into the command buffer at this time. + /// + /// @param[in] source The texture to read for copying. + /// @param[in] destination The texture to overwrite using the source + /// contents. + /// @param[in] source_region The optional region of the source texture + /// to use for copying. If not specified, the + /// full size of the source texture is used. + /// @param[in] destination_origin The origin to start writing to in the + /// destination texture. + /// @param[in] label The optional debug label to give the + /// command. + /// + /// @return If the command was valid for subsequent commitment. + /// + bool AddCopy(std::shared_ptr source, + std::shared_ptr destination, + std::optional source_region = std::nullopt, + IPoint destination_origin = {}, + std::string label = ""); + + //---------------------------------------------------------------------------- + /// @brief Record a command to generate all mip levels for a texture. + /// No work is encoded into the command buffer at this time. + /// + /// @param[in] texture The texture to generate mipmaps for. + /// @param[in] label The optional debug label to give the command. + /// + /// @return If the command was valid for subsequent commitment. + /// + bool AddGenerateMipmaps(std::shared_ptr texture, + std::string label = ""); + + //---------------------------------------------------------------------------- + /// @brief Encode the recorded commands to the underlying command buffer. + /// + /// @param transients_allocator The transients allocator. + /// + /// @return If the commands were encoded to the underlying command + /// buffer. + /// + virtual bool EncodeCommands( + const std::shared_ptr& transients_allocator) const = 0; + + protected: + std::shared_ptr transients_buffer_; + std::vector commands_; + + explicit BlitPass(); + + virtual void OnSetLabel(std::string label) = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(BlitPass); +}; + +} // namespace impeller diff --git a/impeller/renderer/command_buffer.cc b/impeller/renderer/command_buffer.cc index 9b0dc620b4c67..75ff9e700f78a 100644 --- a/impeller/renderer/command_buffer.cc +++ b/impeller/renderer/command_buffer.cc @@ -27,4 +27,13 @@ std::shared_ptr CommandBuffer::CreateRenderPass( return nullptr; } +std::shared_ptr CommandBuffer::CreateBlitPass() const { + auto pass = OnCreateBlitPass(); + if (pass && pass->IsValid()) { + pass->SetLabel("BlitPass"); + return pass; + } + return nullptr; +} + } // namespace impeller diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 8d47ca148fe80..92328a0ae3197 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/renderer/blit_pass.h" namespace impeller { @@ -65,20 +66,29 @@ class CommandBuffer { //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. /// - /// @param[in] desc The description of the render target this pass will - /// target. + /// @param[in] render_target The description of the render target this pass + /// will target. /// /// @return A valid render pass or null. /// std::shared_ptr CreateRenderPass( RenderTarget render_target) const; + //---------------------------------------------------------------------------- + /// @brief Create a blit pass to record blit commands into. + /// + /// @return A valid blit pass or null. + /// + std::shared_ptr CreateBlitPass() const; + protected: CommandBuffer(); virtual std::shared_ptr OnCreateRenderPass( RenderTarget render_target) const = 0; + virtual std::shared_ptr OnCreateBlitPass() const = 0; + private: FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer); }; From 370d68bfa5918234d75fbdad162330c962a5cdf7 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 18:50:16 -0700 Subject: [PATCH 02/14] Use GLES3 blit if available --- impeller/renderer/backend/gles/BUILD.gn | 2 +- .../renderer/backend/gles/blit_pass_gles.cc | 91 ++++++++++++++++++- impeller/renderer/backend/gles/formats_gles.h | 1 - impeller/renderer/backend/gles/gles.h | 2 +- .../renderer/backend/gles/proc_table_gles.cc | 1 + .../renderer/backend/gles/proc_table_gles.h | 4 +- .../renderer/backend/gles/texture_gles.cc | 14 ++- impeller/renderer/backend/gles/texture_gles.h | 2 + 8 files changed, 105 insertions(+), 12 deletions(-) diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 4fdc6e6de15da..05627e2e1925d 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -62,8 +62,8 @@ impeller_component("gles") { if (!is_android && !is_fuchsia) { public_configs = [ ":gles_config" ] sources += [ - "//third_party/angle/include/GLES2/gl2.h", "//third_party/angle/include/GLES2/gl2ext.h", + "//third_party/angle/include/GLES3/gl3.h", ] } diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index e8e55d753ebf5..4e159771018c9 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -6,13 +6,13 @@ #include -#include "GLES2/gl2.h" #include "flutter/fml/trace_event.h" #include "impeller/base/config.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h" #include "impeller/renderer/backend/gles/pipeline_gles.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h" #include "impeller/renderer/formats.h" @@ -35,6 +35,56 @@ void BlitPassGLES::OnSetLabel(std::string label) { label_ = std::move(label); } +static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) { + if (fbo != GL_NONE) { + gl.BindFramebuffer(type, GL_NONE); + gl.DeleteFramebuffers(1u, &fbo); + } +}; + +static std::optional BindFBO(const ProcTableGLES& gl, + const std::shared_ptr& texture, + GLenum type) { + auto handle = TextureGLES::Cast(texture.get())->GetGLHandle(); + if (!handle.has_value()) { + return std::nullopt; + } + + if (TextureGLES::Cast(*texture).IsWrapped()) { + gl.BindFramebuffer(type, 0); + return 0; // The texture is attached to the default FBO. + } + + GLuint fbo; + gl.GenFramebuffers(1u, &fbo); + gl.BindFramebuffer(type, fbo); + switch (TextureGLES::Cast(*texture).GetType()) { + case TextureGLES::Type::kTexture: + gl.FramebufferTexture2D(type, // target + GL_COLOR_ATTACHMENT0, // attachment + GL_TEXTURE_2D, // textarget + handle.value(), // texture + 0 // level + ); + break; + case TextureGLES::Type::kRenderBuffer: + gl.FramebufferRenderbuffer(type, // target + GL_COLOR_ATTACHMENT0, // attachment + GL_RENDERBUFFER, // render-buffer target + handle.value() // render-buffer + ); + break; + } + + if (gl.CheckFramebufferStatus(type) != GL_FRAMEBUFFER_COMPLETE) { + VALIDATION_LOG << "Could not create a complete frambuffer."; + DeleteFBO(gl, fbo, type); + return std::nullopt; + } + + return fbo; +}; + [[nodiscard]] bool EncodeCommandsInReactor( const std::shared_ptr& transients_allocator, const ReactorGLES& reactor, @@ -67,14 +117,49 @@ void BlitPassGLES::OnSetLabel(std::string label) { if (auto* copy_command = std::get_if(&command.data)) { - if (!gl.BindFramebuffer.IsAvailable()) { + // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to + // emulate the blit when it's not available in the driver. + if (!gl.BlitFramebuffer.IsAvailable()) { // TODO(bdero): Emulate the blit using a raster draw call here. return true; } + + GLuint read_fbo = GL_NONE; + GLuint draw_fbo = GL_NONE; + fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() { + DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER); + DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER); + }); + + { + auto read = BindFBO(gl, copy_command->source, GL_READ_FRAMEBUFFER); + if (!read.has_value()) { + return false; + } + read_fbo = read.value(); + auto draw = BindFBO(gl, copy_command->destination, GL_DRAW_FRAMEBUFFER); + if (!draw.has_value()) { + return false; + } + read_fbo = read.value(); + draw_fbo = draw.value(); + } + gl.Disable(GL_SCISSOR_TEST); gl.Disable(GL_DEPTH_TEST); gl.Disable(GL_STENCIL_TEST); - auto source = TextureGLES::Cast(copy_command->source.get()); + + gl.BlitFramebuffer(copy_command->source_region.origin.x, // srcX0 + copy_command->source_region.origin.y, // srcY0 + copy_command->source_region.size.width, // srcX1 + copy_command->source_region.size.height, // srcY1 + copy_command->destination_origin.x, // dstX0 + copy_command->destination_origin.y, // dstY0 + copy_command->source_region.size.width, // dstX1 + copy_command->source_region.size.height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); } diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h index e20e2fd5bc4b4..fb5ce27e0a384 100644 --- a/impeller/renderer/backend/gles/formats_gles.h +++ b/impeller/renderer/backend/gles/formats_gles.h @@ -4,7 +4,6 @@ #include -#include "GLES2/gl2ext.h" #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "impeller/renderer/backend/gles/gles.h" diff --git a/impeller/renderer/backend/gles/gles.h b/impeller/renderer/backend/gles/gles.h index 2711c36642479..599e3c9433c97 100644 --- a/impeller/renderer/backend/gles/gles.h +++ b/impeller/renderer/backend/gles/gles.h @@ -4,6 +4,6 @@ #pragma once -#include "GLES2/gl2.h" +#include "GLES3/gl3.h" #define GL_GLEXT_PROTOTYPES #include "GLES2/gl2ext.h" diff --git a/impeller/renderer/backend/gles/proc_table_gles.cc b/impeller/renderer/backend/gles/proc_table_gles.cc index 95fbc84700fae..a01246ed23087 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.cc +++ b/impeller/renderer/backend/gles/proc_table_gles.cc @@ -87,6 +87,7 @@ ProcTableGLES::ProcTableGLES(Resolver resolver) { reinterpret_cast(fn_ptr); \ proc_ivar.error_fn = error_fn; \ } + FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC); FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC); #undef IMPELLER_PROC diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index c0a8769e85488..ee98d44e3cf09 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -93,7 +93,6 @@ struct GLProc { PROC(BindTexture); \ PROC(BlendEquationSeparate); \ PROC(BlendFuncSeparate); \ - PROC(BlitFramebuffer); \ PROC(BufferData); \ PROC(CheckFramebufferStatus); \ PROC(Clear); \ @@ -164,6 +163,8 @@ struct GLProc { PROC(VertexAttribPointer); \ PROC(Viewport); +#define FOR_EACH_IMPELLER_GLES3_PROC(PROC) PROC(BlitFramebuffer); + #define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ PROC(DiscardFramebufferEXT); \ PROC(PushDebugGroupKHR); \ @@ -190,6 +191,7 @@ class ProcTableGLES { GLProc name = {"gl" #name, nullptr}; FOR_EACH_IMPELLER_PROC(IMPELLER_PROC); + FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC); FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC); #undef IMPELLER_PROC diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index 634fcfcdcdf7e..ef09f59ce3e84 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -350,11 +350,15 @@ void TextureGLES::InitializeContentsIfNecessary() const { } } -bool TextureGLES::Bind() const { +std::optional TextureGLES::GetGLHandle() const { if (!IsValid()) { - return false; + return std::nullopt; } - auto handle = reactor_->GetGLHandle(handle_); + return reactor_->GetGLHandle(handle_); +} + +bool TextureGLES::Bind() const { + auto handle = GetGLHandle(); if (!handle.has_value()) { return false; } @@ -392,7 +396,7 @@ bool TextureGLES::GenerateMipmaps() const { return false; } - auto handle = reactor_->GetGLHandle(handle_); + auto handle = GetGLHandle(); if (!handle.has_value()) { return false; } @@ -423,7 +427,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo, return false; } InitializeContentsIfNecessary(); - auto handle = reactor_->GetGLHandle(handle_); + auto handle = GetGLHandle(); if (!handle.has_value()) { return false; } diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index cef3aec6a9f67..5f2e8706bb723 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -33,6 +33,8 @@ class TextureGLES final : public Texture, // |Texture| ~TextureGLES() override; + std::optional GetGLHandle() const; + [[nodiscard]] bool Bind() const; [[nodiscard]] bool GenerateMipmaps() const; From 88746ef8cc74d3c70158c1804abe7e80b021297e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 22:03:07 -0700 Subject: [PATCH 03/14] Add tests --- impeller/fixtures/BUILD.gn | 2 + impeller/fixtures/mipmaps.frag | 18 +++++ impeller/fixtures/mipmaps.vert | 18 +++++ impeller/playground/playground.cc | 4 +- impeller/playground/playground.h | 4 +- impeller/renderer/blit_pass.cc | 2 +- impeller/renderer/blit_pass.h | 2 +- impeller/renderer/renderer_unittests.cc | 89 +++++++++++++++++++++++++ 8 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 impeller/fixtures/mipmaps.frag create mode 100644 impeller/fixtures/mipmaps.vert diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 5d205a4f1464a..d955a90bfa9df 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -16,6 +16,8 @@ impeller_shaders("shader_fixtures") { "impeller.vert", "instanced_draw.frag", "instanced_draw.vert", + "mipmaps.frag", + "mipmaps.vert", "simple.vert", "test_texture.frag", "test_texture.vert", diff --git a/impeller/fixtures/mipmaps.frag b/impeller/fixtures/mipmaps.frag new file mode 100644 index 0000000000000..05114fdda4d91 --- /dev/null +++ b/impeller/fixtures/mipmaps.frag @@ -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 FragInfo { + float lod; +} +frag_info; + +uniform sampler2D tex; + +in vec2 v_uv; + +out vec4 frag_color; + +void main() { + frag_color = textureLod(tex, v_uv, frag_info.lod); +} diff --git a/impeller/fixtures/mipmaps.vert b/impeller/fixtures/mipmaps.vert new file mode 100644 index 0000000000000..ede92aacce0ae --- /dev/null +++ b/impeller/fixtures/mipmaps.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 VertInfo { + mat4 mvp; +} +vert_info; + +in vec3 vertex_position; +in vec2 uv; + +out vec2 v_uv; + +void main() { + gl_Position = vert_info.mvp * vec4(vertex_position, 1.0); + v_uv = uv; +} diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 1c757d16e80da..c89e5b47ee09d 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -334,7 +334,7 @@ std::optional Playground::LoadFixtureImageRGBA( } std::shared_ptr Playground::CreateTextureForFixture( - const char* fixture_name) const { + const char* fixture_name, size_t mip_count) const { auto image = LoadFixtureImageRGBA(fixture_name); if (!image.has_value()) { return nullptr; @@ -343,7 +343,7 @@ std::shared_ptr Playground::CreateTextureForFixture( auto texture_descriptor = TextureDescriptor{}; texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = image->GetSize(); - texture_descriptor.mip_count = 1u; + texture_descriptor.mip_count = mip_count; auto texture = renderer_->GetContext()->GetPermanentsAllocator()->CreateTexture( diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index f26447112f27b..2e7d4c5a4fcfa 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -56,8 +56,8 @@ class Playground : public ::testing::TestWithParam { std::optional LoadFixtureImageRGBA( const char* fixture_name) const; - std::shared_ptr CreateTextureForFixture( - const char* fixture_name) const; + std::shared_ptr CreateTextureForFixture(const char* fixture_name, + size_t mip_count = 1u) const; std::shared_ptr CreateTextureCubeForFixture( std::array fixture_names) const; diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index 480c573959aac..d1d215ec369e8 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -57,7 +57,7 @@ bool BlitPass::AddCopy(std::shared_ptr source, return true; } -bool BlitPass::AddGenerateMipmaps(std::shared_ptr texture, +bool BlitPass::GenerateMipmaps(std::shared_ptr texture, std::string label) { if (!texture) { VALIDATION_LOG << "Attempted to add an invalid mipmap generation command " diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index 08feb3c071d7f..f91cb1d773d24 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -66,7 +66,7 @@ class BlitPass { /// /// @return If the command was valid for subsequent commitment. /// - bool AddGenerateMipmaps(std::shared_ptr texture, + bool GenerateMipmaps(std::shared_ptr texture, std::string label = ""); //---------------------------------------------------------------------------- diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index bdb6dbf5aaae8..128585cb511d9 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -442,6 +442,95 @@ TEST_P(RendererTest, CanRenderInstanced) { } #endif // IMPELLER_ENABLE_METAL +TEST_P(RendererTest, CanBlit) { + auto context = GetContext(); + ASSERT_TRUE(context); + + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(bridge && boston); + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + Renderer::RenderCallback callback = [context = GetContext(), + &bridge](RenderTarget& render_target) { + auto buffer = context->CreateRenderCommandBuffer(); + if (!buffer) { + return false; + } + buffer->SetLabel("Playground Command Buffer"); + + auto pass = buffer->CreateBlitPass(); + if (!pass) { + return false; + } + pass->SetLabel("Playground Blit Pass"); + + if (render_target.GetColorAttachments().empty()) { + return false; + } + auto color0 = render_target.GetColorAttachments().find(0)->second; + + // Blit `bridge` to the top left corner of the on-screen texture. + pass->AddCopy(bridge, color0.texture); + + pass->EncodeCommands(context->GetTransientsAllocator()); + if (!buffer->SubmitCommands()) { + return false; + } + return true; + }; + OpenPlaygroundHere(callback); +} + +TEST_P(RendererTest, CanGenerateMipmaps) { + auto context = GetContext(); + ASSERT_TRUE(context); + + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(boston); + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + Renderer::RenderCallback callback = [context = GetContext(), + &boston](RenderTarget& render_target) { + auto buffer = context->CreateRenderCommandBuffer(); + if (!buffer) { + return false; + } + buffer->SetLabel("Playground Command Buffer"); + + { + auto pass = buffer->CreateBlitPass(); + if (!pass) { + return false; + } + pass->SetLabel("Playground Blit Pass"); + pass->GenerateMipmaps(boston, "Boston Mipmaps"); + pass->EncodeCommands(context->GetTransientsAllocator()); + } + + { + auto pass = buffer->CreateRenderPass(render_target); + if (!pass) { + return false; + } + pass->SetLabel("Playground Blit Pass"); + { + Command cmd; + pass->AddCommand(std::move(cmd)); + } + pass->EncodeCommands(context->GetTransientsAllocator()); + } + + if (!buffer->SubmitCommands()) { + return false; + } + return true; + }; + OpenPlaygroundHere(callback); +} + TEST_P(RendererTest, TheImpeller) { using VS = ImpellerVertexShader; using FS = ImpellerFragmentShader; From 9583329919d82120a79ea9d05a61a1b08cd3f7a8 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 22:07:22 -0700 Subject: [PATCH 04/14] Licenses --- ci/licenses_golden/licenses_flutter | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8338fce6e0928..bf7ca0a4e0fc1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -671,6 +671,8 @@ FILE: ../../../flutter/impeller/renderer/allocator.cc FILE: ../../../flutter/impeller/renderer/allocator.h FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/capabilities_gles.cc @@ -712,6 +714,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/context_mtl.h @@ -742,6 +746,8 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/vulkan/allocator_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/allocator_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/capabilities_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/command_buffer_vk.cc @@ -773,6 +779,10 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/texture_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/vertex_descriptor_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/vertex_descriptor_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/vk.h +FILE: ../../../flutter/impeller/renderer/blit_command.cc +FILE: ../../../flutter/impeller/renderer/blit_command.h +FILE: ../../../flutter/impeller/renderer/blit_pass.cc +FILE: ../../../flutter/impeller/renderer/blit_pass.h FILE: ../../../flutter/impeller/renderer/buffer.cc FILE: ../../../flutter/impeller/renderer/buffer.h FILE: ../../../flutter/impeller/renderer/buffer_view.cc From 67c40417d69d8d3d8b1ffa24b5fcaa33b5e95598 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 22:08:33 -0700 Subject: [PATCH 05/14] Format --- impeller/playground/playground.cc | 3 ++- impeller/renderer/blit_pass.cc | 2 +- impeller/renderer/blit_pass.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index c89e5b47ee09d..91e793064421c 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -334,7 +334,8 @@ std::optional Playground::LoadFixtureImageRGBA( } std::shared_ptr Playground::CreateTextureForFixture( - const char* fixture_name, size_t mip_count) const { + const char* fixture_name, + size_t mip_count) const { auto image = LoadFixtureImageRGBA(fixture_name); if (!image.has_value()) { return nullptr; diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index d1d215ec369e8..a06989508dbd6 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -58,7 +58,7 @@ bool BlitPass::AddCopy(std::shared_ptr source, } bool BlitPass::GenerateMipmaps(std::shared_ptr texture, - std::string label) { + std::string label) { if (!texture) { VALIDATION_LOG << "Attempted to add an invalid mipmap generation command " "with no texture."; diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index f91cb1d773d24..f7a499a19a8b6 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -67,7 +67,7 @@ class BlitPass { /// @return If the command was valid for subsequent commitment. /// bool GenerateMipmaps(std::shared_ptr texture, - std::string label = ""); + std::string label = ""); //---------------------------------------------------------------------------- /// @brief Encode the recorded commands to the underlying command buffer. From d8ae678114e5a197f4194eceb88c4af44c159764 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Jul 2022 23:12:46 -0700 Subject: [PATCH 06/14] Remainter of mipmap test --- impeller/fixtures/mipmaps.vert | 4 +- impeller/playground/playground.cc | 5 +- impeller/playground/playground.h | 5 +- impeller/renderer/renderer_unittests.cc | 69 +++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/impeller/fixtures/mipmaps.vert b/impeller/fixtures/mipmaps.vert index ede92aacce0ae..c31cb3049468c 100644 --- a/impeller/fixtures/mipmaps.vert +++ b/impeller/fixtures/mipmaps.vert @@ -7,12 +7,12 @@ uniform VertInfo { } vert_info; -in vec3 vertex_position; +in vec2 vertex_position; in vec2 uv; out vec2 v_uv; void main() { - gl_Position = vert_info.mvp * vec4(vertex_position, 1.0); + gl_Position = vert_info.mvp * vec4(vertex_position, 0.0, 1.0); v_uv = uv; } diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 91e793064421c..7bc06e5700a12 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -335,7 +335,7 @@ std::optional Playground::LoadFixtureImageRGBA( std::shared_ptr Playground::CreateTextureForFixture( const char* fixture_name, - size_t mip_count) const { + std::optional mip_count) const { auto image = LoadFixtureImageRGBA(fixture_name); if (!image.has_value()) { return nullptr; @@ -344,7 +344,8 @@ std::shared_ptr Playground::CreateTextureForFixture( auto texture_descriptor = TextureDescriptor{}; texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = image->GetSize(); - texture_descriptor.mip_count = mip_count; + texture_descriptor.mip_count = + mip_count.has_value() ? mip_count.value() : image->GetSize().MipCount(); auto texture = renderer_->GetContext()->GetPermanentsAllocator()->CreateTexture( diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 2e7d4c5a4fcfa..0a49412101c80 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -56,8 +56,9 @@ class Playground : public ::testing::TestWithParam { std::optional LoadFixtureImageRGBA( const char* fixture_name) const; - std::shared_ptr CreateTextureForFixture(const char* fixture_name, - size_t mip_count = 1u) const; + std::shared_ptr CreateTextureForFixture( + const char* fixture_name, + std::optional mip_count = 1u) const; std::shared_ptr CreateTextureCubeForFixture( std::array fixture_names) const; diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 128585cb511d9..3c133e84e4cd2 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" +#include "impeller/base/strings.h" #include "impeller/fixtures/box_fade.frag.h" #include "impeller/fixtures/box_fade.vert.h" #include "impeller/fixtures/colors.frag.h" @@ -12,6 +14,8 @@ #include "impeller/fixtures/impeller.vert.h" #include "impeller/fixtures/instanced_draw.frag.h" #include "impeller/fixtures/instanced_draw.vert.h" +#include "impeller/fixtures/mipmaps.frag.h" +#include "impeller/fixtures/mipmaps.vert.h" #include "impeller/fixtures/test_texture.frag.h" #include "impeller/fixtures/test_texture.vert.h" #include "impeller/geometry/path_builder.h" @@ -487,26 +491,65 @@ TEST_P(RendererTest, CanGenerateMipmaps) { auto context = GetContext(); ASSERT_TRUE(context); - auto boston = CreateTextureForFixture("boston.jpg"); + using VS = MipmapsVertexShader; + using FS = MipmapsFragmentShader; + auto desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(desc.has_value()); + desc->SetSampleCount(SampleCount::kCount4); + auto mipmaps_pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + ASSERT_TRUE(mipmaps_pipeline); + + auto boston = CreateTextureForFixture("boston.jpg", std::nullopt); ASSERT_TRUE(boston); auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); - Renderer::RenderCallback callback = [context = GetContext(), - &boston](RenderTarget& render_target) { + // Vertex buffer. + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + auto size = Point(boston->GetSize()); + vertex_builder.AddVertices({ + {{0, 0}, {0.0, 0.0}}, // 1 + {{size.x, 0}, {1.0, 0.0}}, // 2 + {{size.x, size.y}, {1.0, 1.0}}, // 3 + {{0, 0}, {0.0, 0.0}}, // 1 + {{size.x, size.y}, {1.0, 1.0}}, // 3 + {{0, size.y}, {0.0, 1.0}}, // 4 + }); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + ASSERT_TRUE(vertex_buffer); + + bool first_frame = true; + Renderer::RenderCallback callback = [&](RenderTarget& render_target) { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({400, 80}); + ImGui::SetNextWindowPos({20, 20}); + } + + static int lod = 0; + + ImGui::Begin("Controls"); + ImGui::SliderInt("LOD", &lod, 0, boston->GetSize().MipCount()); + ImGui::End(); + auto buffer = context->CreateRenderCommandBuffer(); if (!buffer) { return false; } buffer->SetLabel("Playground Command Buffer"); - { + if (first_frame) { auto pass = buffer->CreateBlitPass(); if (!pass) { return false; } pass->SetLabel("Playground Blit Pass"); + pass->GenerateMipmaps(boston, "Boston Mipmaps"); + pass->EncodeCommands(context->GetTransientsAllocator()); } @@ -518,6 +561,24 @@ TEST_P(RendererTest, CanGenerateMipmaps) { pass->SetLabel("Playground Blit Pass"); { Command cmd; + cmd.label = SPrintF("Image LOD %d", lod); + cmd.pipeline = mipmaps_pipeline; + + cmd.BindVertices(vertex_buffer); + + VS::VertInfo vert_info; + vert_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + VS::BindVertInfo(cmd, + pass->GetTransientsBuffer().EmplaceUniform(vert_info)); + + FS::FragInfo frag_info; + frag_info.lod = lod; + FS::BindFragInfo(cmd, + pass->GetTransientsBuffer().EmplaceUniform(frag_info)); + + FS::BindTex(cmd, boston, sampler); + pass->AddCommand(std::move(cmd)); } pass->EncodeCommands(context->GetTransientsAllocator()); From d67ae154a5e91763d137ebe022be6d1ffaf67b37 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 00:27:48 -0700 Subject: [PATCH 07/14] Mip count convenience --- impeller/renderer/blit_pass.cc | 4 ++-- impeller/renderer/blit_pass.h | 3 +-- impeller/renderer/renderer_unittests.cc | 10 +++++----- impeller/renderer/texture.cc | 4 ++++ impeller/renderer/texture.h | 2 ++ 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index a06989508dbd6..3f18902c9626c 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -57,8 +57,8 @@ bool BlitPass::AddCopy(std::shared_ptr source, return true; } -bool BlitPass::GenerateMipmaps(std::shared_ptr texture, - std::string label) { +bool BlitPass::GenerateMipmap(std::shared_ptr texture, + std::string label) { if (!texture) { VALIDATION_LOG << "Attempted to add an invalid mipmap generation command " "with no texture."; diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index f7a499a19a8b6..a35e86f5582ec 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -66,8 +66,7 @@ class BlitPass { /// /// @return If the command was valid for subsequent commitment. /// - bool GenerateMipmaps(std::shared_ptr texture, - std::string label = ""); + bool GenerateMipmap(std::shared_ptr texture, std::string label = ""); //---------------------------------------------------------------------------- /// @brief Encode the recorded commands to the underlying command buffer. diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 3c133e84e4cd2..342b521ddef07 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" #include "impeller/base/strings.h" @@ -524,15 +523,14 @@ TEST_P(RendererTest, CanGenerateMipmaps) { bool first_frame = true; Renderer::RenderCallback callback = [&](RenderTarget& render_target) { if (first_frame) { - first_frame = false; ImGui::SetNextWindowSize({400, 80}); ImGui::SetNextWindowPos({20, 20}); } - static int lod = 0; + static int lod = 4; ImGui::Begin("Controls"); - ImGui::SliderInt("LOD", &lod, 0, boston->GetSize().MipCount()); + ImGui::SliderInt("LOD", &lod, 0, boston->GetMipCount() - 1); ImGui::End(); auto buffer = context->CreateRenderCommandBuffer(); @@ -548,11 +546,13 @@ TEST_P(RendererTest, CanGenerateMipmaps) { } pass->SetLabel("Playground Blit Pass"); - pass->GenerateMipmaps(boston, "Boston Mipmaps"); + pass->GenerateMipmap(boston, "Boston Mipmap"); pass->EncodeCommands(context->GetTransientsAllocator()); } + first_frame = false; + { auto pass = buffer->CreateRenderPass(render_target); if (!pass) { diff --git a/impeller/renderer/texture.cc b/impeller/renderer/texture.cc index 3b63a02065904..4a2a335b4709f 100644 --- a/impeller/renderer/texture.cc +++ b/impeller/renderer/texture.cc @@ -42,6 +42,10 @@ bool Texture::SetContents(std::shared_ptr mapping, return true; } +size_t Texture::GetMipCount() const { + return GetTextureDescriptor().mip_count; +} + const TextureDescriptor& Texture::GetTextureDescriptor() const { return desc_; } diff --git a/impeller/renderer/texture.h b/impeller/renderer/texture.h index 9d71147582f03..6ddb0dcde5ce9 100644 --- a/impeller/renderer/texture.h +++ b/impeller/renderer/texture.h @@ -31,6 +31,8 @@ class Texture { virtual ISize GetSize() const = 0; + size_t GetMipCount() const; + const TextureDescriptor& GetTextureDescriptor() const; TextureIntent GetIntent() const; From 552d9120bdd781c793480cc5e49cec2d58c86997 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 01:55:58 -0700 Subject: [PATCH 08/14] Add mip filter setting to sampler --- .../display_list/display_list_dispatcher.cc | 6 ++++ .../renderer/backend/gles/sampler_gles.cc | 34 ++++++++++++++----- impeller/renderer/backend/metal/formats_mtl.h | 12 +++++++ .../backend/metal/sampler_library_mtl.mm | 1 + impeller/renderer/formats.h | 10 ++++++ impeller/renderer/renderer_unittests.cc | 19 ++++++++--- impeller/renderer/sampler_descriptor.h | 7 ++-- 7 files changed, 74 insertions(+), 15 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index f8e2a2ddffea8..b06e0ca57a776 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -22,6 +22,7 @@ #include "impeller/geometry/path_builder.h" #include "impeller/geometry/scalar.h" #include "impeller/geometry/vertices.h" +#include "impeller/renderer/formats.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" @@ -778,6 +779,11 @@ static impeller::SamplerDescriptor ToSamplerDescriptor( desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; desc.label = "Linear Sampler"; break; + case flutter::DlImageSampling::kMipmapLinear: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; + desc.mip_filter = impeller::MipFilter::kLinear; + desc.label = "Mipmap Linear Sampler"; + break; default: break; } diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc index d9b3fba473bb8..030bdda8007f8 100644 --- a/impeller/renderer/backend/gles/sampler_gles.cc +++ b/impeller/renderer/backend/gles/sampler_gles.cc @@ -7,6 +7,7 @@ #include "impeller/renderer/backend/gles/formats_gles.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h" +#include "impeller/renderer/formats.h" namespace impeller { @@ -18,12 +19,29 @@ bool SamplerGLES::IsValid() const { return true; } -static GLint ToParam(MinMagFilter filter) { - switch (filter) { - case MinMagFilter::kNearest: - return GL_NEAREST; - case MinMagFilter::kLinear: - return GL_LINEAR; +static GLint ToParam(MinMagFilter minmag_filter, MipFilter mip_filter) { + switch (mip_filter) { + case MipFilter::kNone: + switch (minmag_filter) { + case MinMagFilter::kNearest: + return GL_NEAREST; + case MinMagFilter::kLinear: + return GL_LINEAR; + } + case MipFilter::kNearest: + switch (minmag_filter) { + case MinMagFilter::kNearest: + return GL_NEAREST_MIPMAP_NEAREST; + case MinMagFilter::kLinear: + return GL_NEAREST_MIPMAP_LINEAR; + } + case MipFilter::kLinear: + switch (minmag_filter) { + case MinMagFilter::kNearest: + return GL_LINEAR_MIPMAP_NEAREST; + case MinMagFilter::kLinear: + return GL_LINEAR_MIPMAP_LINEAR; + } } FML_UNREACHABLE(); } @@ -54,9 +72,9 @@ bool SamplerGLES::ConfigureBoundTexture(const TextureGLES& texture, const auto& desc = GetDescriptor(); gl.TexParameteri(target.value(), GL_TEXTURE_MIN_FILTER, - ToParam(desc.min_filter)); + ToParam(desc.min_filter, desc.mip_filter)); gl.TexParameteri(target.value(), GL_TEXTURE_MAG_FILTER, - ToParam(desc.mag_filter)); + ToParam(desc.mag_filter, desc.mip_filter)); gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_S, ToAddressMode(desc.width_address_mode)); gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_T, diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 4099007385f4a..ef9bdc31fe0fc 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -275,6 +275,18 @@ constexpr MTLSamplerMinMagFilter ToMTLSamplerMinMagFilter(MinMagFilter filter) { return MTLSamplerMinMagFilterNearest; } +constexpr MTLSamplerMipFilter ToMTLSamplerMipFilter(MipFilter filter) { + switch (filter) { + case MipFilter::kNone: + return MTLSamplerMipFilterNotMipmapped; + case MipFilter::kNearest: + return MTLSamplerMipFilterNearest; + case MipFilter::kLinear: + return MTLSamplerMipFilterLinear; + } + return MTLSamplerMipFilterNotMipmapped; +} + constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode( SamplerAddressMode mode) { switch (mode) { diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.mm b/impeller/renderer/backend/metal/sampler_library_mtl.mm index 781303c7d789a..c9099c509e646 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.mm +++ b/impeller/renderer/backend/metal/sampler_library_mtl.mm @@ -25,6 +25,7 @@ auto desc = [[MTLSamplerDescriptor alloc] init]; desc.minFilter = ToMTLSamplerMinMagFilter(descriptor.min_filter); desc.magFilter = ToMTLSamplerMinMagFilter(descriptor.mag_filter); + desc.mipFilter = ToMTLSamplerMipFilter(descriptor.mip_filter); desc.sAddressMode = ToMTLSamplerAddressMode(descriptor.width_address_mode); desc.tAddressMode = ToMTLSamplerAddressMode(descriptor.height_address_mode); desc.rAddressMode = ToMTLSamplerAddressMode(descriptor.depth_address_mode); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 126a569b35287..ebf318f42ca26 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -199,6 +199,16 @@ enum class MinMagFilter { kLinear, }; +enum class MipFilter { + /// Always sample from mip level 0. Other mip levels are ignored. + kNone, + /// Sample from the nearest mip level. + kNearest, + /// Sample from the two nearest mip levels and linearly interpolate between + /// them. + kLinear, +}; + enum class SamplerAddressMode { kClampToEdge, kRepeat, diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 342b521ddef07..2ae5642b30984 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -501,8 +501,6 @@ TEST_P(RendererTest, CanGenerateMipmaps) { auto boston = CreateTextureForFixture("boston.jpg", std::nullopt); ASSERT_TRUE(boston); - auto sampler = context->GetSamplerLibrary()->GetSampler({}); - ASSERT_TRUE(sampler); // Vertex buffer. VertexBufferBuilder vertex_builder; @@ -527,10 +525,18 @@ TEST_P(RendererTest, CanGenerateMipmaps) { ImGui::SetNextWindowPos({20, 20}); } - static int lod = 4; + const char* mip_filter_names[] = {"None", "Nearest", "Linear"}; + const MipFilter mip_filters[] = {MipFilter::kNone, MipFilter::kNearest, + MipFilter::kLinear}; + + // UI state. + static int selected_mip_filter = 2; + static float lod = 4; ImGui::Begin("Controls"); - ImGui::SliderInt("LOD", &lod, 0, boston->GetMipCount() - 1); + ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names, + sizeof(mip_filter_names) / sizeof(char*)); + ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1); ImGui::End(); auto buffer = context->CreateRenderCommandBuffer(); @@ -561,7 +567,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { pass->SetLabel("Playground Blit Pass"); { Command cmd; - cmd.label = SPrintF("Image LOD %d", lod); + cmd.label = "Image LOD"; cmd.pipeline = mipmaps_pipeline; cmd.BindVertices(vertex_buffer); @@ -577,6 +583,9 @@ TEST_P(RendererTest, CanGenerateMipmaps) { FS::BindFragInfo(cmd, pass->GetTransientsBuffer().EmplaceUniform(frag_info)); + SamplerDescriptor sampler_desc; + sampler_desc.mip_filter = mip_filters[selected_mip_filter]; + auto sampler = context->GetSamplerLibrary()->GetSampler(sampler_desc); FS::BindTex(cmd, boston, sampler); pass->AddCommand(std::move(cmd)); diff --git a/impeller/renderer/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h index d028f203fc37c..6ebb96e4e70cf 100644 --- a/impeller/renderer/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -18,6 +18,7 @@ class Context; struct SamplerDescriptor final : public Comparable { MinMagFilter min_filter = MinMagFilter::kNearest; MinMagFilter mag_filter = MinMagFilter::kNearest; + MipFilter mip_filter = MipFilter::kNone; SamplerAddressMode width_address_mode = SamplerAddressMode::kClampToEdge; SamplerAddressMode height_address_mode = SamplerAddressMode::kClampToEdge; @@ -27,13 +28,15 @@ struct SamplerDescriptor final : public Comparable { // Comparable std::size_t GetHash() const override { - return fml::HashCombine(min_filter, mag_filter, width_address_mode, - height_address_mode, depth_address_mode); + return fml::HashCombine(min_filter, mag_filter, mip_filter, + width_address_mode, height_address_mode, + depth_address_mode); } // Comparable bool IsEqual(const SamplerDescriptor& o) const override { return min_filter == o.min_filter && mag_filter == o.mag_filter && + mip_filter == o.mip_filter && width_address_mode == o.width_address_mode && height_address_mode == o.height_address_mode && depth_address_mode == o.depth_address_mode; From c532288ae3a899c827a41adb4df3ea8d660f742c Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 02:16:44 -0700 Subject: [PATCH 09/14] Flip mip filter enums to correct direction, add min filter to mipmap playground --- impeller/renderer/backend/gles/sampler_gles.cc | 6 +++--- impeller/renderer/renderer_unittests.cc | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc index 030bdda8007f8..72fea36726f95 100644 --- a/impeller/renderer/backend/gles/sampler_gles.cc +++ b/impeller/renderer/backend/gles/sampler_gles.cc @@ -33,12 +33,12 @@ static GLint ToParam(MinMagFilter minmag_filter, MipFilter mip_filter) { case MinMagFilter::kNearest: return GL_NEAREST_MIPMAP_NEAREST; case MinMagFilter::kLinear: - return GL_NEAREST_MIPMAP_LINEAR; + return GL_LINEAR_MIPMAP_NEAREST; } case MipFilter::kLinear: switch (minmag_filter) { case MinMagFilter::kNearest: - return GL_LINEAR_MIPMAP_NEAREST; + return GL_NEAREST_MIPMAP_LINEAR; case MinMagFilter::kLinear: return GL_LINEAR_MIPMAP_LINEAR; } @@ -74,7 +74,7 @@ bool SamplerGLES::ConfigureBoundTexture(const TextureGLES& texture, gl.TexParameteri(target.value(), GL_TEXTURE_MIN_FILTER, ToParam(desc.min_filter, desc.mip_filter)); gl.TexParameteri(target.value(), GL_TEXTURE_MAG_FILTER, - ToParam(desc.mag_filter, desc.mip_filter)); + ToParam(desc.mag_filter, MipFilter::kNone)); gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_S, ToAddressMode(desc.width_address_mode)); gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_T, diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 2ae5642b30984..f1e438b29a4f3 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -521,21 +521,26 @@ TEST_P(RendererTest, CanGenerateMipmaps) { bool first_frame = true; Renderer::RenderCallback callback = [&](RenderTarget& render_target) { if (first_frame) { - ImGui::SetNextWindowSize({400, 80}); - ImGui::SetNextWindowPos({20, 20}); + ImGui::SetNextWindowPos({10, 10}); } const char* mip_filter_names[] = {"None", "Nearest", "Linear"}; const MipFilter mip_filters[] = {MipFilter::kNone, MipFilter::kNearest, MipFilter::kLinear}; + const char* min_filter_names[] = {"Nearest", "Linear"}; + const MinMagFilter min_filters[] = {MinMagFilter::kNearest, + MinMagFilter::kLinear}; // UI state. static int selected_mip_filter = 2; - static float lod = 4; + static int selected_min_filter = 0; + static float lod = 4.5; - ImGui::Begin("Controls"); + ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names, sizeof(mip_filter_names) / sizeof(char*)); + ImGui::Combo("Min filter", &selected_min_filter, min_filter_names, + sizeof(min_filter_names) / sizeof(char*)); ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1); ImGui::End(); @@ -585,6 +590,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { SamplerDescriptor sampler_desc; sampler_desc.mip_filter = mip_filters[selected_mip_filter]; + sampler_desc.min_filter = min_filters[selected_min_filter]; auto sampler = context->GetSamplerLibrary()->GetSampler(sampler_desc); FS::BindTex(cmd, boston, sampler); From ffca873714f7412cbedfc701bf1b10fea524cad1 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 04:46:54 -0700 Subject: [PATCH 10/14] Clip to source/destination image --- .../renderer/backend/metal/blit_pass_mtl.mm | 2 +- impeller/renderer/blit_pass.cc | 35 ++++++- impeller/renderer/blit_pass.h | 4 +- impeller/renderer/renderer_unittests.cc | 99 ++++++++++++++++--- 4 files changed, 118 insertions(+), 22 deletions(-) diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm index a7e0380415b8a..fee37791a2e87 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.mm +++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm @@ -85,7 +85,7 @@ } auto destination = - TextureMTL::Cast(*copy_command->source).GetMTLTexture(); + TextureMTL::Cast(*copy_command->destination).GetMTLTexture(); if (!destination) { return false; } diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index 3f18902c9626c..3fa72f1f23f48 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -34,23 +34,48 @@ bool BlitPass::AddCopy(std::shared_ptr source, IPoint destination_origin, std::string label) { if (!source) { - VALIDATION_LOG << "Attempted to add an invalid copy with no source."; + VALIDATION_LOG << "Attempted to add a texture blit with no source."; return false; } if (!destination) { - VALIDATION_LOG << "Attempted to add an invalid copy with no destination."; + VALIDATION_LOG << "Attempted to add a texture blit with no destination."; return false; } + if (source->GetTextureDescriptor().sample_count != + destination->GetTextureDescriptor().sample_count) { + VALIDATION_LOG << SPrintF( + "The source sample count (%d) must match the destination sample count " + "(%d) for blits.", + source->GetTextureDescriptor().sample_count, + destination->GetTextureDescriptor().sample_count); + } + + if (!source_region.has_value()) { + source_region = IRect::MakeSize(source->GetSize()); + } + + // Clip the source image. + source_region = + source_region->Intersection(IRect::MakeSize(source->GetSize())); + if (!source_region.has_value()) { + return true; // Nothing to blit. + } + + // Clip the destination image. + source_region = source_region->Intersection( + IRect(-destination_origin, destination->GetSize())); + if (!source_region.has_value()) { + return true; // Nothing to blit. + } + commands_.emplace_back(BlitCommand{ .label = label, .data = BlitCommand::CopyTextureToTexture{ .source = source, .destination = destination, - .source_region = source_region.has_value() - ? source_region.value() - : IRect::MakeSize(source->GetSize()), + .source_region = source_region.value(), .destination_origin = destination_origin, }, }); diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index a35e86f5582ec..4d2c9a481ae8c 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -35,7 +35,9 @@ class BlitPass { //---------------------------------------------------------------------------- /// @brief Record a command to copy the contents of one texture to - /// another texture. + /// another texture. The blit area is limited by the intersection + /// of the texture coverage with respect the source region and + /// destination origin. /// No work is encoded into the command buffer at this time. /// /// @param[in] source The texture to read for copying. diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index f1e438b29a4f3..b93e060c0e841 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -445,39 +445,108 @@ TEST_P(RendererTest, CanRenderInstanced) { } #endif // IMPELLER_ENABLE_METAL -TEST_P(RendererTest, CanBlit) { +TEST_P(RendererTest, CanBlitTextureToTexture) { auto context = GetContext(); ASSERT_TRUE(context); + using VS = MipmapsVertexShader; + using FS = MipmapsFragmentShader; + auto desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(desc.has_value()); + desc->SetSampleCount(SampleCount::kCount4); + auto mipmaps_pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + ASSERT_TRUE(mipmaps_pipeline); + + TextureDescriptor texture_desc; + texture_desc.format = PixelFormat::kR8G8B8A8UNormInt; + texture_desc.size = {800, 600}; + texture_desc.mip_count = 1u; + texture_desc.usage = + static_cast(TextureUsage::kRenderTarget) | + static_cast(TextureUsage::kShaderRead); + auto texture = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kHostVisible, texture_desc); + ASSERT_TRUE(texture); + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); auto boston = CreateTextureForFixture("boston.jpg"); ASSERT_TRUE(bridge && boston); auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); - Renderer::RenderCallback callback = [context = GetContext(), - &bridge](RenderTarget& render_target) { + // Vertex buffer. + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + auto size = Point(boston->GetSize()); + vertex_builder.AddVertices({ + {{0, 0}, {0.0, 0.0}}, // 1 + {{size.x, 0}, {1.0, 0.0}}, // 2 + {{size.x, size.y}, {1.0, 1.0}}, // 3 + {{0, 0}, {0.0, 0.0}}, // 1 + {{size.x, size.y}, {1.0, 1.0}}, // 3 + {{0, size.y}, {0.0, 1.0}}, // 4 + }); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetTransientsAllocator()); + ASSERT_TRUE(vertex_buffer); + + Renderer::RenderCallback callback = [&](RenderTarget& render_target) { auto buffer = context->CreateRenderCommandBuffer(); if (!buffer) { return false; } buffer->SetLabel("Playground Command Buffer"); - auto pass = buffer->CreateBlitPass(); - if (!pass) { - return false; - } - pass->SetLabel("Playground Blit Pass"); + { + auto pass = buffer->CreateBlitPass(); + if (!pass) { + return false; + } + pass->SetLabel("Playground Blit Pass"); - if (render_target.GetColorAttachments().empty()) { - return false; + if (render_target.GetColorAttachments().empty()) { + return false; + } + + // Blit `bridge` to the top left corner of the texture. + pass->AddCopy(bridge, texture); + + pass->EncodeCommands(context->GetTransientsAllocator()); } - auto color0 = render_target.GetColorAttachments().find(0)->second; - // Blit `bridge` to the top left corner of the on-screen texture. - pass->AddCopy(bridge, color0.texture); + { + auto pass = buffer->CreateRenderPass(render_target); + if (!pass) { + return false; + } + pass->SetLabel("Playground Render Pass"); + { + Command cmd; + cmd.label = "Image"; + cmd.pipeline = mipmaps_pipeline; + + cmd.BindVertices(vertex_buffer); + + VS::VertInfo vert_info; + vert_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) * + Matrix::MakeScale(GetContentScale()); + VS::BindVertInfo(cmd, + pass->GetTransientsBuffer().EmplaceUniform(vert_info)); + + FS::FragInfo frag_info; + frag_info.lod = 0; + FS::BindFragInfo(cmd, + pass->GetTransientsBuffer().EmplaceUniform(frag_info)); + + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + FS::BindTex(cmd, texture, sampler); + + pass->AddCommand(std::move(cmd)); + } + pass->EncodeCommands(context->GetTransientsAllocator()); + } - pass->EncodeCommands(context->GetTransientsAllocator()); if (!buffer->SubmitCommands()) { return false; } @@ -569,7 +638,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { if (!pass) { return false; } - pass->SetLabel("Playground Blit Pass"); + pass->SetLabel("Playground Render Pass"); { Command cmd; cmd.label = "Image LOD"; From 974238163db9e1b3511d473996e79253d5c07a88 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 14:41:28 -0700 Subject: [PATCH 11/14] Get GLES3 blits working --- impeller/renderer/backend/gles/BUILD.gn | 3 ++ .../renderer/backend/gles/blit_pass_gles.cc | 50 +++++++++---------- .../renderer/backend/gles/render_pass_gles.cc | 6 +-- .../renderer/backend/gles/texture_gles.cc | 20 +++++--- impeller/renderer/backend/gles/texture_gles.h | 3 +- impeller/renderer/blit_pass.cc | 1 + 6 files changed, 45 insertions(+), 38 deletions(-) diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 05627e2e1925d..3d9f7d0abf46b 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -63,6 +63,9 @@ impeller_component("gles") { public_configs = [ ":gles_config" ] sources += [ "//third_party/angle/include/GLES2/gl2ext.h", + + # The GLES3 API is a superset of GLES2. Although we target GLES2, we use + # some GLES3 features if the driver supports them. "//third_party/angle/include/GLES3/gl3.h", ] } diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index 4e159771018c9..efe9299deb65c 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -42,43 +42,36 @@ static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) { } }; -static std::optional BindFBO(const ProcTableGLES& gl, - const std::shared_ptr& texture, - GLenum type) { +static std::optional ConfigureFBO( + const ProcTableGLES& gl, + const std::shared_ptr& texture, + GLenum fbo_type) { auto handle = TextureGLES::Cast(texture.get())->GetGLHandle(); if (!handle.has_value()) { return std::nullopt; } if (TextureGLES::Cast(*texture).IsWrapped()) { - gl.BindFramebuffer(type, 0); - return 0; // The texture is attached to the default FBO. + // The texture is attached to the default FBO, so there's no need to + // create/configure one. + gl.BindFramebuffer(fbo_type, 0); + return 0; } GLuint fbo; gl.GenFramebuffers(1u, &fbo); - gl.BindFramebuffer(type, fbo); - switch (TextureGLES::Cast(*texture).GetType()) { - case TextureGLES::Type::kTexture: - gl.FramebufferTexture2D(type, // target - GL_COLOR_ATTACHMENT0, // attachment - GL_TEXTURE_2D, // textarget - handle.value(), // texture - 0 // level - ); - break; - case TextureGLES::Type::kRenderBuffer: - gl.FramebufferRenderbuffer(type, // target - GL_COLOR_ATTACHMENT0, // attachment - GL_RENDERBUFFER, // render-buffer target - handle.value() // render-buffer - ); - break; + gl.BindFramebuffer(fbo_type, fbo); + + if (!TextureGLES::Cast(*texture).SetAsFramebufferAttachment( + fbo_type, fbo, TextureGLES::AttachmentPoint::kColor0)) { + VALIDATION_LOG << "Could not attach texture to framebuffer."; + DeleteFBO(gl, fbo, fbo_type); + return std::nullopt; } - if (gl.CheckFramebufferStatus(type) != GL_FRAMEBUFFER_COMPLETE) { + if (gl.CheckFramebufferStatus(fbo_type) != GL_FRAMEBUFFER_COMPLETE) { VALIDATION_LOG << "Could not create a complete frambuffer."; - DeleteFBO(gl, fbo, type); + DeleteFBO(gl, fbo, fbo_type); return std::nullopt; } @@ -132,16 +125,19 @@ static std::optional BindFBO(const ProcTableGLES& gl, }); { - auto read = BindFBO(gl, copy_command->source, GL_READ_FRAMEBUFFER); + auto read = ConfigureFBO(gl, copy_command->source, GL_READ_FRAMEBUFFER); if (!read.has_value()) { return false; } read_fbo = read.value(); - auto draw = BindFBO(gl, copy_command->destination, GL_DRAW_FRAMEBUFFER); + } + + { + auto draw = + ConfigureFBO(gl, copy_command->destination, GL_DRAW_FRAMEBUFFER); if (!draw.has_value()) { return false; } - read_fbo = read.value(); draw_fbo = draw.value(); } diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index c0392239f6e2b..3dae613f4ad31 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -174,19 +174,19 @@ struct RenderPassData { if (auto color = TextureGLES::Cast(pass_data.color_attachment.get())) { if (!color->SetAsFramebufferAttachment( - fbo, TextureGLES::AttachmentPoint::kColor0)) { + GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kColor0)) { return false; } } if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) { if (!depth->SetAsFramebufferAttachment( - fbo, TextureGLES::AttachmentPoint::kDepth)) { + GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kDepth)) { return false; } } if (auto stencil = TextureGLES::Cast(pass_data.stencil_attachment.get())) { if (!stencil->SetAsFramebufferAttachment( - fbo, TextureGLES::AttachmentPoint::kStencil)) { + GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kStencil)) { return false; } } diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index ef09f59ce3e84..f88bd377dacb2 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -386,10 +386,15 @@ bool TextureGLES::GenerateMipmaps() const { } auto type = GetTextureDescriptor().type; - if (type != TextureType::kTexture2D && type != TextureType::kTextureCube) { - VALIDATION_LOG << "Could not generate mipmaps for texture type. Only " - "Texture2D and TextureCube are supported for GLES."; - return false; + switch (type) { + case TextureType::kTexture2D: + break; + case TextureType::kTexture2DMultisample: + VALIDATION_LOG << "Generating mipmaps for multisample textures is not " + "supported in the GLES backend."; + return false; + case TextureType::kTextureCube: + break; } if (!Bind()) { @@ -421,7 +426,8 @@ static GLenum ToAttachmentPoint(TextureGLES::AttachmentPoint point) { } } -bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo, +bool TextureGLES::SetAsFramebufferAttachment(GLenum target, + GLuint fbo, AttachmentPoint point) const { if (!IsValid()) { return false; @@ -434,7 +440,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo, const auto& gl = reactor_->GetProcTable(); switch (type_) { case Type::kTexture: - gl.FramebufferTexture2D(GL_FRAMEBUFFER, // target + gl.FramebufferTexture2D(target, // target ToAttachmentPoint(point), // attachment GL_TEXTURE_2D, // textarget handle.value(), // texture @@ -442,7 +448,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo, ); break; case Type::kRenderBuffer: - gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, // target + gl.FramebufferRenderbuffer(target, // target ToAttachmentPoint(point), // attachment GL_RENDERBUFFER, // render-buffer target handle.value() // render-buffer diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index 5f2e8706bb723..03c8b5cbcfdd7 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -44,7 +44,8 @@ class TextureGLES final : public Texture, kDepth, kStencil, }; - [[nodiscard]] bool SetAsFramebufferAttachment(GLuint fbo, + [[nodiscard]] bool SetAsFramebufferAttachment(GLenum target, + GLuint fbo, AttachmentPoint point) const; Type GetType() const; diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index 3fa72f1f23f48..e24aea797fdbc 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -49,6 +49,7 @@ bool BlitPass::AddCopy(std::shared_ptr source, "(%d) for blits.", source->GetTextureDescriptor().sample_count, destination->GetTextureDescriptor().sample_count); + return false; } if (!source_region.has_value()) { From d5fad8e564b3769eb0aa77c377ff2eed45b7359a Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Jul 2022 18:31:39 -0700 Subject: [PATCH 12/14] Mipmap flag for playground tests --- impeller/playground/playground.cc | 4 ++-- impeller/playground/playground.h | 2 +- impeller/renderer/renderer_unittests.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 7bc06e5700a12..d2058ef6afa6a 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -335,7 +335,7 @@ std::optional Playground::LoadFixtureImageRGBA( std::shared_ptr Playground::CreateTextureForFixture( const char* fixture_name, - std::optional mip_count) const { + bool enable_mipmapping) const { auto image = LoadFixtureImageRGBA(fixture_name); if (!image.has_value()) { return nullptr; @@ -345,7 +345,7 @@ std::shared_ptr Playground::CreateTextureForFixture( texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = image->GetSize(); texture_descriptor.mip_count = - mip_count.has_value() ? mip_count.value() : image->GetSize().MipCount(); + enable_mipmapping ? image->GetSize().MipCount() : 1u; auto texture = renderer_->GetContext()->GetPermanentsAllocator()->CreateTexture( diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 0a49412101c80..d5f8eb6a652d9 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -58,7 +58,7 @@ class Playground : public ::testing::TestWithParam { std::shared_ptr CreateTextureForFixture( const char* fixture_name, - std::optional mip_count = 1u) const; + bool enable_mipmapping = false) const; std::shared_ptr CreateTextureCubeForFixture( std::array fixture_names) const; diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index b93e060c0e841..f473a286dd8ea 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -568,7 +568,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(mipmaps_pipeline); - auto boston = CreateTextureForFixture("boston.jpg", std::nullopt); + auto boston = CreateTextureForFixture("boston.jpg", true); ASSERT_TRUE(boston); // Vertex buffer. From 2b892cf36f333b3c9be14fa1aec1e5c5d0df8947 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 27 Jul 2022 04:36:19 -0700 Subject: [PATCH 13/14] Refactor blit commands into subclasses --- ci/licenses_golden/licenses_flutter | 4 + impeller/renderer/backend/gles/BUILD.gn | 2 + .../backend/gles/blit_command_gles.cc | 134 +++++++++++++++++ .../renderer/backend/gles/blit_command_gles.h | 41 +++++ .../renderer/backend/gles/blit_pass_gles.cc | 140 +++++------------- .../renderer/backend/gles/blit_pass_gles.h | 13 ++ impeller/renderer/backend/metal/BUILD.gn | 2 + .../renderer/backend/metal/blit_command_mtl.h | 43 ++++++ .../backend/metal/blit_command_mtl.mm | 70 +++++++++ .../renderer/backend/metal/blit_pass_mtl.h | 19 ++- .../renderer/backend/metal/blit_pass_mtl.mm | 78 +++++----- .../renderer/backend/vulkan/blit_pass_vk.h | 11 ++ impeller/renderer/blit_command.h | 26 ++-- impeller/renderer/blit_pass.cc | 21 +-- impeller/renderer/blit_pass.h | 11 +- 15 files changed, 431 insertions(+), 184 deletions(-) create mode 100644 impeller/renderer/backend/gles/blit_command_gles.cc create mode 100644 impeller/renderer/backend/gles/blit_command_gles.h create mode 100644 impeller/renderer/backend/metal/blit_command_mtl.h create mode 100644 impeller/renderer/backend/metal/blit_command_mtl.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index bf7ca0a4e0fc1..0bad4d89e830d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -671,6 +671,8 @@ FILE: ../../../flutter/impeller/renderer/allocator.cc FILE: ../../../flutter/impeller/renderer/allocator.h FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/blit_command_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/blit_command_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc @@ -714,6 +716,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/blit_command_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/blit_command_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.h diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn index 3d9f7d0abf46b..7141abc3304ed 100644 --- a/impeller/renderer/backend/gles/BUILD.gn +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -16,6 +16,8 @@ impeller_component("gles") { sources = [ "allocator_gles.cc", "allocator_gles.h", + "blit_command_gles.cc", + "blit_command_gles.h", "blit_pass_gles.cc", "blit_pass_gles.h", "buffer_bindings_gles.cc", diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc new file mode 100644 index 0000000000000..9d070597e7af4 --- /dev/null +++ b/impeller/renderer/backend/gles/blit_command_gles.cc @@ -0,0 +1,134 @@ +// 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/gles/blit_command_gles.h" + +#include "flutter/fml/closure.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/texture_gles.h" + +namespace impeller { + +BlitEncodeGLES::~BlitEncodeGLES() = default; + +static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) { + if (fbo != GL_NONE) { + gl.BindFramebuffer(type, GL_NONE); + gl.DeleteFramebuffers(1u, &fbo); + } +}; + +static std::optional ConfigureFBO( + const ProcTableGLES& gl, + const std::shared_ptr& texture, + GLenum fbo_type) { + auto handle = TextureGLES::Cast(texture.get())->GetGLHandle(); + if (!handle.has_value()) { + return std::nullopt; + } + + if (TextureGLES::Cast(*texture).IsWrapped()) { + // The texture is attached to the default FBO, so there's no need to + // create/configure one. + gl.BindFramebuffer(fbo_type, 0); + return 0; + } + + GLuint fbo; + gl.GenFramebuffers(1u, &fbo); + gl.BindFramebuffer(fbo_type, fbo); + + if (!TextureGLES::Cast(*texture).SetAsFramebufferAttachment( + fbo_type, fbo, TextureGLES::AttachmentPoint::kColor0)) { + VALIDATION_LOG << "Could not attach texture to framebuffer."; + DeleteFBO(gl, fbo, fbo_type); + return std::nullopt; + } + + if (gl.CheckFramebufferStatus(fbo_type) != GL_FRAMEBUFFER_COMPLETE) { + VALIDATION_LOG << "Could not create a complete frambuffer."; + DeleteFBO(gl, fbo, fbo_type); + return std::nullopt; + } + + return fbo; +}; + +BlitCopyTextureToTextureCommandGLES::~BlitCopyTextureToTextureCommandGLES() = + default; + +std::string BlitCopyTextureToTextureCommandGLES::GetLabel() const { + return label; +} + +bool BlitCopyTextureToTextureCommandGLES::Encode( + const ReactorGLES& reactor) const { + const auto& gl = reactor.GetProcTable(); + + // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to + // emulate the blit when it's not available in the driver. + if (!gl.BlitFramebuffer.IsAvailable()) { + // TODO(bdero): Emulate the blit using a raster draw call here. + FML_LOG(ERROR) << "Texture blit fallback not implemented yet for GLES2."; + return true; + } + + GLuint read_fbo = GL_NONE; + GLuint draw_fbo = GL_NONE; + fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() { + DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER); + DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER); + }); + + { + auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER); + if (!read.has_value()) { + return false; + } + read_fbo = read.value(); + } + + { + auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER); + if (!draw.has_value()) { + return false; + } + draw_fbo = draw.value(); + } + + gl.Disable(GL_SCISSOR_TEST); + gl.Disable(GL_DEPTH_TEST); + gl.Disable(GL_STENCIL_TEST); + + gl.BlitFramebuffer(source_region.origin.x, // srcX0 + source_region.origin.y, // srcY0 + source_region.size.width, // srcX1 + source_region.size.height, // srcY1 + destination_origin.x, // dstX0 + destination_origin.y, // dstY0 + source_region.size.width, // dstX1 + source_region.size.height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + + return true; +}; + +BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default; + +std::string BlitGenerateMipmapCommandGLES::GetLabel() const { + return label; +} + +bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const { + auto texture_gles = TextureGLES::Cast(texture.get()); + if (!texture_gles->GenerateMipmaps()) { + return false; + } + + return true; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/blit_command_gles.h b/impeller/renderer/backend/gles/blit_command_gles.h new file mode 100644 index 0000000000000..430fe661cf854 --- /dev/null +++ b/impeller/renderer/backend/gles/blit_command_gles.h @@ -0,0 +1,41 @@ +// 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 "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/blit_command.h" + +namespace impeller { + +/// Mixin for dispatching metal commands. +struct BlitEncodeGLES : BackendCast { + virtual ~BlitEncodeGLES(); + + virtual std::string GetLabel() const = 0; + + [[nodiscard]] virtual bool Encode(const ReactorGLES& reactor) const = 0; +}; + +struct BlitCopyTextureToTextureCommandGLES + : public BlitEncodeGLES, + public BlitCopyTextureToTextureCommand { + ~BlitCopyTextureToTextureCommandGLES() override; + + std::string GetLabel() const override; + + [[nodiscard]] bool Encode(const ReactorGLES& reactor) const override; +}; + +struct BlitGenerateMipmapCommandGLES : public BlitEncodeGLES, + public BlitGenerateMipmapCommand { + ~BlitGenerateMipmapCommandGLES() override; + + std::string GetLabel() const override; + + [[nodiscard]] bool Encode(const ReactorGLES& reactor) const override; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc index efe9299deb65c..07d6c732bda00 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.cc +++ b/impeller/renderer/backend/gles/blit_pass_gles.cc @@ -5,10 +5,12 @@ #include "impeller/renderer/backend/gles/blit_pass_gles.h" #include +#include #include "flutter/fml/trace_event.h" #include "impeller/base/config.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/blit_command_gles.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h" #include "impeller/renderer/backend/gles/pipeline_gles.h" @@ -35,53 +37,10 @@ void BlitPassGLES::OnSetLabel(std::string label) { label_ = std::move(label); } -static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) { - if (fbo != GL_NONE) { - gl.BindFramebuffer(type, GL_NONE); - gl.DeleteFramebuffers(1u, &fbo); - } -}; - -static std::optional ConfigureFBO( - const ProcTableGLES& gl, - const std::shared_ptr& texture, - GLenum fbo_type) { - auto handle = TextureGLES::Cast(texture.get())->GetGLHandle(); - if (!handle.has_value()) { - return std::nullopt; - } - - if (TextureGLES::Cast(*texture).IsWrapped()) { - // The texture is attached to the default FBO, so there's no need to - // create/configure one. - gl.BindFramebuffer(fbo_type, 0); - return 0; - } - - GLuint fbo; - gl.GenFramebuffers(1u, &fbo); - gl.BindFramebuffer(fbo_type, fbo); - - if (!TextureGLES::Cast(*texture).SetAsFramebufferAttachment( - fbo_type, fbo, TextureGLES::AttachmentPoint::kColor0)) { - VALIDATION_LOG << "Could not attach texture to framebuffer."; - DeleteFBO(gl, fbo, fbo_type); - return std::nullopt; - } - - if (gl.CheckFramebufferStatus(fbo_type) != GL_FRAMEBUFFER_COMPLETE) { - VALIDATION_LOG << "Could not create a complete frambuffer."; - DeleteFBO(gl, fbo, fbo_type); - return std::nullopt; - } - - return fbo; -}; - [[nodiscard]] bool EncodeCommandsInReactor( const std::shared_ptr& transients_allocator, const ReactorGLES& reactor, - const std::vector& commands, + const std::vector>& commands, const std::string& label) { TRACE_EVENT0("impeller", __FUNCTION__); @@ -102,69 +61,15 @@ static std::optional ConfigureFBO( for (const auto& command : commands) { fml::ScopedCleanupClosure pop_cmd_debug_marker( [&gl]() { gl.PopDebugGroup(); }); - if (!command.label.empty()) { - gl.PushDebugGroup(command.label); + auto label = command->GetLabel(); + if (!label.empty()) { + gl.PushDebugGroup(label); } else { pop_cmd_debug_marker.Release(); } - if (auto* copy_command = - std::get_if(&command.data)) { - // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to - // emulate the blit when it's not available in the driver. - if (!gl.BlitFramebuffer.IsAvailable()) { - // TODO(bdero): Emulate the blit using a raster draw call here. - return true; - } - - GLuint read_fbo = GL_NONE; - GLuint draw_fbo = GL_NONE; - fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() { - DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER); - DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER); - }); - - { - auto read = ConfigureFBO(gl, copy_command->source, GL_READ_FRAMEBUFFER); - if (!read.has_value()) { - return false; - } - read_fbo = read.value(); - } - - { - auto draw = - ConfigureFBO(gl, copy_command->destination, GL_DRAW_FRAMEBUFFER); - if (!draw.has_value()) { - return false; - } - draw_fbo = draw.value(); - } - - gl.Disable(GL_SCISSOR_TEST); - gl.Disable(GL_DEPTH_TEST); - gl.Disable(GL_STENCIL_TEST); - - gl.BlitFramebuffer(copy_command->source_region.origin.x, // srcX0 - copy_command->source_region.origin.y, // srcY0 - copy_command->source_region.size.width, // srcX1 - copy_command->source_region.size.height, // srcY1 - copy_command->destination_origin.x, // dstX0 - copy_command->destination_origin.y, // dstY0 - copy_command->source_region.size.width, // dstX1 - copy_command->source_region.size.height, // dstY1 - GL_COLOR_BUFFER_BIT, // mask - GL_NEAREST // filter - ); - - } - - else if (auto* mipmap_command = - std::get_if(&command.data)) { - auto texture = TextureGLES::Cast(mipmap_command->texture.get()); - if (!texture->GenerateMipmaps()) { - return false; - } + if (!command->Encode(reactor)) { + return false; } } @@ -181,7 +86,7 @@ bool BlitPassGLES::EncodeCommands( return true; } - return reactor_->AddOperation([transients_allocator, commands = commands_, + return reactor_->AddOperation([transients_allocator, &commands = commands_, label = label_](const auto& reactor) { auto result = EncodeCommandsInReactor(transients_allocator, reactor, commands, label); @@ -189,4 +94,31 @@ bool BlitPassGLES::EncodeCommands( }); } +// |BlitPass| +void BlitPassGLES::OnCopyTextureToTextureCommand( + std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) { + auto command = std::make_unique(); + command->label = label; + command->source = std::move(source); + command->destination = std::move(destination); + command->source_region = source_region; + command->destination_origin = destination_origin; + + commands_.emplace_back(std::move(command)); +} + +// |BlitPass| +void BlitPassGLES::OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) { + auto command = std::make_unique(); + command->label = label; + command->texture = std::move(texture); + + commands_.emplace_back(std::move(command)); +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h index 99b18622517be..2268f5dd542ff 100644 --- a/impeller/renderer/backend/gles/blit_pass_gles.h +++ b/impeller/renderer/backend/gles/blit_pass_gles.h @@ -7,6 +7,7 @@ #include "flutter/fml/macros.h" #include "flutter/impeller/renderer/backend/gles/reactor_gles.h" #include "flutter/impeller/renderer/blit_pass.h" +#include "impeller/renderer/backend/gles/blit_command_gles.h" namespace impeller { @@ -18,6 +19,7 @@ class BlitPassGLES final : public BlitPass { private: friend class CommandBufferGLES; + std::vector> commands_; ReactorGLES::Ref reactor_; std::string label_; bool is_valid_ = false; @@ -34,6 +36,17 @@ class BlitPassGLES final : public BlitPass { bool EncodeCommands( const std::shared_ptr& transients_allocator) const override; + // |BlitPass| + void OnCopyTextureToTextureCommand(std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) override; + + // |BlitPass| + void OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) override; + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassGLES); }; diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn index fc364afab6947..8a1fe8eed2c99 100644 --- a/impeller/renderer/backend/metal/BUILD.gn +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("metal") { sources = [ "allocator_mtl.h", "allocator_mtl.mm", + "blit_command_mtl.h", + "blit_command_mtl.mm", "blit_pass_mtl.h", "blit_pass_mtl.mm", "command_buffer_mtl.h", diff --git a/impeller/renderer/backend/metal/blit_command_mtl.h b/impeller/renderer/backend/metal/blit_command_mtl.h new file mode 100644 index 0000000000000..cfaed52ab9c04 --- /dev/null +++ b/impeller/renderer/backend/metal/blit_command_mtl.h @@ -0,0 +1,43 @@ +// 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 "impeller/base/backend_cast.h" +#include "impeller/renderer/blit_command.h" + +namespace impeller { + +/// Mixin for dispatching metal commands. +struct BlitEncodeMTL : BackendCast { + virtual ~BlitEncodeMTL(); + + virtual std::string GetLabel() const = 0; + + [[nodiscard]] virtual bool Encode( + id encoder) const = 0; +}; + +struct BlitCopyTextureToTextureCommandMTL + : public BlitCopyTextureToTextureCommand, + public BlitEncodeMTL { + ~BlitCopyTextureToTextureCommandMTL() override; + + std::string GetLabel() const override; + + [[nodiscard]] bool Encode(id encoder) const override; +}; + +struct BlitGenerateMipmapCommandMTL : public BlitGenerateMipmapCommand, + public BlitEncodeMTL { + ~BlitGenerateMipmapCommandMTL() override; + + std::string GetLabel() const override; + + [[nodiscard]] bool Encode(id encoder) const override; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/blit_command_mtl.mm b/impeller/renderer/backend/metal/blit_command_mtl.mm new file mode 100644 index 0000000000000..c3704a951b5a0 --- /dev/null +++ b/impeller/renderer/backend/metal/blit_command_mtl.mm @@ -0,0 +1,70 @@ +// 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/metal/blit_command_mtl.h" + +#include "impeller/renderer/backend/metal/texture_mtl.h" + +namespace impeller { + +BlitEncodeMTL::~BlitEncodeMTL() = default; + +BlitCopyTextureToTextureCommandMTL::~BlitCopyTextureToTextureCommandMTL() = + default; + +std::string BlitCopyTextureToTextureCommandMTL::GetLabel() const { + return label; +} + +bool BlitCopyTextureToTextureCommandMTL::Encode( + id encoder) const { + auto source_mtl = TextureMTL::Cast(*source).GetMTLTexture(); + if (!source_mtl) { + return false; + } + + auto destination_mtl = TextureMTL::Cast(*destination).GetMTLTexture(); + if (!destination_mtl) { + return false; + } + + auto source_origin_mtl = + MTLOriginMake(source_region.origin.x, source_region.origin.y, 0); + auto source_size_mtl = + MTLSizeMake(source_region.size.width, source_region.size.height, 1); + auto destination_origin_mtl = + MTLOriginMake(destination_origin.x, destination_origin.y, 0); + + [encoder copyFromTexture:source_mtl + sourceSlice:0 + sourceLevel:0 + sourceOrigin:source_origin_mtl + sourceSize:source_size_mtl + toTexture:destination_mtl + destinationSlice:0 + destinationLevel:0 + destinationOrigin:destination_origin_mtl]; + + return true; +}; + +BlitGenerateMipmapCommandMTL::~BlitGenerateMipmapCommandMTL() = default; + +std::string BlitGenerateMipmapCommandMTL::GetLabel() const { + return label; +} + +bool BlitGenerateMipmapCommandMTL::Encode( + id encoder) const { + auto texture_mtl = TextureMTL::Cast(*texture).GetMTLTexture(); + if (!texture_mtl) { + return false; + } + + [encoder generateMipmapsForTexture:texture_mtl]; + + return true; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h index af1624c90e9f8..4c9b41bbabded 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.h +++ b/impeller/renderer/backend/metal/blit_pass_mtl.h @@ -7,6 +7,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/blit_command_mtl.h" #include "impeller/renderer/blit_pass.h" namespace impeller { @@ -19,24 +20,36 @@ class BlitPassMTL final : public BlitPass { private: friend class CommandBufferMTL; + std::vector> commands_; id buffer_ = nil; std::string label_; bool is_valid_ = false; explicit BlitPassMTL(id buffer); - // |RenderPass| + // |BlitPass| bool IsValid() const override; - // |RenderPass| + // |BlitPass| void OnSetLabel(std::string label) override; - // |RenderPass| + // |BlitPass| bool EncodeCommands( const std::shared_ptr& transients_allocator) const override; bool EncodeCommands(id pass) const; + // |BlitPass| + void OnCopyTextureToTextureCommand(std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) override; + + // |BlitPass| + void OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) override; + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassMTL); }; diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm index fee37791a2e87..d65cc544ce367 100644 --- a/impeller/renderer/backend/metal/blit_pass_mtl.mm +++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm @@ -4,12 +4,14 @@ #include "impeller/renderer/backend/metal/blit_pass_mtl.h" #include +#include #include #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/metal/blit_command_mtl.h" #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/pipeline_mtl.h" @@ -71,57 +73,45 @@ fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; for (const auto& command : commands_) { fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); - if (!command.label.empty()) { - [encoder pushDebugGroup:@(command.label.c_str())]; + auto label = command->GetLabel(); + if (!label.empty()) { + [encoder pushDebugGroup:@(label.c_str())]; } else { auto_pop_debug_marker.Release(); } - if (auto* copy_command = - std::get_if(&command.data)) { - auto source = TextureMTL::Cast(*copy_command->source).GetMTLTexture(); - if (!source) { - return false; - } - - auto destination = - TextureMTL::Cast(*copy_command->destination).GetMTLTexture(); - if (!destination) { - return false; - } - - auto source_origin = - MTLOriginMake(copy_command->source_region.origin.x, - copy_command->source_region.origin.y, 0); - auto source_size = - MTLSizeMake(copy_command->source_region.size.width, - copy_command->source_region.size.height, 1); - auto destination_origin = - MTLOriginMake(copy_command->destination_origin.x, - copy_command->destination_origin.y, 0); - - [encoder copyFromTexture:source - sourceSlice:0 - sourceLevel:0 - sourceOrigin:source_origin - sourceSize:source_size - toTexture:destination - destinationSlice:0 - destinationLevel:0 - destinationOrigin:destination_origin]; - } - - else if (auto* mipmap_command = - std::get_if(&command.data)) { - auto texture = TextureMTL::Cast(*mipmap_command->texture).GetMTLTexture(); - if (!texture) { - return false; - } - - [encoder generateMipmapsForTexture:texture]; + if (command->Encode(encoder)) { + return false; } } return true; } +// |BlitPass| +void BlitPassMTL::OnCopyTextureToTextureCommand( + std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) { + auto command = std::make_unique(); + command->label = label; + command->source = std::move(source); + command->destination = std::move(destination); + command->source_region = source_region; + command->destination_origin = destination_origin; + + commands_.emplace_back(std::move(command)); +} + +// |BlitPass| +void BlitPassMTL::OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) { + auto command = std::make_unique(); + command->label = label; + command->texture = std::move(texture); + + commands_.emplace_back(std::move(command)); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h index efa07c16c05bb..f05d878c725ec 100644 --- a/impeller/renderer/backend/vulkan/blit_pass_vk.h +++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h @@ -29,6 +29,17 @@ class BlitPassVK final : public BlitPass { bool EncodeCommands( const std::shared_ptr& transients_allocator) const override; + // |BlitPass| + void OnCopyTextureToTextureCommand(std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) override; + + // |BlitPass| + void OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) override; + FML_DISALLOW_COPY_AND_ASSIGN(BlitPassVK); }; diff --git a/impeller/renderer/blit_command.h b/impeller/renderer/blit_command.h index 8fe3317fac5ab..289e95a35f72c 100644 --- a/impeller/renderer/blit_command.h +++ b/impeller/renderer/blit_command.h @@ -4,27 +4,23 @@ #pragma once -#include - #include "impeller/renderer/texture.h" namespace impeller { struct BlitCommand { - struct CopyTextureToTexture { - std::shared_ptr source; - std::shared_ptr destination; - IRect source_region; - IPoint destination_origin; - }; - - struct GenerateMipmaps { - std::shared_ptr texture; - }; + std::string label; +}; - using Variant = std::variant; +struct BlitCopyTextureToTextureCommand : public BlitCommand { + std::shared_ptr source; + std::shared_ptr destination; + IRect source_region; + IPoint destination_origin; +}; - std::string label; - Variant data; +struct BlitGenerateMipmapCommand : public BlitCommand { + std::shared_ptr texture; }; + } // namespace impeller diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc index e24aea797fdbc..7e2b0e7843f3e 100644 --- a/impeller/renderer/blit_pass.cc +++ b/impeller/renderer/blit_pass.cc @@ -70,16 +70,9 @@ bool BlitPass::AddCopy(std::shared_ptr source, return true; // Nothing to blit. } - commands_.emplace_back(BlitCommand{ - .label = label, - .data = - BlitCommand::CopyTextureToTexture{ - .source = source, - .destination = destination, - .source_region = source_region.value(), - .destination_origin = destination_origin, - }, - }); + OnCopyTextureToTextureCommand(std::move(source), std::move(destination), + source_region.value(), destination_origin, + label); return true; } @@ -91,13 +84,7 @@ bool BlitPass::GenerateMipmap(std::shared_ptr texture, return false; } - commands_.emplace_back(BlitCommand{ - .label = label, - .data = - BlitCommand::GenerateMipmaps{ - .texture = texture, - }, - }); + OnGenerateMipmapCommand(std::move(texture), label); return true; } diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h index 4d2c9a481ae8c..e5fd8e0bb054e 100644 --- a/impeller/renderer/blit_pass.h +++ b/impeller/renderer/blit_pass.h @@ -83,12 +83,21 @@ class BlitPass { protected: std::shared_ptr transients_buffer_; - std::vector commands_; explicit BlitPass(); virtual void OnSetLabel(std::string label) = 0; + virtual void OnCopyTextureToTextureCommand( + std::shared_ptr source, + std::shared_ptr destination, + IRect source_region, + IPoint destination_origin, + std::string label) = 0; + + virtual void OnGenerateMipmapCommand(std::shared_ptr texture, + std::string label) = 0; + private: FML_DISALLOW_COPY_AND_ASSIGN(BlitPass); }; From 4dbd85c27ce6266157f5444bf9baaf60f5e529d8 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 27 Jul 2022 15:04:26 -0700 Subject: [PATCH 14/14] Address comments --- impeller/renderer/backend/gles/blit_command_gles.cc | 2 +- impeller/renderer/backend/gles/blit_command_gles.h | 2 +- impeller/renderer/backend/metal/blit_command_mtl.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc index 9d070597e7af4..2dde4c9741381 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.cc +++ b/impeller/renderer/backend/gles/blit_command_gles.cc @@ -71,7 +71,7 @@ bool BlitCopyTextureToTextureCommandGLES::Encode( if (!gl.BlitFramebuffer.IsAvailable()) { // TODO(bdero): Emulate the blit using a raster draw call here. FML_LOG(ERROR) << "Texture blit fallback not implemented yet for GLES2."; - return true; + return false; } GLuint read_fbo = GL_NONE; diff --git a/impeller/renderer/backend/gles/blit_command_gles.h b/impeller/renderer/backend/gles/blit_command_gles.h index 430fe661cf854..2fd57fbe1e1ab 100644 --- a/impeller/renderer/backend/gles/blit_command_gles.h +++ b/impeller/renderer/backend/gles/blit_command_gles.h @@ -10,7 +10,7 @@ namespace impeller { -/// Mixin for dispatching metal commands. +/// Mixin for dispatching GLES commands. struct BlitEncodeGLES : BackendCast { virtual ~BlitEncodeGLES(); diff --git a/impeller/renderer/backend/metal/blit_command_mtl.h b/impeller/renderer/backend/metal/blit_command_mtl.h index cfaed52ab9c04..e502cdd2c8d46 100644 --- a/impeller/renderer/backend/metal/blit_command_mtl.h +++ b/impeller/renderer/backend/metal/blit_command_mtl.h @@ -11,7 +11,7 @@ namespace impeller { -/// Mixin for dispatching metal commands. +/// Mixin for dispatching Metal commands. struct BlitEncodeMTL : BackendCast { virtual ~BlitEncodeMTL();