diff --git a/engine/src/flutter/impeller/playground/imgui/imgui_impl_impeller.cc b/engine/src/flutter/impeller/playground/imgui/imgui_impl_impeller.cc index 71055cf319f..9328bd8ca66 100644 --- a/engine/src/flutter/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/engine/src/flutter/impeller/playground/imgui/imgui_impl_impeller.cc @@ -78,6 +78,7 @@ bool ImGui_ImplImpeller_Init(std::shared_ptr context) { impeller::StorageMode::kHostVisible, texture_descriptor); IM_ASSERT(bd->font_texture != nullptr && "Could not allocate ImGui font texture."); + bd->font_texture->SetLabel("ImGui Font Texture"); [[maybe_unused]] bool uploaded = bd->font_texture->SetContents( pixels, texture_descriptor.GetByteSizeOfBaseMipLevel()); diff --git a/engine/src/flutter/impeller/playground/playground.cc b/engine/src/flutter/impeller/playground/playground.cc index 590ef13e173..6c04e35dffe 100644 --- a/engine/src/flutter/impeller/playground/playground.cc +++ b/engine/src/flutter/impeller/playground/playground.cc @@ -203,6 +203,7 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { []() { ImGui_ImplImpeller_Shutdown(); }); ::glfwSetWindowSize(window, GetWindowSize().width, GetWindowSize().height); + ::glfwSetWindowPos(window, 200, 100); ::glfwShowWindow(window); while (true) { diff --git a/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc index 40065ded57e..8ff893b8203 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/backend/gles/buffer_bindings_gles.h" +#include #include #include "impeller/base/config.h" @@ -21,7 +22,14 @@ BufferBindingsGLES::~BufferBindingsGLES() = default; bool BufferBindingsGLES::RegisterVertexStageInput( const ProcTableGLES& gl, - const std::vector& inputs) { + const std::vector& p_inputs) { + // Attrib locations have to be iterated over in order of location because we + // will be calculating offsets later. + auto inputs = p_inputs; + std::sort(inputs.begin(), inputs.end(), [](const auto& lhs, const auto& rhs) { + return lhs.location < rhs.location; + }); + std::vector vertex_attrib_arrays; size_t offset = 0u; for (const auto& input : inputs) { diff --git a/engine/src/flutter/impeller/renderer/backend/gles/device_buffer_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/device_buffer_gles.cc index bd25f87f8ad..e1b91fb5af3 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/device_buffer_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/device_buffer_gles.cc @@ -21,7 +21,7 @@ DeviceBufferGLES::DeviceBufferGLES(ReactorGLES::Ref reactor, // |DeviceBuffer| DeviceBufferGLES::~DeviceBufferGLES() { - if (!reactor_) { + if (!handle_.IsDead()) { reactor_->CollectHandle(handle_); } } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/gles_handle.h b/engine/src/flutter/impeller/renderer/backend/gles/gles_handle.h index ed253be4bb1..afd503b31fe 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/gles_handle.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/gles_handle.h @@ -21,6 +21,8 @@ enum class HandleType { kTexture, kBuffer, kProgram, + kRenderBuffer, + kFrameBuffer, }; class ReactorGLES; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.cc index 4480516d5e8..4e1e11c8b6e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.cc @@ -6,6 +6,7 @@ #include +#include "impeller/base/comparable.h" #include "impeller/base/validation.h" namespace impeller { @@ -43,6 +44,10 @@ ProcTableGLES::Resolver WrappedResolver(ProcTableGLES::Resolver resolver) { auto truncated = function.substr(0u, function.size() - 3); return resolver(truncated.c_str()); } + if (function.find("EXT", function.size() - 3) != std::string::npos) { + auto truncated = function.substr(0u, function.size() - 3); + return resolver(truncated.c_str()); + } return nullptr; }; } @@ -95,6 +100,12 @@ ProcTableGLES::ProcTableGLES(Resolver resolver) { PushDebugGroupKHR.Reset(); PopDebugGroupKHR.Reset(); ObjectLabelKHR.Reset(); + } else { + GetIntegerv(GL_MAX_LABEL_LENGTH_KHR, &debug_label_max_length_); + } + + if (!description_->HasExtension("GL_EXT_discard_framebuffer")) { + DiscardFramebufferEXT.Reset(); } is_valid_ = true; @@ -223,14 +234,17 @@ bool ProcTableGLES::IsCurrentFramebufferComplete() const { static std::optional ToDebugIdentifier(DebugResourceType type) { switch (type) { case DebugResourceType::kTexture: - // No entry in GL_KHR_debug headers. - return std::nullopt; + return GL_TEXTURE; case DebugResourceType::kBuffer: return GL_BUFFER_KHR; case DebugResourceType::kProgram: return GL_PROGRAM_KHR; case DebugResourceType::kShader: return GL_SHADER_KHR; + case DebugResourceType::kRenderBuffer: + return GL_RENDERBUFFER; + case DebugResourceType::kFrameBuffer: + return GL_FRAMEBUFFER; } FML_UNREACHABLE(); } @@ -238,12 +252,12 @@ static std::optional ToDebugIdentifier(DebugResourceType type) { void ProcTableGLES::SetDebugLabel(DebugResourceType type, GLint name, const std::string& label) const { - if (!ObjectLabelKHR.IsAvailable()) { + if (debug_label_max_length_ <= 0) { return; } const auto identifier = ToDebugIdentifier(type); const auto label_length = - std::min(GL_MAX_LABEL_LENGTH_KHR - 1, label.size()); + std::min(debug_label_max_length_ - 1, label.size()); if (!identifier.has_value()) { return; } @@ -254,4 +268,25 @@ void ProcTableGLES::SetDebugLabel(DebugResourceType type, ); } +void ProcTableGLES::PushDebugGroup(const std::string& label) const { + if (debug_label_max_length_ <= 0) { + return; + } + UniqueID id; + const auto label_length = + std::min(debug_label_max_length_ - 1, label.size()); + PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, // source + static_cast(id.id), // id + label_length, // length + label.data() // message + ); +} + +void ProcTableGLES::PopDebugGroup() const { + if (debug_label_max_length_ <= 0) { + return; + } + PopDebugGroupKHR(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.h index 064d7c8995b..a5d84256a8e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.h @@ -80,6 +80,8 @@ struct GLProc { PROC(AttachShader); \ PROC(BindAttribLocation); \ PROC(BindBuffer); \ + PROC(BindFramebuffer); \ + PROC(BindRenderbuffer); \ PROC(BindTexture); \ PROC(BlendEquationSeparate); \ PROC(BlendFuncSeparate); \ @@ -95,7 +97,9 @@ struct GLProc { PROC(CreateShader); \ PROC(CullFace); \ PROC(DeleteBuffers); \ + PROC(DeleteFramebuffers); \ PROC(DeleteProgram); \ + PROC(DeleteRenderbuffers); \ PROC(DeleteShader); \ PROC(DeleteTextures); \ PROC(DepthFunc); \ @@ -107,8 +111,12 @@ struct GLProc { PROC(DrawElements); \ PROC(Enable); \ PROC(EnableVertexAttribArray); \ + PROC(FramebufferRenderbuffer); \ + PROC(FramebufferTexture2D); \ PROC(FrontFace); \ PROC(GenBuffers); \ + PROC(GenFramebuffers); \ + PROC(GenRenderbuffers); \ PROC(GenTextures); \ PROC(GetActiveUniform); \ PROC(GetBooleanv); \ @@ -123,6 +131,7 @@ struct GLProc { PROC(IsFramebuffer); \ PROC(IsProgram); \ PROC(LinkProgram); \ + PROC(RenderbufferStorage); \ PROC(Scissor); \ PROC(ShaderBinary); \ PROC(ShaderSource); \ @@ -141,6 +150,7 @@ struct GLProc { PROC(Viewport); #define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ + PROC(DiscardFramebufferEXT); \ PROC(PushDebugGroupKHR); \ PROC(PopDebugGroupKHR); \ PROC(ObjectLabelKHR); @@ -150,6 +160,8 @@ enum class DebugResourceType { kBuffer, kProgram, kShader, + kRenderBuffer, + kFrameBuffer, }; class ProcTableGLES { @@ -181,9 +193,14 @@ class ProcTableGLES { GLint name, const std::string& label) const; + void PushDebugGroup(const std::string& string) const; + + void PopDebugGroup() const; + private: bool is_valid_ = false; std::unique_ptr description_; + GLint debug_label_max_length_ = 0; FML_DISALLOW_COPY_AND_ASSIGN(ProcTableGLES); }; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc index a81fc4662d3..36b5b11d358 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc @@ -66,6 +66,12 @@ static std::optional CreateGLHandle(const ProcTableGLES& gl, return handle; case HandleType::kProgram: return gl.CreateProgram(); + case HandleType::kRenderBuffer: + gl.GenRenderbuffers(1u, &handle); + return handle; + case HandleType::kFrameBuffer: + gl.GenFramebuffers(1u, &handle); + return handle; } return std::nullopt; } @@ -85,6 +91,12 @@ static bool CollectGLHandle(const ProcTableGLES& gl, case HandleType::kProgram: gl.DeleteProgram(handle); return true; + case HandleType::kRenderBuffer: + gl.DeleteRenderbuffers(1u, &handle); + return true; + case HandleType::kFrameBuffer: + gl.DeleteFramebuffers(1u, &handle); + return true; } return false; } @@ -135,6 +147,10 @@ static DebugResourceType ToDebugResourceType(HandleType type) { return DebugResourceType::kBuffer; case HandleType::kProgram: return DebugResourceType::kProgram; + case HandleType::kRenderBuffer: + return DebugResourceType::kRenderBuffer; + case HandleType::kFrameBuffer: + return DebugResourceType::kFrameBuffer; } FML_UNREACHABLE(); } @@ -213,6 +229,17 @@ void ReactorGLES::SetDebugLabel(const GLESHandle& handle, std::string label) { if (handle.IsDead()) { return; } + if (in_reaction_) { + if (auto found = live_gl_handles_.find(handle); + found != live_gl_handles_.end() && found->second.has_value()) { + GetProcTable().SetDebugLabel( + ToDebugResourceType(found->first.type), // type + found->second.value(), // name + label // label + ); + return; + } + } pending_debug_labels_[handle] = std::move(label); } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc index 6ee2b6e4386..5dd4b8ee10c 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc @@ -12,6 +12,7 @@ #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" namespace impeller { @@ -119,8 +120,9 @@ struct RenderPassData { uint32_t clear_stencil = 0u; Scalar clear_depth = 1.0; - bool has_depth_attachment = false; - bool has_stencil_attachment = false; + std::shared_ptr color_attachment; + std::shared_ptr depth_attachment; + std::shared_ptr stencil_attachment; bool clear_color_attachment = true; bool clear_depth_attachment = true; @@ -129,6 +131,8 @@ struct RenderPassData { bool discard_color_attachment = true; bool discard_depth_attachment = true; bool discard_stencil_attachment = true; + + std::string label; }; [[nodiscard]] bool EncodeCommandsInReactor( @@ -138,17 +142,70 @@ struct RenderPassData { const std::vector& commands) { TRACE_EVENT0("impeller", __FUNCTION__); + if (commands.empty()) { + return true; + } + const auto& gl = reactor.GetProcTable(); + fml::ScopedCleanupClosure pop_pass_debug_marker( + [&gl]() { gl.PopDebugGroup(); }); + if (!pass_data.label.empty()) { + gl.PushDebugGroup(pass_data.label); + } else { + pop_pass_debug_marker.Release(); + } + + GLuint fbo = GL_NONE; + fml::ScopedCleanupClosure delete_fbo([&gl, &fbo]() { + if (fbo != GL_NONE) { + gl.BindFramebuffer(GL_FRAMEBUFFER, GL_NONE); + gl.DeleteFramebuffers(1u, &fbo); + } + }); + + const auto is_default_fbo = + TextureGLES::Cast(*pass_data.color_attachment).IsWrapped(); + + if (!is_default_fbo) { + // Create and bind an offscreen FBO. + gl.GenFramebuffers(1u, &fbo); + gl.BindFramebuffer(GL_FRAMEBUFFER, fbo); + + if (auto color = TextureGLES::Cast(pass_data.color_attachment.get())) { + if (!color->SetAsFramebufferAttachment( + fbo, TextureGLES::AttachmentPoint::kColor0)) { + return false; + } + } + if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) { + if (!depth->SetAsFramebufferAttachment( + fbo, TextureGLES::AttachmentPoint::kDepth)) { + return false; + } + } + if (auto stencil = TextureGLES::Cast(pass_data.stencil_attachment.get())) { + if (!stencil->SetAsFramebufferAttachment( + fbo, TextureGLES::AttachmentPoint::kStencil)) { + return false; + } + } + + if (gl.CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + VALIDATION_LOG << "Could not create a complete frambuffer."; + return false; + } + } + gl.ClearColor(pass_data.clear_color.red, // red pass_data.clear_color.green, // green pass_data.clear_color.blue, // blue pass_data.clear_color.alpha // alpha ); - if (pass_data.has_depth_attachment) { + if (pass_data.depth_attachment) { gl.ClearDepthf(pass_data.clear_depth); } - if (pass_data.has_stencil_attachment) { + if (pass_data.stencil_attachment) { gl.ClearStencil(pass_data.clear_stencil); } @@ -175,6 +232,14 @@ struct RenderPassData { return false; } + fml::ScopedCleanupClosure pop_cmd_debug_marker( + [&gl]() { gl.PopDebugGroup(); }); + if (!command.label.empty()) { + gl.PushDebugGroup(command.label); + } else { + pop_cmd_debug_marker.Release(); + } + const auto& pipeline = PipelineGLES::Cast(*command.pipeline); const auto* color_attachment = @@ -217,7 +282,7 @@ struct RenderPassData { viewport.rect.size.width, // width viewport.rect.size.height // height ); - if (pass_data.has_depth_attachment) { + if (pass_data.depth_attachment) { gl.DepthRangef(viewport.depth_range.z_near, viewport.depth_range.z_far); } @@ -351,8 +416,25 @@ struct RenderPassData { } } - // TODO(csg): Respect the discard flags using glDiscardFramebuffer. Vital for - // mobile GPUs. + if (gl.DiscardFramebufferEXT.IsAvailable()) { + std::vector attachments; + if (pass_data.discard_color_attachment) { + attachments.push_back(is_default_fbo ? GL_COLOR_EXT + : GL_COLOR_ATTACHMENT0); + } + if (pass_data.discard_depth_attachment) { + attachments.push_back(is_default_fbo ? GL_DEPTH_EXT + : GL_DEPTH_ATTACHMENT); + } + if (pass_data.discard_stencil_attachment) { + attachments.push_back(is_default_fbo ? GL_STENCIL_EXT + : GL_STENCIL_ATTACHMENT); + } + gl.DiscardFramebufferEXT(GL_FRAMEBUFFER, // target + attachments.size(), // attachments to discard + attachments.data() // size + ); + } return true; } @@ -375,11 +457,13 @@ bool RenderPassGLES::EncodeCommands( const auto& stencil0 = render_target.GetStencilAttachment(); auto pass_data = std::make_shared(); + pass_data->label = label_; pass_data->viewport.rect = Rect::MakeSize(Size(GetRenderTargetSize())); //---------------------------------------------------------------------------- /// Setup color data. /// + pass_data->color_attachment = color0.texture; pass_data->clear_color = color0.clear_color; pass_data->clear_color_attachment = CanClearAttachment(color0.load_action); pass_data->discard_color_attachment = @@ -389,7 +473,7 @@ bool RenderPassGLES::EncodeCommands( /// Setup depth data. /// if (depth0.has_value()) { - pass_data->has_depth_attachment = true; + pass_data->depth_attachment = depth0->texture; pass_data->clear_depth = depth0->clear_depth; pass_data->clear_depth_attachment = CanClearAttachment(depth0->load_action); pass_data->discard_depth_attachment = @@ -400,7 +484,7 @@ bool RenderPassGLES::EncodeCommands( /// Setup depth data. /// if (stencil0.has_value()) { - pass_data->has_stencil_attachment = true; + pass_data->stencil_attachment = stencil0->texture; pass_data->clear_stencil = stencil0->clear_stencil; pass_data->clear_stencil_attachment = CanClearAttachment(stencil0->load_action); diff --git a/engine/src/flutter/impeller/renderer/backend/gles/surface_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/surface_gles.cc index b13264d688f..364dd2a5767 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/surface_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/surface_gles.cc @@ -25,31 +25,33 @@ std::unique_ptr SurfaceGLES::WrapFBO(std::shared_ptr context, const auto& gl_context = ContextGLES::Cast(*context); TextureDescriptor color0_tex; - color0_tex.type = TextureType::kTexture2D; // lies + color0_tex.type = TextureType::kTexture2D; color0_tex.format = color_format; color0_tex.size = fbo_size; color0_tex.usage = static_cast(TextureUsage::kRenderTarget); - color0_tex.sample_count = SampleCount::kCount4; + color0_tex.sample_count = SampleCount::kCount1; ColorAttachment color0; - color0.texture = std::make_shared(gl_context.GetReactor(), - std::move(color0_tex)); + color0.texture = std::make_shared( + gl_context.GetReactor(), std::move(color0_tex), + TextureGLES::IsWrapped::kWrapped); color0.clear_color = Color::DarkSlateGray(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; TextureDescriptor stencil0_tex; - stencil0_tex.type = TextureType::kTexture2D; // lies + stencil0_tex.type = TextureType::kTexture2D; stencil0_tex.format = color_format; stencil0_tex.size = fbo_size; stencil0_tex.usage = static_cast(TextureUsage::kRenderTarget); - stencil0_tex.sample_count = SampleCount::kCount4; + stencil0_tex.sample_count = SampleCount::kCount1; StencilAttachment stencil0; stencil0.clear_stencil = 0; - stencil0.texture = std::make_shared(gl_context.GetReactor(), - std::move(stencil0_tex)); + stencil0.texture = std::make_shared( + gl_context.GetReactor(), std::move(stencil0_tex), + TextureGLES::IsWrapped::kWrapped); stencil0.load_action = LoadAction::kClear; stencil0.store_action = StoreAction::kDontCare; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc index c6b0bceff9b..418a72e765d 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc @@ -4,6 +4,8 @@ #include "impeller/renderer/backend/gles/texture_gles.h" +#include + #include "flutter/fml/mapping.h" #include "impeller/base/allocation.h" #include "impeller/base/config.h" @@ -11,15 +13,46 @@ namespace impeller { +static TextureGLES::Type GetTextureTypeFromDescriptor( + const TextureDescriptor& desc) { + const auto usage = static_cast(desc.usage); + const auto render_target = + static_cast(TextureUsage::kRenderTarget); + if (usage == render_target) { + return TextureGLES::Type::kRenderBuffer; + } + return TextureGLES::Type::kTexture; +} + +HandleType ToHandleType(TextureGLES::Type type) { + switch (type) { + case TextureGLES::Type::kTexture: + return HandleType::kTexture; + case TextureGLES::Type::kRenderBuffer: + return HandleType::kRenderBuffer; + } + FML_UNREACHABLE(); +} + +TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc) + : TextureGLES(std::move(reactor), std::move(desc), false) {} + +TextureGLES::TextureGLES(ReactorGLES::Ref reactor, + TextureDescriptor desc, + enum IsWrapped wrapped) + : TextureGLES(std::move(reactor), std::move(desc), true) {} + TextureGLES::TextureGLES(std::shared_ptr reactor, - TextureDescriptor desc) + TextureDescriptor desc, + bool is_wrapped) : Texture(std::move(desc)), reactor_(reactor), - handle_(reactor_->CreateHandle(HandleType::kTexture)) { + type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())), + handle_(reactor_->CreateHandle(ToHandleType(type_))), + is_wrapped_(is_wrapped) { if (!GetTextureDescriptor().IsValid()) { return; } - is_valid_ = true; } @@ -35,10 +68,10 @@ bool TextureGLES::IsValid() const { // |Texture| void TextureGLES::SetLabel(const std::string_view& label) { - if (!IsValid() || handle_.IsDead()) { - return; + label_ = std::string{label.data(), label.size()}; + if (contents_initialized_) { + reactor_->SetDebugLabel(handle_, label_); } - reactor_->SetDebugLabel(handle_, std::string{label.data(), label.size()}); } struct TexImage2DData { @@ -47,6 +80,24 @@ struct TexImage2DData { GLenum type = GL_NONE; std::shared_ptr data; + TexImage2DData(PixelFormat pixel_format) { + switch (pixel_format) { + case PixelFormat::kR8UNormInt: + case PixelFormat::kR8G8B8A8UNormInt: + case PixelFormat::kB8G8R8A8UNormInt: + case PixelFormat::kR8G8B8A8UNormIntSRGB: + case PixelFormat::kB8G8R8A8UNormIntSRGB: + internal_format = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case PixelFormat::kUnknown: + case PixelFormat::kS8UInt: + return; + } + is_valid_ = true; + } + TexImage2DData(PixelFormat pixel_format, const uint8_t* contents, size_t length) { @@ -108,6 +159,17 @@ bool TextureGLES::SetContents(const uint8_t* contents, size_t length) { return true; } + if (GetType() != Type::kTexture) { + VALIDATION_LOG << "Incorrect texture usage flags for setting contents on " + "this texture object."; + return false; + } + + if (is_wrapped_) { + VALIDATION_LOG << "Cannot set the contents of a wrapped texture."; + return false; + } + const auto& tex_descriptor = GetTextureDescriptor(); if (tex_descriptor.size.IsEmpty()) { @@ -142,20 +204,27 @@ bool TextureGLES::SetContents(const uint8_t* contents, size_t length) { } const auto& gl = reactor.GetProcTable(); gl.BindTexture(GL_TEXTURE_2D, gl_handle.value()); - gl.TexImage2D( - GL_TEXTURE_2D, // target - 0u, // LOD level (base mip level size checked) - data->internal_format, // internal format - size.width, // width - size.height, // height - 0u, // border - data->format, // format - data->type, // type - reinterpret_cast(data->data->GetMapping()) // data + const GLvoid* tex_data = nullptr; + if (data->data) { + tex_data = data->data->GetMapping(); + } + gl.TexImage2D(GL_TEXTURE_2D, // target + 0u, // LOD level + data->internal_format, // internal format + size.width, // width + size.height, // height + 0u, // border + data->format, // format + data->type, // type + tex_data // data ); }; - return reactor_->AddOperation(texture_upload); + contents_initialized_ = reactor_->AddOperation(texture_upload); + if (contents_initialized_) { + reactor_->SetDebugLabel(handle_, label_); + } + return contents_initialized_; } // |Texture| @@ -163,6 +232,85 @@ ISize TextureGLES::GetSize() const { return GetTextureDescriptor().size; } +static std::optional ToRenderBufferFormat(PixelFormat format) { + switch (format) { + case PixelFormat::kB8G8R8A8UNormInt: + case PixelFormat::kR8G8B8A8UNormInt: + return GL_RGBA4; + case PixelFormat::kS8UInt: + return GL_STENCIL_INDEX8; + case PixelFormat::kUnknown: + case PixelFormat::kR8UNormInt: + case PixelFormat::kR8G8B8A8UNormIntSRGB: + case PixelFormat::kB8G8R8A8UNormIntSRGB: + return std::nullopt; + } + FML_UNREACHABLE(); +} + +void TextureGLES::InitializeContentsIfNecessary() const { + if (!IsValid()) { + return; + } + if (contents_initialized_) { + return; + } + contents_initialized_ = true; + + if (is_wrapped_) { + return; + } + + auto size = GetSize(); + + if (size.IsEmpty()) { + return; + } + + const auto& gl = reactor_->GetProcTable(); + auto handle = reactor_->GetGLHandle(handle_); + if (!handle.has_value()) { + VALIDATION_LOG << "Could not initialize the contents of texture."; + return; + } + + switch (type_) { + case Type::kTexture: { + TexImage2DData tex_data(GetTextureDescriptor().format); + if (!tex_data.IsValid()) { + VALIDATION_LOG << "Invalid format for texture image."; + return; + } + gl.BindTexture(GL_TEXTURE_2D, handle.value()); + gl.TexImage2D(GL_TEXTURE_2D, // target + 0u, // LOD level (base mip level size checked) + tex_data.internal_format, // internal format + size.width, // width + size.height, // height + 0u, // border + tex_data.format, // format + tex_data.type, // type + nullptr // data + ); + } break; + case Type::kRenderBuffer: + auto render_buffer_format = + ToRenderBufferFormat(GetTextureDescriptor().format); + if (!render_buffer_format.has_value()) { + VALIDATION_LOG << "Invalid format for render-buffer image."; + return; + } + gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); + gl.RenderbufferStorage(GL_RENDERBUFFER, // target + render_buffer_format.value(), // internal format + size.width, // width + size.height // height + ); + break; + } + reactor_->SetDebugLabel(handle_, label_); +} + bool TextureGLES::Bind() const { if (!IsValid()) { return false; @@ -172,7 +320,61 @@ bool TextureGLES::Bind() const { return false; } const auto& gl = reactor_->GetProcTable(); - gl.BindTexture(GL_TEXTURE_2D, handle.value()); + switch (type_) { + case Type::kTexture: + gl.BindTexture(GL_TEXTURE_2D, handle.value()); + break; + case Type::kRenderBuffer: + gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); + break; + } + InitializeContentsIfNecessary(); + return true; +} + +TextureGLES::Type TextureGLES::GetType() const { + return type_; +} + +static GLenum ToAttachmentPoint(TextureGLES::AttachmentPoint point) { + switch (point) { + case TextureGLES::AttachmentPoint::kColor0: + return GL_COLOR_ATTACHMENT0; + case TextureGLES::AttachmentPoint::kDepth: + return GL_DEPTH_ATTACHMENT; + case TextureGLES::AttachmentPoint::kStencil: + return GL_STENCIL_ATTACHMENT; + } +} + +bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo, + AttachmentPoint point) const { + if (!IsValid()) { + return false; + } + InitializeContentsIfNecessary(); + auto handle = reactor_->GetGLHandle(handle_); + if (!handle.has_value()) { + return false; + } + const auto& gl = reactor_->GetProcTable(); + switch (type_) { + case Type::kTexture: + gl.FramebufferTexture2D(GL_FRAMEBUFFER, // target + ToAttachmentPoint(point), // attachment + GL_TEXTURE_2D, // textarget + handle.value(), // texture + 0 // level + ); + break; + case Type::kRenderBuffer: + gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, // target + ToAttachmentPoint(point), // attachment + GL_RENDERBUFFER, // render-buffer target + handle.value() // render-buffer + ); + break; + } return true; } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h index bdcbca124eb..9111c08b644 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h @@ -15,20 +15,53 @@ namespace impeller { class TextureGLES final : public Texture, public BackendCast { public: + enum class Type { + kTexture, + kRenderBuffer, + }; + + enum class IsWrapped { + kWrapped, + }; + TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc); + TextureGLES(ReactorGLES::Ref reactor, + TextureDescriptor desc, + IsWrapped wrapped); + // |Texture| ~TextureGLES() override; - bool Bind() const; + [[nodiscard]] bool Bind() const; + + enum class AttachmentPoint { + kColor0, + kDepth, + kStencil, + }; + [[nodiscard]] bool SetAsFramebufferAttachment(GLuint fbo, + AttachmentPoint point) const; + + Type GetType() const; + + bool IsWrapped() const { return is_wrapped_; } private: friend class AllocatorMTL; ReactorGLES::Ref reactor_; + const Type type_; GLESHandle handle_; + mutable bool contents_initialized_ = false; + const bool is_wrapped_; + std::string label_; bool is_valid_ = false; + TextureGLES(std::shared_ptr reactor, + TextureDescriptor desc, + bool is_wrapped); + // |Texture| void SetLabel(const std::string_view& label) override; @@ -41,6 +74,8 @@ class TextureGLES final : public Texture, // |Texture| ISize GetSize() const override; + void InitializeContentsIfNecessary() const; + FML_DISALLOW_COPY_AND_ASSIGN(TextureGLES); };