Skip to content

Commit 4fef40c

Browse files
author
Jonah Williams
authored
[Impeller] OpenGL MSAA for desktop/web devices. (#163939)
Add support for MSAA without the render to texture extension. This allows our CI goldens to run with anti aliasing. Fixes flutter/flutter#158360 (again)
1 parent 1beba50 commit 4fef40c

File tree

5 files changed

+111
-26
lines changed

5 files changed

+111
-26
lines changed

engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
133133
gl.GetIntegerv(GL_MAX_SAMPLES_EXT, &value);
134134
supports_offscreen_msaa_ = value >= 4;
135135
}
136+
} else if (desc->GetGlVersion().major_version >= 3 && desc->IsES()) {
137+
GLint value = 0;
138+
gl.GetIntegerv(GL_MAX_SAMPLES, &value);
139+
supports_offscreen_msaa_ = value >= 4;
136140
}
137141
is_es_ = desc->IsES();
138142
is_angle_ = desc->IsANGLE();

engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ void(glDepthRange)(GLdouble n, GLdouble f);
247247
PROC(UniformBlockBinding); \
248248
PROC(BindBufferRange); \
249249
PROC(WaitSync); \
250+
PROC(RenderbufferStorageMultisample) \
250251
PROC(BlitFramebuffer);
251252

252253
#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \

engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ struct RenderPassData {
130130
Scalar clear_depth = 1.0;
131131

132132
std::shared_ptr<Texture> color_attachment;
133+
std::shared_ptr<Texture> resolve_attachment;
133134
std::shared_ptr<Texture> depth_attachment;
134135
std::shared_ptr<Texture> stencil_attachment;
135136

@@ -214,6 +215,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
214215
TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
215216
const bool is_default_fbo = color_gles.IsWrapped();
216217

218+
std::optional<GLuint> fbo = 0;
217219
if (is_default_fbo) {
218220
if (color_gles.GetFBO().has_value()) {
219221
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
@@ -222,7 +224,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
222224
} else {
223225
// Create and bind an offscreen FBO.
224226
if (!color_gles.GetCachedFBO().IsDead()) {
225-
auto fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
227+
fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
226228
if (!fbo.has_value()) {
227229
return false;
228230
}
@@ -231,7 +233,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
231233
HandleGLES cached_fbo =
232234
reactor.CreateUntrackedHandle(HandleType::kFrameBuffer);
233235
color_gles.SetCachedFBO(cached_fbo);
234-
auto fbo = reactor.GetGLHandle(cached_fbo);
236+
fbo = reactor.GetGLHandle(cached_fbo);
235237
if (!fbo.has_value()) {
236238
return false;
237239
}
@@ -520,6 +522,54 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
520522
}
521523
}
522524

525+
if (pass_data.resolve_attachment &&
526+
!gl.GetCapabilities()->SupportsImplicitResolvingMSAA() &&
527+
!is_default_fbo) {
528+
FML_DCHECK(pass_data.resolve_attachment != pass_data.color_attachment);
529+
// Perform multisample resolve via blit.
530+
// Create and bind a resolve FBO.
531+
GLuint resolve_fbo;
532+
gl.GenFramebuffers(1u, &resolve_fbo);
533+
gl.BindFramebuffer(GL_FRAMEBUFFER, resolve_fbo);
534+
535+
if (!TextureGLES::Cast(*pass_data.resolve_attachment)
536+
.SetAsFramebufferAttachment(
537+
GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) {
538+
return false;
539+
}
540+
541+
auto status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
542+
if (gl.CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
543+
VALIDATION_LOG << "Could not create a complete frambuffer: "
544+
<< DebugToFramebufferError(status);
545+
return false;
546+
}
547+
548+
// Bind MSAA renderbuffer to read framebuffer.
549+
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, fbo.value());
550+
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
551+
552+
RenderPassGLES::ResetGLState(gl);
553+
auto size = pass_data.color_attachment->GetSize();
554+
555+
gl.BlitFramebuffer(/*srcX0=*/0,
556+
/*srcY0=*/0,
557+
/*srcX1=*/size.width,
558+
/*srcY1=*/size.height,
559+
/*dstX0=*/0,
560+
/*dstY0=*/0,
561+
/*dstX1=*/size.width,
562+
/*dstY1=*/size.height,
563+
/*mask=*/GL_COLOR_BUFFER_BIT,
564+
/*filter=*/GL_NEAREST);
565+
566+
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_NONE);
567+
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, GL_NONE);
568+
gl.DeleteFramebuffers(1u, &resolve_fbo);
569+
// Rebind the original FBO so that we can discard it below.
570+
gl.BindFramebuffer(GL_FRAMEBUFFER, fbo.value());
571+
}
572+
523573
if (gl.DiscardFramebufferEXT.IsAvailable()) {
524574
std::array<GLenum, 3> attachments;
525575
size_t attachment_count = 0;
@@ -580,17 +630,21 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
580630
/// Setup color data.
581631
///
582632
pass_data->color_attachment = color0.texture;
633+
pass_data->resolve_attachment = color0.resolve_texture;
583634
pass_data->clear_color = color0.clear_color;
584635
pass_data->clear_color_attachment = CanClearAttachment(color0.load_action);
585636
pass_data->discard_color_attachment =
586637
CanDiscardAttachmentWhenDone(color0.store_action);
587638

588639
// When we are using EXT_multisampled_render_to_texture, it is implicitly
589640
// resolved when we bind the texture to the framebuffer. We don't need to
590-
// discard the attachment when we are done.
641+
// discard the attachment when we are done. If not using
642+
// EXT_multisampled_render_to_texture but still using MSAA we discard the
643+
// attachment as normal.
591644
if (color0.resolve_texture) {
592-
FML_DCHECK(context.GetCapabilities()->SupportsImplicitResolvingMSAA());
593-
pass_data->discard_color_attachment = false;
645+
pass_data->discard_color_attachment =
646+
pass_data->discard_color_attachment &&
647+
!context.GetCapabilities()->SupportsImplicitResolvingMSAA();
594648
}
595649

596650
//----------------------------------------------------------------------------

engine/src/flutter/impeller/renderer/backend/gles/test/texture_gles_unittests.cc

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "impeller/core/texture_descriptor.h"
1212
#include "impeller/renderer/backend/gles/handle_gles.h"
1313
#include "impeller/renderer/backend/gles/proc_table_gles.h"
14+
#include "impeller/renderer/backend/gles/test/mock_gles.h"
1415

1516
namespace impeller::testing {
1617

@@ -77,11 +78,19 @@ TEST_P(TextureGLESTest, Binds2DTexture) {
7778

7879
ASSERT_TRUE(texture);
7980

80-
EXPECT_EQ(
81-
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
82-
TextureGLES::Type::kTexture);
83-
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
84-
TextureGLES::Type::kTextureMultisampled);
81+
if (GetContext()->GetCapabilities()->SupportsImplicitResolvingMSAA()) {
82+
EXPECT_EQ(
83+
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
84+
TextureGLES::Type::kTexture);
85+
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
86+
TextureGLES::Type::kTextureMultisampled);
87+
} else {
88+
EXPECT_EQ(
89+
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
90+
TextureGLES::Type::kRenderBufferMultisampled);
91+
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
92+
TextureGLES::Type::kRenderBufferMultisampled);
93+
}
8594
}
8695

