[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 https://github.com/flutter/flutter/issues/158360 (again)
This commit is contained in:
parent
1beba504d9
commit
4fef40c0f6
@ -133,6 +133,10 @@ 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();
|
||||
|
@ -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) \
|
||||
|
@ -130,6 +130,7 @@ 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;
|
||||
|
||||
@ -214,6 +215,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
|
||||
TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
|
||||
const bool is_default_fbo = color_gles.IsWrapped();
|
||||
|
||||
std::optional<GLuint> fbo = 0;
|
||||
if (is_default_fbo) {
|
||||
if (color_gles.GetFBO().has_value()) {
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
@ -222,7 +224,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
|
||||
} else {
|
||||
// Create and bind an offscreen FBO.
|
||||
if (!color_gles.GetCachedFBO().IsDead()) {
|
||||
auto fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
|
||||
fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
|
||||
if (!fbo.has_value()) {
|
||||
return false;
|
||||
}
|
||||
@ -231,7 +233,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
|
||||
HandleGLES cached_fbo =
|
||||
reactor.CreateUntrackedHandle(HandleType::kFrameBuffer);
|
||||
color_gles.SetCachedFBO(cached_fbo);
|
||||
auto fbo = reactor.GetGLHandle(cached_fbo);
|
||||
fbo = reactor.GetGLHandle(cached_fbo);
|
||||
if (!fbo.has_value()) {
|
||||
return false;
|
||||
}
|
||||
@ -520,6 +522,54 @@ 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.value());
|
||||
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
||||
|
||||
RenderPassGLES::ResetGLState(gl);
|
||||
auto size = pass_data.color_attachment->GetSize();
|
||||
|
||||
gl.BlitFramebuffer(/*srcX0=*/0,
|
||||
/*srcY0=*/0,
|
||||
/*srcX1=*/size.width,
|
||||
/*srcY1=*/size.height,
|
||||
/*dstX0=*/0,
|
||||
/*dstY0=*/0,
|
||||
/*dstX1=*/size.width,
|
||||
/*dstY1=*/size.height,
|
||||
/*mask=*/GL_COLOR_BUFFER_BIT,
|
||||
/*filter=*/GL_NEAREST);
|
||||
|
||||
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.value());
|
||||
}
|
||||
|
||||
if (gl.DiscardFramebufferEXT.IsAvailable()) {
|
||||
std::array<GLenum, 3> attachments;
|
||||
size_t attachment_count = 0;
|
||||
@ -580,6 +630,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 =
|
||||
@ -587,10 +638,13 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
|
||||
|
||||
// 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.
|
||||
// discard the attachment when we are done. If not using
|
||||
// EXT_multisampled_render_to_texture but still using MSAA we discard the
|
||||
// attachment as normal.
|
||||
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();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "impeller/core/texture_descriptor.h"
|
||||
#include "impeller/renderer/backend/gles/handle_gles.h"
|
||||
#include "impeller/renderer/backend/gles/proc_table_gles.h"
|
||||
#include "impeller/renderer/backend/gles/test/mock_gles.h"
|
||||
|
||||
namespace impeller::testing {
|
||||
|
||||
@ -77,11 +78,19 @@ TEST_P(TextureGLESTest, Binds2DTexture) {
|
||||
|
||||
ASSERT_TRUE(texture);
|
||||
|
||||
EXPECT_EQ(
|
||||
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
|
||||
TextureGLES::Type::kTexture);
|
||||
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
|
||||
TextureGLES::Type::kTextureMultisampled);
|
||||
if (GetContext()->GetCapabilities()->SupportsImplicitResolvingMSAA()) {
|
||||
EXPECT_EQ(
|
||||
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
|
||||
TextureGLES::Type::kTexture);
|
||||
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
|
||||
TextureGLES::Type::kTextureMultisampled);
|
||||
} else {
|
||||
EXPECT_EQ(
|
||||
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
|
||||
TextureGLES::Type::kRenderBufferMultisampled);
|
||||
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
|
||||
TextureGLES::Type::kRenderBufferMultisampled);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impeller::testing
|
||||
|
@ -45,7 +45,8 @@ static bool IsDepthStencilFormat(PixelFormat format) {
|
||||
}
|
||||
|
||||
static TextureGLES::Type GetTextureTypeFromDescriptor(
|
||||
const TextureDescriptor& desc) {
|
||||
const TextureDescriptor& desc,
|
||||
const std::shared_ptr<const CapabilitiesGLES>& capabilities) {
|
||||
const auto usage = static_cast<TextureUsageMask>(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 ? (capabilities->SupportsImplicitResolvingMSAA()
|
||||
? TextureGLES::Type::kTextureMultisampled
|
||||
: TextureGLES::Type::kRenderBufferMultisampled)
|
||||
: TextureGLES::Type::kTexture;
|
||||
}
|
||||
|
||||
@ -192,7 +195,9 @@ TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
|
||||
std::optional<HandleGLES> external_handle)
|
||||
: Texture(desc),
|
||||
reactor_(std::move(reactor)),
|
||||
type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
|
||||
type_(GetTextureTypeFromDescriptor(
|
||||
GetTextureDescriptor(),
|
||||
reactor_->GetProcTable().GetCapabilities())),
|
||||
handle_(external_handle.has_value()
|
||||
? external_handle.value()
|
||||
: reactor_->CreateUntrackedHandle(ToHandleType(type_))),
|
||||
@ -367,7 +372,7 @@ static std::optional<GLenum> 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:
|
||||
@ -457,21 +462,33 @@ void TextureGLES::InitializeContentsIfNecessary() const {
|
||||
}
|
||||
gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
|
||||
{
|
||||
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 //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user