flypig.co.uk

List items

Items from the current list are shown below.

Gecko

29 Jun 2024 : Day 273 #
Continuing on with our investigation from yesterday, we've got a WebView that now runs but doesn't render. Today I need to find out the reason. The good news is that the WebGL on this build is still working, so we're definitely getting closer.

If you've been following along you'll know that this isn't the first time I've needed to get the WebView render pipeline working. The difference is that this time I have the delta between where we are now and where there's a working WebView, I just need to tread carefully enough between here and there so as not to destroy the WebGL in the process.

So, first up, why is the WebView not working. From what we saw yesterday I already know that this is bounded by a broken call to GLContextProvider::CreateOffscreen() which is returning null. I want to step through the code to find out why.

Last night I couldn't do this because I only had a partial build (meaning the debug symbols and source were misaligned with the binary). I ran a build overnight to fix that. So now it's time to step through.

I've placed a breakpoint on CompositorOGL::CreateContext(). Let's see what we can see when we step through the code from there. Keep in mind that we're interested in the call to CreateOffscreen() and what it's returning.
Thread 38 "Compositor" hit Breakpoint 1, mozilla::layers::
    CompositorOGL::CreateContext (this=this@entry=0x7ed4002ed0)
    at gfx/layers/opengl/CompositorOGL.cpp:227
