diff --git a/impeller/renderer/backend/gles/capabilities_gles.cc b/impeller/renderer/backend/gles/capabilities_gles.cc index 37bd3e665d90d..54ac1e70d3e6f 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.cc +++ b/impeller/renderer/backend/gles/capabilities_gles.cc @@ -106,6 +106,19 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) { gl.GetDescription()->HasExtension(kOESTextureBorderClampExt)) { supports_decal_sampler_address_mode_ = true; } + + if (gl.GetDescription()->HasExtension( + "GL_EXT_multisampled_render_to_texture") && + // The current implementation of MSAA support in Impeller GLES requires + // the use of glBlitFramebuffer, which is not available on all GLES + // implementations. We can't use MSAA on these platforms yet. + gl.BlitFramebuffer.IsAvailable()) { + // We hard-code 4x MSAA, so let's make sure it's supported. + GLint value = 0; + gl.GetIntegerv(GL_MAX_SAMPLES_EXT, &value); + + supports_offscreen_msaa_ = value >= 4; + } } size_t CapabilitiesGLES::GetMaxTextureUnits(ShaderStage stage) const { @@ -124,7 +137,7 @@ size_t CapabilitiesGLES::GetMaxTextureUnits(ShaderStage stage) const { } bool CapabilitiesGLES::SupportsOffscreenMSAA() const { - return false; + return supports_offscreen_msaa_; } bool CapabilitiesGLES::SupportsSSBO() const { diff --git a/impeller/renderer/backend/gles/capabilities_gles.h b/impeller/renderer/backend/gles/capabilities_gles.h index 3bcd0cfd85d27..edb9a1b33bb41 100644 --- a/impeller/renderer/backend/gles/capabilities_gles.h +++ b/impeller/renderer/backend/gles/capabilities_gles.h @@ -118,6 +118,7 @@ class CapabilitiesGLES final private: bool supports_framebuffer_fetch_ = false; bool supports_decal_sampler_address_mode_ = false; + bool supports_offscreen_msaa_ = false; }; } // namespace impeller diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h index ea56ebb6e516f..b4296fbfe264a 100644 --- a/impeller/renderer/backend/gles/formats_gles.h +++ b/impeller/renderer/backend/gles/formats_gles.h @@ -185,7 +185,7 @@ constexpr std::optional ToTextureTarget(TextureType type) { case TextureType::kTexture2D: return GL_TEXTURE_2D; case TextureType::kTexture2DMultisample: - return std::nullopt; + return GL_TEXTURE_2D; case TextureType::kTextureCube: return GL_TEXTURE_CUBE_MAP; case TextureType::kTextureExternalOES: diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index a856feeeff1a1..5536a3cd6c40a 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -6,7 +6,9 @@ #include "flutter/fml/trace_event.h" #include "fml/closure.h" +#include "fml/logging.h" #include "impeller/base/validation.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/renderer/backend/gles/context_gles.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h" @@ -125,6 +127,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; @@ -186,6 +189,7 @@ struct RenderPassData { return false; } } + if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) { if (!depth->SetAsFramebufferAttachment( GL_FRAMEBUFFER, TextureGLES::AttachmentPoint::kDepth)) { @@ -470,6 +474,45 @@ struct RenderPassData { } } + // When we have a resolve_attachment, MSAA is being used. We blit from the + // MSAA FBO to the resolve FBO, otherwise the resolve FBO ends up being + // incomplete (because it has no attachments). + // + // Note that this only works on OpenGLES 3.0+, or put another way, in older + // versions of OpenGLES, MSAA is not currently supported by Impeller. It's + // possible to work around this issue a few different ways (not yet done). + // + // TODO(matanlurey): See https://github.com/flutter/flutter/issues/137093. + if (!is_default_fbo && pass_data.resolve_attachment) { + // MSAA should not be enabled if BlitFramebuffer is not available. + FML_DCHECK(gl.BlitFramebuffer.IsAvailable()); + + GLuint draw_fbo = GL_NONE; + gl.GenFramebuffers(1u, &draw_fbo); + gl.BindFramebuffer(GL_FRAMEBUFFER, draw_fbo); + + auto resolve = TextureGLES::Cast(pass_data.resolve_attachment.get()); + if (!resolve->SetAsFramebufferAttachment( + GL_FRAMEBUFFER, TextureGLES::AttachmentPoint::kColor0)) { + return false; + } + + gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fbo); + gl.BindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + auto size = pass_data.resolve_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 + ); + } + if (gl.DiscardFramebufferEXT.IsAvailable()) { std::vector attachments; @@ -498,11 +541,6 @@ struct RenderPassData { attachments.data() // size ); } -#ifdef IMPELLER_DEBUG - if (is_default_fbo) { - tracer->MarkFrameEnd(gl); - } -#endif // IMPELLER_DEBUG return true; } @@ -531,6 +569,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 = @@ -548,7 +587,7 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const { } //---------------------------------------------------------------------------- - /// Setup depth data. + /// Setup stencil data. /// if (stencil0.has_value()) { pass_data->stencil_attachment = stencil0->texture; diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index ad8d319150c53..c2d4742ae9db3 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -12,6 +12,7 @@ #include "impeller/base/allocation.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/renderer/backend/gles/formats_gles.h" namespace impeller { @@ -21,19 +22,22 @@ static TextureGLES::Type GetTextureTypeFromDescriptor( const auto usage = static_cast(desc.usage); const auto render_target = static_cast(TextureUsage::kRenderTarget); - if (usage == render_target) { + const auto is_msaa = desc.sample_count == SampleCount::kCount4; + if (usage == render_target && !is_msaa) { + // TODO(matanlurey): MSAA render buffers? + // See https://github.com/flutter/flutter/issues/137095. return TextureGLES::Type::kRenderBuffer; } - return TextureGLES::Type::kTexture; + return is_msaa ? TextureGLES::Type::kTextureMultisampled + : TextureGLES::Type::kTexture; } HandleType ToHandleType(TextureGLES::Type type) { switch (type) { case TextureGLES::Type::kTexture: + case TextureGLES::Type::kTextureMultisampled: return HandleType::kTexture; case TextureGLES::Type::kRenderBuffer: - // MSAA textures are treated as render buffers. - case TextureGLES::Type::kRenderBufferMultisampled: return HandleType::kRenderBuffer; } FML_UNREACHABLE(); @@ -118,8 +122,17 @@ struct TexImage2DData { external_format = GL_RGBA; type = GL_HALF_FLOAT; break; - case PixelFormat::kUnknown: + // TODO(matanlurey): This is a combined depth stencil format (like + // kD24UnormS8Uint below). We should find a way to use a stencil-only + // format instead. + // + // See https://github.com/flutter/flutter/issues/137094. case PixelFormat::kS8UInt: + internal_format = GL_DEPTH_STENCIL; + external_format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + break; + case PixelFormat::kUnknown: case PixelFormat::kD24UnormS8Uint: case PixelFormat::kD32FloatS8UInt: case PixelFormat::kR8UNormInt: @@ -133,52 +146,9 @@ struct TexImage2DData { } TexImage2DData(PixelFormat pixel_format, - std::shared_ptr mapping) { - switch (pixel_format) { - case PixelFormat::kUnknown: - return; - case PixelFormat::kA8UNormInt: { - internal_format = GL_ALPHA; - external_format = GL_ALPHA; - type = GL_UNSIGNED_BYTE; - data = std::move(mapping); - break; - } - case PixelFormat::kR8G8B8A8UNormInt: { - internal_format = GL_RGBA; - external_format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - data = std::move(mapping); - break; - } - case PixelFormat::kR32G32B32A32Float: { - internal_format = GL_RGBA; - external_format = GL_RGBA; - type = GL_FLOAT; - data = std::move(mapping); - break; - } - case PixelFormat::kR16G16B16A16Float: { - internal_format = GL_RGBA; - external_format = GL_RGBA; - type = GL_HALF_FLOAT; - data = std::move(mapping); - break; - } - case PixelFormat::kR8G8B8A8UNormIntSRGB: - case PixelFormat::kB8G8R8A8UNormInt: - case PixelFormat::kB8G8R8A8UNormIntSRGB: - case PixelFormat::kS8UInt: - case PixelFormat::kD24UnormS8Uint: - case PixelFormat::kD32FloatS8UInt: - case PixelFormat::kR8UNormInt: - case PixelFormat::kR8G8UNormInt: - case PixelFormat::kB10G10R10XRSRGB: - case PixelFormat::kB10G10R10XR: - case PixelFormat::kB10G10R10A10XR: - return; - } - is_valid_ = true; + std::shared_ptr mapping) + : TexImage2DData(pixel_format) { + data = std::move(mapping); } bool IsValid() const { return is_valid_; } @@ -362,7 +332,8 @@ void TextureGLES::InitializeContentsIfNecessary() const { } switch (type_) { - case Type::kTexture: { + case Type::kTexture: + case Type::kTextureMultisampled: { TexImage2DData tex_data(GetTextureDescriptor().format); if (!tex_data.IsValid()) { VALIDATION_LOG << "Invalid format for texture image."; @@ -382,7 +353,6 @@ void TextureGLES::InitializeContentsIfNecessary() const { nullptr // data ); } - } break; case Type::kRenderBuffer: { auto render_buffer_format = @@ -401,26 +371,6 @@ void TextureGLES::InitializeContentsIfNecessary() const { ); } } break; - case Type::kRenderBufferMultisampled: { - auto render_buffer_msaa = - ToRenderBufferFormat(GetTextureDescriptor().format); - if (!render_buffer_msaa.has_value()) { - VALIDATION_LOG << "Invalid format for render-buffer MSAA image."; - return; - } - gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); - { - TRACE_EVENT0("impeller", "RenderBufferStorageInitialization"); - gl.RenderbufferStorageMultisampleEXT( - GL_RENDERBUFFER, // target - 4, // samples - render_buffer_msaa.value(), // internal format - size.width, // width - size.height // height - ); - } - break; - } } } @@ -438,7 +388,8 @@ bool TextureGLES::Bind() const { } const auto& gl = reactor_->GetProcTable(); switch (type_) { - case Type::kTexture: { + case Type::kTexture: + case Type::kTextureMultisampled: { const auto target = ToTextureTarget(GetTextureDescriptor().type); if (!target.has_value()) { VALIDATION_LOG << "Could not bind texture of this type."; @@ -447,8 +398,6 @@ bool TextureGLES::Bind() const { gl.BindTexture(target.value(), handle.value()); } break; case Type::kRenderBuffer: - // MSAA textures are treated as render buffers. - case Type::kRenderBufferMultisampled: gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value()); break; } @@ -516,6 +465,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLenum target, return false; } const auto& gl = reactor_->GetProcTable(); + switch (type_) { case Type::kTexture: gl.FramebufferTexture2D(target, // target @@ -525,16 +475,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLenum target, 0 // level ); break; - case Type::kRenderBuffer: - gl.FramebufferRenderbuffer(target, // target - ToAttachmentPoint(point), // attachment - GL_RENDERBUFFER, // render-buffer target - handle.value() // render-buffer - ); - break; - case Type::kRenderBufferMultisampled: - // Assume that when MSAA is enabled, we're using 4x MSAA. - FML_DCHECK(GetTextureDescriptor().sample_count == SampleCount::kCount4); + case Type::kTextureMultisampled: gl.FramebufferTexture2DMultisampleEXT( target, // target ToAttachmentPoint(point), // attachment @@ -544,7 +485,16 @@ bool TextureGLES::SetAsFramebufferAttachment(GLenum target, 4 // samples ); break; + case Type::kRenderBuffer: + gl.FramebufferRenderbuffer(target, // target + ToAttachmentPoint(point), // attachment + GL_RENDERBUFFER, // render-buffer target + handle.value() // render-buffer + ); + gl.BindRenderbuffer(GL_RENDERBUFFER, GL_NONE); + break; } + return true; } diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index 7f5cb90e4d00b..53a404b84016b 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -17,8 +17,8 @@ class TextureGLES final : public Texture, public: enum class Type { kTexture, + kTextureMultisampled, kRenderBuffer, - kRenderBufferMultisampled, }; enum class IsWrapped {