[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);
|
gl.GetIntegerv(GL_MAX_SAMPLES_EXT, &value);
|
||||||
supports_offscreen_msaa_ = value >= 4;
|
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_es_ = desc->IsES();
|
||||||
is_angle_ = desc->IsANGLE();
|
is_angle_ = desc->IsANGLE();
|
||||||
|
@ -247,6 +247,7 @@ void(glDepthRange)(GLdouble n, GLdouble f);
|
|||||||
PROC(UniformBlockBinding); \
|
PROC(UniformBlockBinding); \
|
||||||
PROC(BindBufferRange); \
|
PROC(BindBufferRange); \
|
||||||
PROC(WaitSync); \
|
PROC(WaitSync); \
|
||||||
|
PROC(RenderbufferStorageMultisample) \
|
||||||
PROC(BlitFramebuffer);
|
PROC(BlitFramebuffer);
|
||||||
|
|
||||||
#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \
|
#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \
|
||||||
|
@ -130,6 +130,7 @@ struct RenderPassData {
|
|||||||
Scalar clear_depth = 1.0;
|
Scalar clear_depth = 1.0;
|
||||||
|
|
||||||
std::shared_ptr<Texture> color_attachment;
|
std::shared_ptr<Texture> color_attachment;
|
||||||
|
std::shared_ptr<Texture> resolve_attachment;
|
||||||
std::shared_ptr<Texture> depth_attachment;
|
std::shared_ptr<Texture> depth_attachment;
|
||||||
std::shared_ptr<Texture> stencil_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);
|
TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
|
||||||
const bool is_default_fbo = color_gles.IsWrapped();
|
const bool is_default_fbo = color_gles.IsWrapped();
|
||||||
|
|
||||||
|
std::optional<GLuint> fbo = 0;
|
||||||
if (is_default_fbo) {
|
if (is_default_fbo) {
|
||||||
if (color_gles.GetFBO().has_value()) {
|
if (color_gles.GetFBO().has_value()) {
|
||||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||||
@ -222,7 +224,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
|
|||||||
} else {
|
} else {
|
||||||
// Create and bind an offscreen FBO.
|
// Create and bind an offscreen FBO.
|
||||||
if (!color_gles.GetCachedFBO().IsDead()) {
|
if (!color_gles.GetCachedFBO().IsDead()) {
|
||||||
auto fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
|
fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
|
||||||
if (!fbo.has_value()) {
|
if (!fbo.has_value()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -231,7 +233,7 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
|
|||||||
HandleGLES cached_fbo =
|
HandleGLES cached_fbo =
|
||||||
reactor.CreateUntrackedHandle(HandleType::kFrameBuffer);
|
reactor.CreateUntrackedHandle(HandleType::kFrameBuffer);
|
||||||
color_gles.SetCachedFBO(cached_fbo);
|
color_gles.SetCachedFBO(cached_fbo);
|
||||||
auto fbo = reactor.GetGLHandle(cached_fbo);
|
fbo = reactor.GetGLHandle(cached_fbo);
|
||||||
if (!fbo.has_value()) {
|
if (!fbo.has_value()) {
|
||||||
return false;
|
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()) {
|
if (gl.DiscardFramebufferEXT.IsAvailable()) {
|
||||||
std::array<GLenum, 3> attachments;
|
std::array<GLenum, 3> attachments;
|
||||||
size_t attachment_count = 0;
|
size_t attachment_count = 0;
|
||||||
@ -580,6 +630,7 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
|
|||||||
/// Setup color data.
|
/// Setup color data.
|
||||||
///
|
///
|
||||||
pass_data->color_attachment = color0.texture;
|
pass_data->color_attachment = color0.texture;
|
||||||
|
pass_data->resolve_attachment = color0.resolve_texture;
|
||||||
pass_data->clear_color = color0.clear_color;
|
pass_data->clear_color = color0.clear_color;
|
||||||
pass_data->clear_color_attachment = CanClearAttachment(color0.load_action);
|
pass_data->clear_color_attachment = CanClearAttachment(color0.load_action);
|
||||||
pass_data->discard_color_attachment =
|
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
|
// 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
|
// 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) {
|
if (color0.resolve_texture) {
|
||||||
FML_DCHECK(context.GetCapabilities()->SupportsImplicitResolvingMSAA());
|
pass_data->discard_color_attachment =
|
||||||
pass_data->discard_color_attachment = false;
|
pass_data->discard_color_attachment &&
|
||||||
|
!context.GetCapabilities()->SupportsImplicitResolvingMSAA();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "impeller/core/texture_descriptor.h"
|
#include "impeller/core/texture_descriptor.h"
|
||||||
#include "impeller/renderer/backend/gles/handle_gles.h"
|
#include "impeller/renderer/backend/gles/handle_gles.h"
|
||||||
#include "impeller/renderer/backend/gles/proc_table_gles.h"
|
#include "impeller/renderer/backend/gles/proc_table_gles.h"
|
||||||
|
#include "impeller/renderer/backend/gles/test/mock_gles.h"
|
||||||
|
|
||||||
namespace impeller::testing {
|
namespace impeller::testing {
|
||||||
|
|
||||||
@ -77,11 +78,19 @@ TEST_P(TextureGLESTest, Binds2DTexture) {
|
|||||||
|
|
||||||
ASSERT_TRUE(texture);
|
ASSERT_TRUE(texture);
|
||||||
|
|
||||||
EXPECT_EQ(
|
if (GetContext()->GetCapabilities()->SupportsImplicitResolvingMSAA()) {
|
||||||
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
|
EXPECT_EQ(
|
||||||
TextureGLES::Type::kTexture);
|
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
|
||||||
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
|
TextureGLES::Type::kTexture);
|
||||||
TextureGLES::Type::kTextureMultisampled);
|
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
|
} // namespace impeller::testing
|
||||||
|
@ -45,7 +45,8 @@ static bool IsDepthStencilFormat(PixelFormat format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static TextureGLES::Type GetTextureTypeFromDescriptor(
|
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 usage = static_cast<TextureUsageMask>(desc.usage);
|
||||||
const auto render_target = TextureUsage::kRenderTarget;
|
const auto render_target = TextureUsage::kRenderTarget;
|
||||||
const auto is_msaa = desc.sample_count == SampleCount::kCount4;
|
const auto is_msaa = desc.sample_count == SampleCount::kCount4;
|
||||||
@ -53,7 +54,9 @@ static TextureGLES::Type GetTextureTypeFromDescriptor(
|
|||||||
return is_msaa ? TextureGLES::Type::kRenderBufferMultisampled
|
return is_msaa ? TextureGLES::Type::kRenderBufferMultisampled
|
||||||
: TextureGLES::Type::kRenderBuffer;
|
: TextureGLES::Type::kRenderBuffer;
|
||||||
}
|
}
|
||||||
return is_msaa ? TextureGLES::Type::kTextureMultisampled
|
return is_msaa ? (capabilities->SupportsImplicitResolvingMSAA()
|
||||||
|
? TextureGLES::Type::kTextureMultisampled
|
||||||
|
: TextureGLES::Type::kRenderBufferMultisampled)
|
||||||
: TextureGLES::Type::kTexture;
|
: TextureGLES::Type::kTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +195,9 @@ TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
|
|||||||
std::optional<HandleGLES> external_handle)
|
std::optional<HandleGLES> external_handle)
|
||||||
: Texture(desc),
|
: Texture(desc),
|
||||||
reactor_(std::move(reactor)),
|
reactor_(std::move(reactor)),
|
||||||
type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
|
type_(GetTextureTypeFromDescriptor(
|
||||||
|
GetTextureDescriptor(),
|
||||||
|
reactor_->GetProcTable().GetCapabilities())),
|
||||||
handle_(external_handle.has_value()
|
handle_(external_handle.has_value()
|
||||||
? external_handle.value()
|
? external_handle.value()
|
||||||
: reactor_->CreateUntrackedHandle(ToHandleType(type_))),
|
: reactor_->CreateUntrackedHandle(ToHandleType(type_))),
|
||||||
@ -367,7 +372,7 @@ static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
|
|||||||
switch (format) {
|
switch (format) {
|
||||||
case PixelFormat::kB8G8R8A8UNormInt:
|
case PixelFormat::kB8G8R8A8UNormInt:
|
||||||
case PixelFormat::kR8G8B8A8UNormInt:
|
case PixelFormat::kR8G8B8A8UNormInt:
|
||||||
return GL_RGBA4;
|
return GL_RGBA8;
|
||||||
case PixelFormat::kR32G32B32A32Float:
|
case PixelFormat::kR32G32B32A32Float:
|
||||||
return GL_RGBA32F;
|
return GL_RGBA32F;
|
||||||
case PixelFormat::kR16G16B16A16Float:
|
case PixelFormat::kR16G16B16A16Float:
|
||||||
@ -457,21 +462,33 @@ void TextureGLES::InitializeContentsIfNecessary() const {
|
|||||||
}
|
}
|
||||||
gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
|
gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
|
||||||
{
|
{
|
||||||
TRACE_EVENT0("impeller", "RenderBufferStorageInitialization");
|
|
||||||
if (type_ == Type::kRenderBufferMultisampled) {
|
if (type_ == Type::kRenderBufferMultisampled) {
|
||||||
gl.RenderbufferStorageMultisampleEXT(
|
// BEWARE: these functions are not at all equivalent! the extensions
|
||||||
GL_RENDERBUFFER, // target
|
// are from EXT_multisampled_render_to_texture and cannot be used
|
||||||
4, // samples
|
// with regular GLES 3.0 multisampled renderbuffers/textures.
|
||||||
render_buffer_format.value(), // internal format
|
if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
|
||||||
size.width, // width
|
gl.RenderbufferStorageMultisampleEXT(
|
||||||
size.height // height
|
/*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 {
|
} else {
|
||||||
gl.RenderbufferStorage(
|
gl.RenderbufferStorage(
|
||||||
GL_RENDERBUFFER, // target
|
/*target=*/GL_RENDERBUFFER, //
|
||||||
render_buffer_format.value(), // internal format
|
/*internal_format=*/render_buffer_format.value(), //
|
||||||
size.width, // width
|
/*width=*/size.width, //
|
||||||
size.height // height
|
/*height=*/size.height //
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user