List items

Items from the current list are shown below.


29 Oct 2023 : Day 74 #
That extra hour was worth the wait. I've not yet decided exactly how I spent it (possibly asleep, possibly fixing my Hue Hub) but it felt good to have enough time for both, and now it feels like it's the middle of the night, but it's actually only six thirty in the evening.

So today I'm continuing to go deeper and deeper into the rendering code. I've now got as far as nsCSSBorderRenderer::DrawBorders(). This is one of the methods that's called to render a bordered rectangle. I'm rendering a page that contains such objects and the breakpoint is hit on both ESR 78 and ESR 91.

The method then makes lots of decisions about exactly what it's going to render: are all the borders the same? Do they have zero thickness? Are they solid? Things like that.

Eventually we get to the point where an actual rectangle is going to be plotted:
But what is mDrawTarget and which version of FillRect() is being called (there appear to be at least 18 to choose from). It looks like it should be an easy question: just check what type mDrawTarget is. But with multiple inheritance and virtual methods it's easier said than done. It can be easier to do at runtime with a debugger than by manual inspection of the code, in fact.

So I'm stepping inside the method on ESR 91, from which it seems to be the case that it's the version in DrawTarget from 2D.h:
(gdb) explore *mDrawTarget
The value of '*mDrawTarget' is of type 'nsCSSBorderRenderer::DrawTarget' which is a typedef of type 'mozilla::gfx::DrawTarget'
The value of '*mDrawTarget' is a struct/class of type 'mozilla::gfx::DrawTarget' with the following fields:

  mozilla::external::AtomicRefCounted<mozilla::gfx::DrawTarget> =
                      <Enter 0 to explore this base class of type 'mozilla::
          mUserData = <Enter 1 to explore this field of type 'mozilla::gfx::UserData'>
         mTransform = <Enter 2 to explore this field of type 'mozilla::gfx::Matrix'>
        mOpaqueRect = <Enter 3 to explore this field of type 'mozilla::gfx::IntRect'>
    mTransformDirty = true .. (Value of type 'bool')
  mPermitSubpixelAA = true .. (Value of type 'bool')
            mFormat = mozilla::gfx::SurfaceFormat::B8G8R8X8 ..
                      (Value of type 'mozilla::gfx::SurfaceFormat')

