Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit df915a2

Browse files
authored
[Impeller] libImpeller: Reset the GL state when transitioning control back to the embedder. (#56597)
Impeller is resilient to OpenGL state being trampled upon when accessing the GL context. But the embedder may not necessarily be. Ideally, we'd be using saving the state and restoring it. But that might be too involved. For now, this sets the GL state to a sane "clean" state. We could, in theory, do this after each render pass but that unnecessarily increases API traffic. For now, I have added it at the transition of the embedder boundary.
1 parent 050c6b7 commit df915a2

File tree

8 files changed

+92
-15
lines changed

8 files changed

+92
-15
lines changed

impeller/renderer/backend/gles/context_gles.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "impeller/base/validation.h"
1010
#include "impeller/renderer/backend/gles/command_buffer_gles.h"
1111
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
12+
#include "impeller/renderer/backend/gles/render_pass_gles.h"
1213
#include "impeller/renderer/command_queue.h"
1314

1415
namespace impeller {
@@ -145,4 +146,15 @@ std::shared_ptr<CommandQueue> ContextGLES::GetCommandQueue() const {
145146
return command_queue_;
146147
}
147148

149+
// |Context|
150+
void ContextGLES::ResetThreadLocalState() const {
151+
if (!IsValid()) {
152+
return;
153+
}
154+
[[maybe_unused]] auto result =
155+
reactor_->AddOperation([](const ReactorGLES& reactor) {
156+
RenderPassGLES::ResetGLState(reactor.GetProcTable());
157+
});
158+
}
159+
148160
} // namespace impeller

impeller/renderer/backend/gles/context_gles.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class ContextGLES final : public Context,
9393
// |Context|
9494
void Shutdown() override;
9595

96+
// |Context|
97+
void ResetThreadLocalState() const override;
98+
9699
ContextGLES(const ContextGLES&) = delete;
97100

98101
ContextGLES& operator=(const ContextGLES&) = delete;

impeller/renderer/backend/gles/render_pass_gles.cc

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ static bool BindVertexBuffer(const ProcTableGLES& gl,
172172
return true;
173173
}
174174

175+
void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
176+
gl.Disable(GL_SCISSOR_TEST);
177+
gl.Disable(GL_DEPTH_TEST);
178+
gl.Disable(GL_STENCIL_TEST);
179+
gl.Disable(GL_CULL_FACE);
180+
gl.Disable(GL_BLEND);
181+
gl.Disable(GL_DITHER);
182+
gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
183+
gl.DepthMask(GL_TRUE);
184+
gl.StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
185+
gl.StencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
186+
}
187+
175188
[[nodiscard]] bool EncodeCommandsInReactor(
176189
const RenderPassData& pass_data,
177190
const std::shared_ptr<Allocator>& transients_allocator,
@@ -267,16 +280,7 @@ static bool BindVertexBuffer(const ProcTableGLES& gl,
267280
clear_bits |= GL_STENCIL_BUFFER_BIT;
268281
}
269282

270-
gl.Disable(GL_SCISSOR_TEST);
271-
gl.Disable(GL_DEPTH_TEST);
272-
gl.Disable(GL_STENCIL_TEST);
273-
gl.Disable(GL_CULL_FACE);
274-
gl.Disable(GL_BLEND);
275-
gl.Disable(GL_DITHER);
276-
gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
277-
gl.DepthMask(GL_TRUE);
278-
gl.StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
279-
gl.StencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
283+
RenderPassGLES::ResetGLState(gl);
280284

281285
gl.Clear(clear_bits);
282286

impeller/renderer/backend/gles/render_pass_gles.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class RenderPassGLES final
1919
// |RenderPass|
2020
~RenderPassGLES() override;
2121

22+
static void ResetGLState(const ProcTableGLES& gl);
23+
2224
private:
2325
friend class CommandBufferGLES;
2426

impeller/renderer/context.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ bool Context::FlushCommandBuffers() {
2525
return true;
2626
}
2727

28+
std::shared_ptr<const IdleWaiter> Context::GetIdleWaiter() const {
29+
return nullptr;
30+
}
31+
32+
void Context::ResetThreadLocalState() const {
33+
// Nothing to do.
34+
}
35+
2836
} // namespace impeller

