diff --git a/impeller/display_list/canvas.cc b/impeller/display_list/canvas.cc index 11cc4d7598a9f..bd3c626b5ff29 100644 --- a/impeller/display_list/canvas.cc +++ b/impeller/display_list/canvas.cc @@ -1663,10 +1663,15 @@ bool Canvas::BlitToOnscreen() { auto offscreen_target = render_passes_.back() .inline_pass_context->GetPassTarget() .GetRenderTarget(); - + // Unlike other backends the blit to restore the onscreen fails for GLES + // if the src is a multisample framebuffer, even if we specifically target + // the non-multisampled resolve of the dst. + bool is_gles_and_must_skip_blit = renderer_.GetContext()->GetBackendType() == + Context::BackendType::kOpenGLES; if (renderer_.GetContext() ->GetCapabilities() - ->SupportsTextureToTextureBlits()) { + ->SupportsTextureToTextureBlits() && + !is_gles_and_must_skip_blit) { auto blit_pass = command_buffer->CreateBlitPass(); blit_pass->AddCopy(offscreen_target.GetRenderTargetTexture(), render_target_.GetRenderTargetTexture()); diff --git a/impeller/renderer/backend/gles/capabilities_gles.cc b/impeller/renderer/backend/gles/capabilities_gles.cc index 67f73eff4e07f..21d49d22c702c 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.cc +++ b/impeller/renderer/backend/gles/capabilities_gles.cc @@ -133,7 +133,12 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) { gl.GetIntegerv(GL_MAX_SAMPLES_EXT, &value); supports_offscreen_msaa_ = value >= 4; } + } else if (desc->GetGlVersion().major_version >= 3 && desc->IsES()) { + GLint value = 0; + gl.GetIntegerv(GL_MAX_SAMPLES, &value); + supports_offscreen_msaa_ = value >= 4; } + is_es_ = desc->IsES(); is_angle_ = desc->IsANGLE(); } diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index ccc499af9dcca..aa34925aa95f2 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -247,6 +247,7 @@ void(glDepthRange)(GLdouble n, GLdouble f); PROC(UniformBlockBinding); \ PROC(BindBufferRange); \ PROC(WaitSync); \ + PROC(RenderbufferStorageMultisample) \ PROC(BlitFramebuffer); #define FOR_EACH_IMPELLER_EXT_PROC(PROC) \ diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index c8900d6e0c1c0..6d90c8b3ae69c 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -6,7 +6,6 @@ #include -#include "flutter/fml/trace_event.h" #include "fml/closure.h" #include "fml/logging.h" #include "impeller/base/validation.h" @@ -130,6 +129,7 @@ struct RenderPassData { Scalar clear_depth = 1.0; std::shared_ptr color_attachment; + std::shared_ptr resolve_attachment; std::shared_ptr depth_attachment; std::shared_ptr stencil_attachment; @@ -196,8 +196,6 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) { const std::vector& bound_textures, const std::vector& bound_buffers, const std::shared_ptr& tracer) { - TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor"); - const auto& gl = reactor.GetProcTable(); #ifdef IMPELLER_DEBUG tracer->MarkFrameStart(gl); @@ -514,6 +512,55 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) { } } + if (pass_data.resolve_attachment && + !gl.GetCapabilities()->SupportsImplicitResolvingMSAA() && + !is_default_fbo) { + FML_DCHECK(pass_data.resolve_attachment != pass_data.color_attachment); + // Perform multisample resolve via blit. + // Create and bind a resolve FBO. + GLuint resolve_fbo; + gl.GenFramebuffers(1u, &resolve_fbo); + gl.BindFramebuffer(GL_FRAMEBUFFER, resolve_fbo); + + if (!TextureGLES::Cast(*pass_data.resolve_attachment) + .SetAsFramebufferAttachment( + GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) { + return false; + } + + auto status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (gl.CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + VALIDATION_LOG << "Could not create a complete frambuffer: " + << DebugToFramebufferError(status); + return false; + } + + // Bind MSAA renderbuffer to read framebuffer. + gl.BindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo); + + RenderPassGLES::ResetGLState(gl); + auto size = pass_data.color_attachment->GetSize(); + + gl.BlitFramebuffer(0, // srcX0 + 0, // srcY0 + size.width, // srcX1 + size.height, // srcY1 + 0, // dstX0 + 0, // dstY0 + size.width, // dstX1 + size.height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + + gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_NONE); + gl.BindFramebuffer(GL_READ_FRAMEBUFFER, GL_NONE); + gl.DeleteFramebuffers(1u, &resolve_fbo); + // Rebind the original FBO so that we can discard it below. + gl.BindFramebuffer(GL_FRAMEBUFFER, fbo); + } + if (gl.DiscardFramebufferEXT.IsAvailable()) { std::array attachments; size_t attachment_count = 0; @@ -574,6 +621,7 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const { /// Setup color data. /// pass_data->color_attachment = color0.texture; + pass_data->resolve_attachment = color0.resolve_texture; pass_data->clear_color = color0.clear_color; pass_data->clear_color_attachment = CanClearAttachment(color0.load_action); pass_data->discard_color_attachment = @@ -583,8 +631,9 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const { // resolved when we bind the texture to the framebuffer. We don't need to // discard the attachment when we are done. if (color0.resolve_texture) { - FML_DCHECK(context.GetCapabilities()->SupportsImplicitResolvingMSAA()); - pass_data->discard_color_attachment = false; + pass_data->discard_color_attachment = + pass_data->discard_color_attachment && + !context.GetCapabilities()->SupportsImplicitResolvingMSAA(); } //---------------------------------------------------------------------------- diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index e85aeb3772111..9bcb38caa9f43 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -45,7 +45,8 @@ static bool IsDepthStencilFormat(PixelFormat format) { } static TextureGLES::Type GetTextureTypeFromDescriptor( - const TextureDescriptor& desc) { + const TextureDescriptor& desc, + bool supports_implict_msaa) { const auto usage = static_cast(desc.usage); const auto render_target = TextureUsage::kRenderTarget; const auto is_msaa = desc.sample_count == SampleCount::kCount4; @@ -53,7 +54,9 @@ static TextureGLES::Type GetTextureTypeFromDescriptor( return is_msaa ? TextureGLES::Type::kRenderBufferMultisampled : TextureGLES::Type::kRenderBuffer; } - return is_msaa ? TextureGLES::Type::kTextureMultisampled + return is_msaa ? (supports_implict_msaa + ? TextureGLES::Type::kTextureMultisampled + : TextureGLES::Type::kRenderBufferMultisampled) : TextureGLES::Type::kTexture; } @@ -192,7 +195,11 @@ TextureGLES::TextureGLES(std::shared_ptr reactor, std::optional external_handle) : Texture(desc), reactor_(std::move(reactor)), - type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())), + type_( + GetTextureTypeFromDescriptor(GetTextureDescriptor(), + reactor_->GetProcTable() + .GetCapabilities() + ->SupportsImplicitResolvingMSAA())), handle_(external_handle.has_value() ? external_handle.value() : reactor_->CreateUntrackedHandle(ToHandleType(type_))), @@ -367,7 +374,7 @@ static std::optional ToRenderBufferFormat(PixelFormat format) { switch (format) { case PixelFormat::kB8G8R8A8UNormInt: case PixelFormat::kR8G8B8A8UNormInt: - return GL_RGBA4; + return GL_RGBA8; case PixelFormat::kR32G32B32A32Float: return GL_RGBA32F; case PixelFormat::kR16G16B16A16Float: @@ -450,19 +457,32 @@ void TextureGLES::InitializeContentsIfNecessary() const { { TRACE_EVENT0("impeller", "RenderBufferStorageInitialization"); if (type_ == Type::kRenderBufferMultisampled) { - gl.RenderbufferStorageMultisampleEXT( - GL_RENDERBUFFER, // target - 4, // samples - render_buffer_format.value(), // internal format - size.width, // width - size.height // height - ); + // BEWARE: these functions are not at all equivalent! the extensions + // are from EXT_multisampled_render_to_texture and cannot be used + // with regular GLES 3.0 multisampled renderbuffers/textures. + if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) { + gl.RenderbufferStorageMultisampleEXT( + /*target=*/GL_RENDERBUFFER, // + /*samples=*/4, // + /*internal_format=*/render_buffer_format.value(), // + /*width=*/size.width, // + /*height=*/size.height // + ); + } else { + gl.RenderbufferStorageMultisample( + /*target=*/GL_RENDERBUFFER, // + /*samples=*/4, // + /*internal_format=*/render_buffer_format.value(), // + /*width=*/size.width, // + /*height=*/size.height // + ); + } } else { gl.RenderbufferStorage( - GL_RENDERBUFFER, // target - render_buffer_format.value(), // internal format - size.width, // width - size.height // height + /*target=*/GL_RENDERBUFFER, // + /*internal_format=*/render_buffer_format.value(), // + /*width=*/size.width, // + /*height=*/size.height // ); } }