8796
} // namespace impeller::testing

engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,18 @@ static bool IsDepthStencilFormat(PixelFormat format) {
4545
}
4646

4747
static TextureGLES::Type GetTextureTypeFromDescriptor(
48-
const TextureDescriptor& desc) {
48+
const TextureDescriptor& desc,
49+
const std::shared_ptr<const CapabilitiesGLES>& capabilities) {
4950
const auto usage = static_cast<TextureUsageMask>(desc.usage);
5051
const auto render_target = TextureUsage::kRenderTarget;
5152
const auto is_msaa = desc.sample_count == SampleCount::kCount4;
5253
if (usage == render_target && IsDepthStencilFormat(desc.format)) {
5354
return is_msaa ? TextureGLES::Type::kRenderBufferMultisampled
5455
: TextureGLES::Type::kRenderBuffer;
5556
}
56-
return is_msaa ? TextureGLES::Type::kTextureMultisampled
57+
return is_msaa ? (capabilities->SupportsImplicitResolvingMSAA()
58+
? TextureGLES::Type::kTextureMultisampled
59+
: TextureGLES::Type::kRenderBufferMultisampled)
5760
: TextureGLES::Type::kTexture;
5861
}
5962

@@ -192,7 +195,9 @@ TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
192195
std::optional<HandleGLES> external_handle)
193196
: Texture(desc),
194197
reactor_(std::move(reactor)),
195-
type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
198+
type_(GetTextureTypeFromDescriptor(
199+
GetTextureDescriptor(),
200+
reactor_->GetProcTable().GetCapabilities())),
196201
handle_(external_handle.has_value()
197202
? external_handle.value()
198203
: reactor_->CreateUntrackedHandle(ToHandleType(type_))),
@@ -367,7 +372,7 @@ static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
367372
switch (format) {
368373
case PixelFormat::kB8G8R8A8UNormInt:
369374
case PixelFormat::kR8G8B8A8UNormInt:
370-
return GL_RGBA4;
375+
return GL_RGBA8;
371376
case PixelFormat::kR32G32B32A32Float:
372377
return GL_RGBA32F;
373378
case PixelFormat::kR16G16B16A16Float:
@@ -457,21 +462,33 @@ void TextureGLES::InitializeContentsIfNecessary() const {
457462
}
458463
gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
459464
{
460-
TRACE_EVENT0("impeller", "RenderBufferStorageInitialization");
461465
if (type_ == Type::kRenderBufferMultisampled) {
462-
gl.RenderbufferStorageMultisampleEXT(
463-
GL_RENDERBUFFER, // target
464-
4, // samples
465-
render_buffer_format.value(), // internal format
466-
size.width, // width
467-
size.height // height
468-
);
466+
// BEWARE: these functions are not at all equivalent! the extensions
467+
// are from EXT_multisampled_render_to_texture and cannot be used
468+
// with regular GLES 3.0 multisampled renderbuffers/textures.
469+
if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
470+
gl.RenderbufferStorageMultisampleEXT(
471+
/*target=*/GL_RENDERBUFFER, //
472+
/*samples=*/4, //
473+
/*internal_format=*/render_buffer_format.value(), //
474+
/*width=*/size.width, //
475+
/*height=*/size.height //
476+
);
477+
} else {
478+
gl.RenderbufferStorageMultisample(
479+
/*target=*/GL_RENDERBUFFER, //
480+
/*samples=*/4, //
481+
/*internal_format=*/render_buffer_format.value(), //
482+
/*width=*/size.width, //
483+
/*height=*/size.height //
484+
);
485+
}
469486
} else {
470487
gl.RenderbufferStorage(
471-
GL_RENDERBUFFER, // target
472-
render_buffer_format.value(), // internal format
473-
size.width, // width
474-
size.height // height
488+
/*target=*/GL_RENDERBUFFER, //
489+
/*internal_format=*/render_buffer_format.value(), //
490+
/*width=*/size.width, //
491+
/*height=*/size.height //
475492
);
476493
}
477494
}

0 commit comments

Comments
 (0)