Skip to content

Commit

Permalink
[Impeller] Switch from glBlitFramebuffer to implicit MSAA resolutio…
Browse files Browse the repository at this point in the history
…n. (flutter#47282)

Closes flutter/flutter#137093.

This widens supports from Open GLES 3.x to Open GLES 2.x, and uses [ARM
GPU Best
Practices](https://developer.arm.com/documentation/101897/0301/Fragment-shading/Multisampling-for-OpenGL-ES):

> Do not use `glBlitFramebuffer()` to implement a multisample resolve.

This PR:

- Removes usage of `glBlitFramebuffer`
- Adds the capability check, `SupportsImplicitResolvingMSAA`, which is
`false` outside of GLES
- Does not discard color attachments resolved by
`EXT_multisampled_render_to_texture` (done implicitly)

I spoke to @jonahwilliams about the changes to the HAL, who I believe
also talked to @bdero about it. The short explantation is that, with the
`EXT_multisampled_render_to_texture`, we can be more efficient by
letting GLES perform multisampled rendering for us (no per-sample data
is written out).

See also
https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
for details.
  • Loading branch information
matanlurey authored Oct 30, 2023
1 parent 4f87c20 commit c7e2ba7
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 64 deletions.
37 changes: 21 additions & 16 deletions impeller/renderer/backend/gles/capabilities_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ static const constexpr char* kNvidiaTextureBorderClampExt =
static const constexpr char* kOESTextureBorderClampExt =
"GL_OES_texture_border_clamp";

// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
static const constexpr char* kMultisampledRenderToTextureExt =
"GL_EXT_multisampled_render_to_texture2";

CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
{
GLint value = 0;
Expand All @@ -32,7 +36,9 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
max_cube_map_texture_size = value;
}

if (gl.GetDescription()->IsES()) {
auto const desc = gl.GetDescription();

if (desc->IsES()) {
GLint value = 0;
gl.GetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &value);
max_fragment_uniform_vectors = value;
Expand All @@ -56,7 +62,7 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
max_texture_size = ISize{value, value};
}

if (gl.GetDescription()->IsES()) {
if (desc->IsES()) {
GLint value = 0;
gl.GetIntegerv(GL_MAX_VARYING_VECTORS, &value);
max_varying_vectors = value;
Expand All @@ -74,7 +80,7 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
max_vertex_texture_image_units = value;
}

if (gl.GetDescription()->IsES()) {
if (desc->IsES()) {
GLint value = 0;
gl.GetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &value);
max_vertex_uniform_vectors = value;
Expand All @@ -92,31 +98,26 @@ CapabilitiesGLES::CapabilitiesGLES(const ProcTableGLES& gl) {
num_compressed_texture_formats = value;
}

if (gl.GetDescription()->IsES()) {
if (desc->IsES()) {
GLint value = 0;
gl.GetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &value);
num_shader_binary_formats = value;
}

supports_framebuffer_fetch_ =
gl.GetDescription()->HasExtension(kFramebufferFetchExt);
supports_framebuffer_fetch_ = desc->HasExtension(kFramebufferFetchExt);

if (gl.GetDescription()->HasExtension(kTextureBorderClampExt) ||
gl.GetDescription()->HasExtension(kNvidiaTextureBorderClampExt) ||
gl.GetDescription()->HasExtension(kOESTextureBorderClampExt)) {
if (desc->HasExtension(kTextureBorderClampExt) ||
desc->HasExtension(kNvidiaTextureBorderClampExt) ||
desc->HasExtension(kOESTextureBorderClampExt)) {
supports_decal_sampler_address_mode_ = true;
}

if (gl.GetDescription()->HasExtension(
"GL_EXT_multisampled_render_to_texture2") &&
// 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()) {
if (desc->HasExtension(kMultisampledRenderToTextureExt)) {
supports_implicit_msaa_ = true;

// 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;
}
}
Expand All @@ -140,6 +141,10 @@ bool CapabilitiesGLES::SupportsOffscreenMSAA() const {
return supports_offscreen_msaa_;
}

bool CapabilitiesGLES::SupportsImplicitResolvingMSAA() const {
return supports_implicit_msaa_;
}

bool CapabilitiesGLES::SupportsSSBO() const {
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions impeller/renderer/backend/gles/capabilities_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class CapabilitiesGLES final
// |Capabilities|
bool SupportsOffscreenMSAA() const override;

// |Capabilities|
bool SupportsImplicitResolvingMSAA() const override;

// |Capabilities|
bool SupportsSSBO() const override;

Expand Down Expand Up @@ -119,6 +122,7 @@ class CapabilitiesGLES final
bool supports_framebuffer_fetch_ = false;
bool supports_decal_sampler_address_mode_ = false;
bool supports_offscreen_msaa_ = false;
bool supports_implicit_msaa_ = false;
};

} // namespace impeller
57 changes: 9 additions & 48 deletions impeller/renderer/backend/gles/render_pass_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "impeller/renderer/backend/gles/render_pass_gles.h"

#include "GLES3/gl3.h"
#include "flutter/fml/trace_event.h"
#include "fml/closure.h"
#include "fml/logging.h"
Expand Down Expand Up @@ -127,7 +128,6 @@ struct RenderPassData {
Scalar clear_depth = 1.0;

std::shared_ptr<Texture> color_attachment;
std::shared_ptr<Texture> resolve_attachment;
std::shared_ptr<Texture> depth_attachment;
std::shared_ptr<Texture> stencil_attachment;

Expand Down Expand Up @@ -474,52 +474,6 @@ 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;
fml::ScopedCleanupClosure delete_draw_fbo([&gl, &draw_fbo, fbo]() {
if (draw_fbo != GL_NONE) {
gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
gl.DeleteFramebuffers(1u, &draw_fbo);
}
});

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<GLenum> attachments;

Expand Down Expand Up @@ -576,12 +530,19 @@ 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 =
CanDiscardAttachmentWhenDone(color0.store_action);

// When we are using EXT_multisampled_render_to_texture, it is implicitly
// 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;
}

//----------------------------------------------------------------------------
/// Setup depth data.
///
Expand Down
5 changes: 5 additions & 0 deletions impeller/renderer/backend/vulkan/capabilities_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,11 @@ bool CapabilitiesVK::SupportsOffscreenMSAA() const {
return true;
}

// |Capabilities|
bool CapabilitiesVK::SupportsImplicitResolvingMSAA() const {
return false;
}

// |Capabilities|
bool CapabilitiesVK::SupportsSSBO() const {
return true;
Expand Down
3 changes: 3 additions & 0 deletions impeller/renderer/backend/vulkan/capabilities_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class CapabilitiesVK final : public Capabilities,
// |Capabilities|
bool SupportsOffscreenMSAA() const override;

// |Capabilities|
bool SupportsImplicitResolvingMSAA() const override;

// |Capabilities|
bool SupportsSSBO() const override;

Expand Down
3 changes: 3 additions & 0 deletions impeller/renderer/capabilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class StandardCapabilities final : public Capabilities {
return supports_offscreen_msaa_;
}

// |Capabilities|
bool SupportsImplicitResolvingMSAA() const override { return false; }

// |Capabilities|
bool SupportsSSBO() const override { return supports_ssbo_; }

Expand Down
5 changes: 5 additions & 0 deletions impeller/renderer/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class Capabilities {
/// color/stencil textures.
virtual bool SupportsOffscreenMSAA() const = 0;

/// @brief Whether the context backend supports multisampled rendering to
/// the on-screen surface without requiring an explicit resolve of
/// the MSAA color attachment.
virtual bool SupportsImplicitResolvingMSAA() const = 0;

/// @brief Whether the context backend supports binding Shader Storage Buffer
/// Objects (SSBOs) to pipelines.
virtual bool SupportsSSBO() const = 0;
Expand Down
16 changes: 16 additions & 0 deletions impeller/renderer/render_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ RenderTarget RenderTarget::CreateOffscreenMSAA(
color0_tex_desc.size = size;
color0_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);

if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
// See below ("SupportsImplicitResolvingMSAA") for more details.
color0_tex_desc.storage_mode = StorageMode::kDevicePrivate;
}

auto color0_msaa_tex = allocator.CreateTexture(color0_tex_desc);
if (!color0_msaa_tex) {
VALIDATION_LOG << "Could not create multisample color texture.";
Expand Down Expand Up @@ -322,6 +327,17 @@ RenderTarget RenderTarget::CreateOffscreenMSAA(
color0.texture = color0_msaa_tex;
color0.resolve_texture = color0_resolve_tex;

if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
// If implicit MSAA is supported, then the resolve texture is not needed
// because the multisample texture is automatically resolved. We instead
// provide a view of the multisample texture as the resolve texture (because
// the HAL does expect a resolve texture).
//
// In practice, this is used for GLES 2.0 EXT_multisampled_render_to_texture
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
color0.resolve_texture = color0_msaa_tex;
}

target.SetColorAttachment(color0, 0u);

// Create MSAA stencil texture.
Expand Down

0 comments on commit c7e2ba7

Please sign in to comment.