Enter the field number of choice: 
But as I mentioned the method is virtual and the above doesn't preclude it being overridden. So let's try to step inside it to see where we end up.
(gdb) n
3217            mDrawTarget->FillRect(GetCornerRect(corner),
(gdb) n
3218                                  ColorPattern(ToDeviceColor(color)));
(gdb) n
3217            mDrawTarget->FillRect(GetCornerRect(corner),
(gdb) s
mozilla::gfx::DrawOptions::DrawOptions (aAntialiasMode=mozilla::gfx::
    AntialiasMode::DEFAULT, aCompositionOp=mozilla::gfx::CompositionOp::OP_OVER, 
    aAlpha=1, this=0x7f9f3dfa90)
    at ${PROJECT}/obj-build-mer-qt-xr/dist/include/mozilla/gfx/2D.h:124
124     ${PROJECT}/obj-build-mer-qt-xr/dist/include/mozilla/gfx/2D.h:
    No such file or directory.
(gdb) n
nsCSSBorderRenderer::DrawBorders (this=this@entry=0x7f9f3dfc20)
    at /usr/src/debug/xulrunner-qt5-91.9.1-1.aarch64/layout/painting/
3219            continue;
That's a mess. It's mostly a mess because there are four method calls essentially all on the same line as the FillRect() call that we're interested in, so we want to stop over three and into one of them:
  1. FillRect() (step into).
  2. GetCornerRect() (step over).
  3. ColorPattern() (step over).
  4. ToDeviceColor() (step over).
Eventually after trying to step through carefully and failing I decide to use brute force by placing a breakpoint on every instance of FillRect(). This from ESR 78:
(gdb) b FillRect
Breakpoint 3 at 0x7ff2858a58: FillRect. (25 locations)
(gdb) c

Thread 10 "GeckoWorkerThre" hit Breakpoint 3, mozilla::gfx::DrawTargetTiled::
    FillRect (this=0x7fb9250c10, aRect=..., aPattern=..., aDrawOptions=...)
    at /usr/src/debug/xulrunner-qt5-78.15.1+git33.2-1.21.1.jolla.aarch64/gfx/2d/
221                                    const DrawOptions& aDrawOptions) {
Happily this gives us what we need: it's the FillRect() method from DrawTargetTiled. This is what that method looks like:
void DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern,
                               const DrawOptions& aDrawOptions) {
  Rect deviceRect = mTransform.TransformBounds(aRect);
  for (size_t i = 0; i < mTiles.size(); i++) {
    if (!mTiles[i].mClippedOut &&
                                   mTiles[i].mDrawTarget->GetSize().height))) {
      mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
On ESR 91 we get something very similar:
(gdb) b FillRect
Breakpoint 5 at 0x7fba5084ec: FillRect. (16 locations)
(gdb) c

Thread 8 "GeckoWorkerThre" hit Breakpoint 5, mozilla::gfx::DrawTargetTiled::
    FillRect (this=0x7f88dd55f0, aRect=..., aPattern=..., aDrawOptions=...)
    at /usr/src/debug/xulrunner-qt5-91.9.1-1.aarch64/gfx/2d/DrawTargetTiled.cpp:221
221                                    const DrawOptions& aDrawOptions) {
That's all encouraging and takes us a bit closer.

If we step through on either ESR 78 or ESR 91 we get to the same place:
(gdb) n
222       Rect deviceRect = mTransform.TransformBounds(aRect);
(gdb) n
223       for (size_t i = 0; i < mTiles.size(); i++) {
(gdb) n
224         if (!mTiles[i].mClippedOut &&
(gdb) n
313     obj-build-mer-qt-xr/dist/include/mozilla/RefPtr.h: No such file or directory.
(gdb) n
228                                        mTiles[i].mDrawTarget->GetSize().height))) {
(gdb) n
225             deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
(gdb) n
229           mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
(gdb) c

Thread 10 "GeckoWorkerThre" hit Breakpoint 3, mozilla::gfx::DrawTargetSkia::
    FillRect (this=0x7fb8f8a3c0, aRect=..., aPattern=..., aOptions=...)
    at gfx/2d/DrawTargetSkia.cpp:775
775                                   const DrawOptions& aOptions) {
The DrawTargetSkia::FillRect() method is more complex, but eventually calls this:
  mCanvas->drawRect(rect, paint.mPaint);
Breakpointing on that takes us another step closer:
(gdb) c
[Switching to LWP 20030]

Thread 10 "GeckoWorkerThre" hit Breakpoint 6, SkCanvas::drawRect
    (this=0x7fb90a72a0, r=..., paint=...)
    at gfx/skia/skia/src/core/SkCanvas.cpp:1803
1803    void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
(gdb) c

Thread 10 "GeckoWorkerThre" hit Breakpoint 6, SkBitmapDevice::drawRect
    (this=0x7fbb044a40, r=..., paint=...)
    at gfx/skia/skia/src/core/SkBitmapDevice.cpp:364
364     void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
(gdb) c

Thread 10 "GeckoWorkerThre" hit Breakpoint 6, SkDraw::drawRect
    (paint=..., rect=..., this=0x7fde8d2d98)
    at gfx/skia/skia/src/core/SkDraw.h:42
42              this->drawRect(rect, paint, nullptr, nullptr);

This takes us to the SkDraw::drawRect() method:
void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
                      const SkMatrix* paintMatrix, const SkRect* postPaintRect)
The code reaches exactly the same place on ESR 78 and ESR 91. This seems like a good place to stop for the night. It'll take me a while to carefully read through this method to figure out what it's doing. It's also a good place to pause since putting a breakpoint on this method will allow me to easily return to this point tomorrow as well.

So that's it for tonight! Sleep well.

For all the other entries in my developer diary, check out the Gecko-dev Diary page.


Uncover Disqus comments