227     already_AddRefed<mozilla::gl::GLContext> CompositorOGL::CreateContext() 
    {
(gdb) n
231       nsIWidget* widget = mWidget->RealWidget();
(gdb) n
232       void* widgetOpenGLContext =
(gdb) n
234       if (widgetOpenGLContext) {
(gdb) n
248       if (!context && gfxEnv::LayersPreferOffscreen()) {
(gdb) n
249         nsCString discardFailureId;
(gdb) n
257         context = GLContextProvider::CreateOffscreen(
(gdb) p context
$1 = {mRawPtr = 0x0}
(gdb) s
mozilla::gl::GLContextProviderEGL::CreateOffscreen (size=..., 
    flags=flags@entry=mozilla::gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE, 
    out_failureId=out_failureId@entry=0x7f1f96b1c8)
    at gfx/gl/GLContextProviderEGL.cpp:1264
1264      gl = CreateHeadless({CreateContextFlags::REQUIRE_COMPAT_PROFILE}, 
    out_failureId);
(gdb) p gl
$2 = {mRawPtr = 0x0}
(gdb) n
1267      if (!gl || !gl->IsOffscreenSizeAllowed(size)) {
(gdb) p gl
$3 = {mRawPtr = 0x7ed419ee40}
(gdb) n
1271      UniquePtr<GLScreenBuffer> newScreen = GLScreenBuffer::Create(gl, 
    size);
(gdb) p size
$4 = (const mozilla::gfx::IntSize &) @0x7ed4002fc4: {<mozilla::gfx::
    BaseSize<int, mozilla::gfx::IntSizeTyped<mozilla::gfx::UnknownUnits> >> = 
    {{{
        width = 1080, height = 2520}, components = {1080, 2520}}}, <mozilla::
    gfx::UnknownUnits> = {<No data fields>}, <No data fields>}
(gdb) n
1272      if ((!newScreen) || (!newScreen->Resize(size))) {
(gdb) p newScreen.mTuple.mFirstA
$6 = (mozilla::gl::GLScreenBuffer *) 0x7ed4003ba0
(gdb) n
1271      UniquePtr<GLScreenBuffer> newScreen = GLScreenBuffer::Create(gl, 
    size);
(gdb) n
1262      RefPtr<GLContext> gl;
(gdb) n
mozilla::layers::CompositorOGL::CreateContext (this=this@entry=0x7ed4002ed0)
    at gfx/layers/opengl/CompositorOGL.cpp:249
249         nsCString discardFailureId;
(gdb) n
263       if (!context) {
(gdb) p context
$7 = {mRawPtr = 0x0}
(gdb) 
As we can see from this, we drop in to the CreateOffscreen() method. This calls CreateHeadless() which gives us what appears to be a valid context. Then we get a call to GLScreenBuffer::Create() which gives us a valid GLScreenBuffer object.

It's hard to tell from the debug trace, but the line that's failing is the following:
if ((!newScreen) || (!newScreen->Resize(size)))
We can see from the trace that newScreen is valid, so it's the call to Resize() which is returning false. We can't yet tell why. But we can find out by stepping inside the Resize() method to check. That'll require us to re-run the application. Let's give that a go.

To help with this I've set a bunch of breakpoints that will allow me to skip between the relevant parts of the code:
(gdb) info break
Num     Type           Disp Enb Address            What
3       breakpoint     keep y   0x0000007ff11988a4 in mozilla::layers::
    CompositorOGL::CreateContext() 
                                                   at gfx/layers/opengl/
    CompositorOGL.cpp:227
        breakpoint already hit 1 time
4       breakpoint     keep y   0x0000007ff1131540 in mozilla::gl::
    GLContextProviderEGL::CreateOffscreen(mozilla::gfx::IntSizeTyped<mozilla::
    gfx::UnknownUnits> const&, mozilla::gl::CreateContextFlags, 
    nsTSubstring<char>*) 
                                                   at gfx/gl/
    GLContextProviderEGL.cpp:1260
        breakpoint already hit 1 time
5       breakpoint     keep y   0x0000007ff1107644 in mozilla::gl::
    GLScreenBuffer::Resize(mozilla::gfx::IntSizeTyped<mozilla::gfx::
    UnknownUnits> const&) 
                                                   at gfx/gl/GLScreenBuffer.cpp:
    339
        breakpoint already hit 1 time
6       breakpoint     keep y   0x0000007ff110695c in mozilla::gl::
    GLScreenBuffer::Attach(mozilla::gl::SharedSurface*, mozilla::gfx::
    IntSizeTyped<mozilla::gfx::UnknownUnits> const&) 
                                                   at gfx/gl/GLScreenBuffer.cpp:
    275
(gdb) 
Now let's step through, hitting these breakpoints as we go, and take special care not to jump past the Resize() call.
Thread 37 &quot;Compositor&quot; hit Breakpoint 3, mozilla::layers::
    CompositorOGL::CreateContext (this=this@entry=0x7ed8002f10)
    at gfx/layers/opengl/CompositorOGL.cpp:227
227     already_AddRefed<mozilla::gl::GLContext> CompositorOGL::CreateContext() 
    {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 4, mozilla::gl::
    GLContextProviderEGL::CreateOffscreen (size=..., 
    flags=flags@entry=mozilla::gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE, 
    out_failureId=out_failureId@entry=0x7f179ac1c8)
    at gfx/gl/GLContextProviderEGL.cpp:1260
1260        CreateContextFlags flags, nsACString* const out_failureId) {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 5, mozilla::gl::GLScreenBuffer::
    Resize (this=0x7ed8003ba0, size=...)
    at gfx/gl/GLScreenBuffer.cpp:339
339     bool GLScreenBuffer::Resize(const gfx::IntSize& size) {
That's all of our breakpoints hit. We're now at the start of the Resize() method. Let's step through it.
339     bool GLScreenBuffer::Resize(const gfx::IntSize& size) {
(gdb) n
342       if (!newBack) return false;
(gdb) p mFactory.mTuple->mFirstA
$15 = (mozilla::gl::SurfaceFactory *) 0x7ed8004190
(gdb) p newBack.mRawPtr
$17 = (mozilla::layers::SharedSurfaceTextureClient *) 0x7ed81af430
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 6, mozilla::gl::GLScreenBuffer::
    Attach (this=this@entry=0x7ed8003ba0, surf=0x7ed81a1c80, size=...)
    at gfx/gl/GLScreenBuffer.cpp:275
275     bool GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& 
    size) {
Now we're inside the Attach() method. Let's step through this.
275     bool GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& 
    size) {
(gdb) n
276       ScopedBindFramebuffer autoFB(mGL);
(gdb) p size
$18 = (const mozilla::gfx::IntSize &) @0x7ed8003004: {<mozilla::gfx::
    BaseSize<int, mozilla::gfx::IntSizeTyped<mozilla::gfx::UnknownUnits> >> = 
    {{{
        width = 1080, height = 2520}, components = {1080, 2520}}}, <mozilla::
    gfx::UnknownUnits> = {<No data fields>}, <No data fields>}
(gdb) n
278       const bool readNeedsUnlock = (mRead && SharedSurf());
(gdb) n
283       surf->LockProd();
(gdb) n
285       if (mRead && size == Size()) {
(gdb) p mRead.mTuple.mFirstA
$20 = (mozilla::gl::ReadBuffer *) 0x0
(gdb) n
289         UniquePtr<ReadBuffer> read = ReadBuffer::Create(mFactory->mDesc.gl, 
    surf);
(gdb) n
291         if (!read) {
(gdb) n
292           surf->UnlockProd();
(gdb) p read.mTuple.mFirstA
$22 = (mozilla::gl::ReadBuffer *) 0x0
(gdb) 
So read is null and that must be because ReadBuffer::Create() is returning null. We need to find out why and the same drill applies: place a breakpoint on ReadBuffer::Create() and step through the code to try to figure out where it's failing.

Once again, we hit all of the breakpoints in order before we get there. There are more of them this time:
Thread 37 &quot;Compositor&quot; hit Breakpoint 3, mozilla::layers::
    CompositorOGL::CreateContext (this=this@entry=0x7ee0002f10)
    at gfx/layers/opengl/CompositorO
GL.cpp:227
227     already_AddRefed<mozilla::gl::GLContext> CompositorOGL::CreateContext() 
    {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 4, mozilla::gl::
    GLContextProviderEGL::CreateOffscreen (size=...,
    flags=flags@entry=mozilla::gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE, 
    out_failureId=out_failureId@entry=0x7f1f98d1c8)
    at gfx/gl/GLContextProviderEGL.c
pp:1260
1260        CreateContextFlags flags, nsACString* const out_failureId) {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 5, mozilla::gl::GLScreenBuffer::
    Resize (this=0x7ee0003bc0, size=...)
    at gfx/gl/GLScreenBuffer.cpp:339
339     bool GLScreenBuffer::Resize(const gfx::IntSize& size) {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 6, mozilla::gl::GLScreenBuffer::
    Attach (this=this@entry=0x7ee0003bc0, surf=0x7ee01a1ca0, size=...)
    at gfx/gl/GLScreenBuffer.cpp:275
275     bool GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& 
    size) {
(gdb) c
Continuing.

Thread 37 &quot;Compositor&quot; hit Breakpoint 7, mozilla::gl::ReadBuffer::
    Create (gl=0x7ee019ee60, surf=surf@entry=0x7ee01a1ca0)
    at gfx/gl/GLScreenBuffer.cpp:358
358     UniquePtr<ReadBuffer> ReadBuffer::Create(GLContext* gl, SharedSurface* 
    surf) {
Now we're in the right place. Let's step through the ReadBuffer::Create() method to find out what's going wrong.
358     UniquePtr<ReadBuffer> ReadBuffer::Create(GLContext* gl, SharedSurface* 
    surf) {
(gdb) n
361       GLContext::LocalErrorScope localError(*gl);
(gdb) n
366       colorTex = surf->ProdTexture();
(gdb) n
367       target = surf->ProdTextureTarget();
(gdb) n
370       GLuint fb = 0;
(gdb) n
371       gl->fGenFramebuffers(1, &fb);
(gdb) n
372       gl->AttachBuffersToFB(colorTex, 0, 0, 0, fb, target);
(gdb) p fb
$38 = 2
(gdb) p target
$39 = 3553
(gdb) n
374       UniquePtr<ReadBuffer> ret(new ReadBuffer(gl, fb, 0, 0, surf));
(gdb) n
376       GLenum err = localError.GetError();
(gdb) p ret.mTuple.mFirstA
$41 = (mozilla::gl::ReadBuffer *) 0x7ee01a16e0
(gdb) n
378       if (err) return nullptr;
(gdb) p err
$42 = 0
(gdb) n
381       if (needsAcquire) {
(gdb) p needsAcquire
$43 = 255
(gdb) n
382         surf->ProducerReadAcquire();
(gdb) n
384       const bool isComplete = gl->IsFramebufferComplete(fb);
(gdb) n
386         surf->ProducerReadRelease();
(gdb) p isComplete
$44 = false
(gdb) set isComplete=true
(gdb) p isComplete
$45 = true
(gdb) n
389       if (!isComplete) return nullptr;
(gdb) n
374       UniquePtr<ReadBuffer> ret(new ReadBuffer(gl, fb, 0, 0, surf));
(gdb) n
361       GLContext::LocalErrorScope localError(*gl);
(gdb) c
Continuing.
Starting at the top, we can see that it's creating the buffers. It generates a framebuffer, then a ReadBuffer; this all appears to work fine. Then it acquires the buffer. Then things start to go wrong. A check is made to see whether the frame buffer is complete, but the result comes back negative.

This results in the overall method returning early with an error. Is it a real error though? It might be interesting to find out what happens if we force the code to ignore the error and continue on regardless.

But when I forcefully clear the error value using the debugger, I still don't get rendering. There's no crash, but there's also no web page: just a blank screen.

So the question I want to now answer is "Why is IsFramebufferComplete() returning false?". It's worth checking what the method does.
bool GLContext::IsFramebufferComplete(GLuint fb, GLenum* pStatus) {
  MOZ_ASSERT(fb);

  ScopedBindFramebuffer autoFB(this, fb);
  MOZ_GL_ASSERT(this, fIsFramebuffer(fb));

  GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
  if (pStatus) *pStatus = status;

  return status == LOCAL_GL_FRAMEBUFFER_COMPLETE;
}
The call to the ScopedBindFramebuffer constructor will bind the framebuffers using the Init() method:
ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL) : mGL(aGL) {
  Init();
}

/* ScopedBindFramebuffer - Saves and restores with GetUserBoundFB and
 * BindUserFB. */

void ScopedBindFramebuffer::Init() {
  if (mGL->IsSupported(GLFeature::split_framebuffer)) {
    mOldReadFB = mGL->GetReadFB();
    mOldDrawFB = mGL->GetDrawFB();
  } else {
    mOldReadFB = mOldDrawFB = mGL->GetFB();
  }
}
So IsFramebufferComplete() is essentially calling fCheckFramebufferStatus() on the bound framebuffer. Maybe we can find out what the error status is from the value returned by fCheckFramebufferStatus(). That might help. Back to the top of the method we go.
Thread 37 &quot;Compositor&quot; hit Breakpoint 7, mozilla::gl::ReadBuffer::
    Create (gl=0x7ee019ee40, surf=surf@entry=0x7ee01a1c80)
    at gfx/gl/GLScreenBuffer.cpp:358
358     UniquePtr<ReadBuffer> ReadBuffer::Create(GLContext* gl, SharedSurface* 
    surf) {
(gdb) n
361       GLContext::LocalErrorScope localError(*gl);
(gdb) n
366       colorTex = surf->ProdTexture();
(gdb) n
367       target = surf->ProdTextureTarget();
(gdb) n
370       GLuint fb = 0;
(gdb) n
371       gl->fGenFramebuffers(1, &fb);
(gdb) n
372       gl->AttachBuffersToFB(colorTex, 0, 0, 0, fb, target);
(gdb) n

Thread 37 &quot;Compositor&quot; hit Breakpoint 11, mozilla::gl::
    ScopedBindFramebuffer::ScopedBindFramebuffer (this=0x7f1f9db048, 
    aGL=0x7ee019ee40, aNewFB=2)
    at gfx/gl/ScopedGLHelpers.cpp:60
60      ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint 
    aNewFB)
(gdb) n
62        Init();
(gdb) n
63        mGL->BindFB(aNewFB);
(gdb) n
mozilla::gl::GLContext::AttachBuffersToFB (this=this@entry=0x7ee019ee40, 
    colorTex=colorTex@entry=0, colorRB=colorRB@entry=0, 
    depthRB=depthRB@entry=0, 
    stencilRB=stencilRB@entry=0, fb=<optimized out>, target=target@entry=3553)
    at gfx/gl/GLContext.cpp:1755
1755      if (colorTex) {
(gdb) n
1761      } else if (colorRB) {
(gdb) n
1769      if (depthRB) {
(gdb) n
1776      if (stencilRB) {
(gdb) n
mozilla::gl::ReadBuffer::Create (gl=0x7ee019ee40, surf=surf@entry=0x7ee01a1c80)
    at gfx/gl/GLScreenBuffer.cpp:374
374       UniquePtr<ReadBuffer> ret(new ReadBuffer(gl, fb, 0, 0, surf));
(gdb) n
376       GLenum err = localError.GetError();
(gdb) n
378       if (err) return nullptr;
(gdb) n
381       if (needsAcquire) {
(gdb) n
382         surf->ProducerReadAcquire();
(gdb) n
384       const bool isComplete = gl->IsFramebufferComplete(fb);
(gdb) s

Thread 37 &quot;Compositor&quot; hit Breakpoint 9, mozilla::gl::GLContext::
    IsFramebufferComplete (this=this@entry=0x7ee019ee40, fb=2, 
    pStatus=pStatus@entry=0x0)
    at gfx/gl/GLContext.cpp:1734
1734    bool GLContext::IsFramebufferComplete(GLuint fb, GLenum* pStatus) {
(gdb) n
1737      ScopedBindFramebuffer autoFB(this, fb);
(gdb) s

Thread 37 &quot;Compositor&quot; hit Breakpoint 11, mozilla::gl::
    ScopedBindFramebuffer::ScopedBindFramebuffer (this=0x7f1f9db048, 
    aGL=0x7ee019ee40, aNewFB=2)
    at gfx/gl/ScopedGLHelpers.cpp:60
60      ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint 
    aNewFB)
(gdb) n
62        Init();
(gdb) p mOldReadFB
$46 = 530428000
(gdb) p mOldDrawFB
$47 = 127
(gdb) n
63        mGL->BindFB(aNewFB);
(gdb) p aNewFB
$48 = 2
(gdb) p mGL
$49 = (mozilla::gl::GLContext * const) 0x7ee019ee40
(gdb) n
mozilla::gl::GLContext::IsFramebufferComplete (this=this@entry=0x7ee019ee40, 
    fb=<optimized out>, pStatus=pStatus@entry=0x0)
    at gfx/gl/GLContext.cpp:1740
1740      GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
(gdb) n
1741      if (pStatus) *pStatus = status;
(gdb) p status
$50 = 36055
(gdb) p/x status
$51 = 0x8cd7
(gdb) 
So we have an error value coming back from fCheckFramebufferStatus() of 0x8cd7. Checking the documentation and the GLConsts.h file, we can see that the error returned is the following:
#define LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT   0x8CD7
And checking the docs, it tells us that:
 
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned if the framebuffer does not have at least one image attached to it.

I'm going to look into this further, but it's the end of the day here, so I'll have to pick this up again tomorrow.

If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.

Comments

Uncover Disqus comments