impeller/renderer/context.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,17 @@ class Context {
222222
/// rendering a 2D workload.
223223
[[nodiscard]] virtual bool FlushCommandBuffers();
224224

225-
virtual std::shared_ptr<const IdleWaiter> GetIdleWaiter() const {
226-
return nullptr;
227-
}
225+
virtual std::shared_ptr<const IdleWaiter> GetIdleWaiter() const;
226+
227+
//----------------------------------------------------------------------------
228+
/// Resets any thread local state that may interfere with embedders.
229+
///
230+
/// Today, only the OpenGL backend can trample on thread local state that the
231+
/// embedder can access. This call puts the GL state in a sane "clean" state.
232+
///
233+
/// Impeller itself is resilient to a dirty thread local state table.
234+
///
235+
virtual void ResetThreadLocalState() const;
228236

229237
protected:
230238
Context();

impeller/toolkit/interop/impeller_unittests.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,44 @@ TEST_P(InteropPlaygroundTest, CanCreateOpenGLImage) {
192192
}));
193193
}
194194

195+
TEST_P(InteropPlaygroundTest, ClearsOpenGLStancilStateAfterTransition) {
196+
auto context = GetInteropContext();
197+
auto impeller_context = context->GetContext();
198+
if (impeller_context->GetBackendType() !=
199+
impeller::Context::BackendType::kOpenGLES) {
200+
GTEST_SKIP() << "This test works with OpenGL handles is only suitable for "
201+
"that backend.";
202+
return;
203+
}
204+
const auto& gl_context = ContextGLES::Cast(*impeller_context);
205+
const auto& gl = gl_context.GetReactor()->GetProcTable();
206+
auto builder =
207+
Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
208+
auto paint = Adopt<Paint>(ImpellerPaintNew());
209+
ImpellerColor color = {0.0, 0.0, 1.0, 1.0};
210+
ImpellerPaintSetColor(paint.GetC(), &color);
211+
ImpellerRect rect = {10, 20, 100, 200};
212+
ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
213+
color = {1.0, 0.0, 0.0, 1.0};
214+
ImpellerPaintSetColor(paint.GetC(), &color);
215+
ImpellerDisplayListBuilderTranslate(builder.GetC(), 110, 210);
216+
ImpellerDisplayListBuilderClipRect(builder.GetC(), &rect,
217+
kImpellerClipOperationDifference);
218+
ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
219+
auto dl = Adopt<DisplayList>(
220+
ImpellerDisplayListBuilderCreateDisplayListNew(builder.GetC()));
221+
ASSERT_TRUE(dl);
222+
ASSERT_TRUE(
223+
OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
224+
ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
225+
// OpenGL state is reset even though the operations above enable a
226+
// stencil check.
227+
GLboolean stencil_enabled = true;
228+
gl.GetBooleanv(GL_STENCIL_TEST, &stencil_enabled);
229+
return stencil_enabled == GL_FALSE;
230+
}));
231+
}
232+
195233
TEST_P(InteropPlaygroundTest, CanCreateParagraphs) {
196234
// Create a typography context.
197235
auto type_context = Adopt<TypographyContext>(ImpellerTypographyContextNew());

impeller/toolkit/interop/surface.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ bool Surface::DrawDisplayList(const DisplayList& dl) const {
6262
auto skia_cull_rect =
6363
SkIRect::MakeWH(cull_rect.GetWidth(), cull_rect.GetHeight());
6464

65-
return RenderToOnscreen(content_context, render_target, display_list,
66-
skia_cull_rect, /*reset_host_buffer=*/true);
65+
auto result = RenderToOnscreen(content_context, render_target, display_list,
66+
skia_cull_rect, /*reset_host_buffer=*/true);
67+
context_->GetContext()->ResetThreadLocalState();
68+
return result;
6769
}
6870

6971
} // namespace impeller::interop

0 commit comments

Comments
 (0)