tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DrawTargetWebgl.cpp (266796B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DrawTargetWebglInternal.h"
      8 #include "FilterNodeWebgl.h"
      9 #include "GLContext.h"
     10 #include "GLScreenBuffer.h"
     11 #include "SharedSurface.h"
     12 #include "SourceSurfaceWebgl.h"
     13 #include "WebGL2Context.h"
     14 #include "WebGLBuffer.h"
     15 #include "WebGLChild.h"
     16 #include "WebGLContext.h"
     17 #include "WebGLFramebuffer.h"
     18 #include "WebGLProgram.h"
     19 #include "WebGLShader.h"
     20 #include "WebGLTexture.h"
     21 #include "WebGLVertexArray.h"
     22 #include "gfxPlatform.h"
     23 #include "mozilla/ClearOnShutdown.h"
     24 #include "mozilla/HelperMacros.h"
     25 #include "mozilla/StaticPrefs_gfx.h"
     26 #include "mozilla/gfx/AAStroke.h"
     27 #include "mozilla/gfx/Blur.h"
     28 #include "mozilla/gfx/DataSurfaceHelpers.h"
     29 #include "mozilla/gfx/DrawTargetSkia.h"
     30 #include "mozilla/gfx/Helpers.h"
     31 #include "mozilla/gfx/HelpersSkia.h"
     32 #include "mozilla/gfx/Logging.h"
     33 #include "mozilla/gfx/PathHelpers.h"
     34 #include "mozilla/gfx/PathSkia.h"
     35 #include "mozilla/gfx/Scale.h"
     36 #include "mozilla/gfx/Swizzle.h"
     37 #include "mozilla/gfx/gfxVars.h"
     38 #include "mozilla/layers/ImageDataSerializer.h"
     39 #include "mozilla/layers/RemoteTextureMap.h"
     40 #include "mozilla/widget/ScreenManager.h"
     41 #include "nsContentUtils.h"
     42 #include "nsIMemoryReporter.h"
     43 #include "skia/include/core/SkPixmap.h"
     44 
     45 #ifdef XP_MACOSX
     46 #  include "mozilla/gfx/ScaledFontMac.h"
     47 #endif
     48 
     49 namespace mozilla::gfx {
     50 
     51 static Atomic<size_t> gReportedTextureMemory;
     52 static Atomic<size_t> gReportedHeapData;
     53 static Atomic<size_t> gReportedContextCount;
     54 static Atomic<size_t> gReportedTargetCount;
     55 
     56 class AcceleratedCanvas2DMemoryReporter final : public nsIMemoryReporter {
     57  ~AcceleratedCanvas2DMemoryReporter() = default;
     58 
     59 public:
     60  NS_DECL_THREADSAFE_ISUPPORTS
     61 
     62  MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
     63  MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
     64 
     65  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     66                            nsISupports* aData, bool aAnonymize) override {
     67    MOZ_COLLECT_REPORT("ac2d-texture-memory", KIND_OTHER, UNITS_BYTES,
     68                       gReportedTextureMemory,
     69                       "GPU memory used by Accelerated Canvas2D textures.");
     70    MOZ_COLLECT_REPORT("explicit/ac2d/heap-resources", KIND_HEAP, UNITS_BYTES,
     71                       gReportedHeapData,
     72                       "Heap overhead for Accelerated Canvas2D resources.");
     73    MOZ_COLLECT_REPORT("ac2d-context-count", KIND_OTHER, UNITS_COUNT,
     74                       gReportedContextCount,
     75                       "Number of Accelerated Canvas2D contexts.");
     76    MOZ_COLLECT_REPORT("ac2d-target-count", KIND_OTHER, UNITS_COUNT,
     77                       gReportedTargetCount,
     78                       "Number of Accelerated Canvas2D targets.");
     79    return NS_OK;
     80  }
     81 
     82  static void Register() {
     83    static bool registered = false;
     84    if (!registered) {
     85      registered = true;
     86      RegisterStrongMemoryReporter(new AcceleratedCanvas2DMemoryReporter);
     87    }
     88  }
     89 };
     90 
     91 NS_IMPL_ISUPPORTS(AcceleratedCanvas2DMemoryReporter, nsIMemoryReporter)
     92 
     93 BackingTexture::BackingTexture(const IntSize& aSize, SurfaceFormat aFormat,
     94                               const RefPtr<WebGLTexture>& aTexture)
     95    : mSize(aSize), mFormat(aFormat), mTexture(aTexture) {}
     96 
     97 #ifdef XP_WIN
     98 // Work around buggy ANGLE/D3D drivers that may copy blocks of pixels outside
     99 // the row length. Extra space is reserved at the end of each row up to stride
    100 // alignment. This does not affect standalone textures.
    101 static const Etagere::AllocatorOptions kR8AllocatorOptions = {16, 1, 1, 0};
    102 #endif
    103 
    104 SharedTexture::SharedTexture(const IntSize& aSize, SurfaceFormat aFormat,
    105                             const RefPtr<WebGLTexture>& aTexture)
    106    : BackingTexture(aSize, aFormat, aTexture),
    107      mAtlasAllocator(
    108 #ifdef XP_WIN
    109          aFormat == SurfaceFormat::A8
    110              ? Etagere::etagere_atlas_allocator_with_options(
    111                    aSize.width, aSize.height, &kR8AllocatorOptions)
    112              :
    113 #endif
    114              Etagere::etagere_atlas_allocator_new(aSize.width, aSize.height)) {
    115 }
    116 
    117 SharedTexture::~SharedTexture() {
    118  if (mAtlasAllocator) {
    119    Etagere::etagere_atlas_allocator_delete(mAtlasAllocator);
    120    mAtlasAllocator = nullptr;
    121  }
    122 }
    123 
    124 SharedTextureHandle::SharedTextureHandle(Etagere::AllocationId aId,
    125                                         const IntRect& aBounds,
    126                                         SharedTexture* aTexture)
    127    : mAllocationId(aId), mBounds(aBounds), mTexture(aTexture) {}
    128 
    129 already_AddRefed<SharedTextureHandle> SharedTexture::Allocate(
    130    const IntSize& aSize) {
    131  Etagere::Allocation alloc = {{0, 0, 0, 0}, Etagere::INVALID_ALLOCATION_ID};
    132  if (!mAtlasAllocator ||
    133      !Etagere::etagere_atlas_allocator_allocate(mAtlasAllocator, aSize.width,
    134                                                 aSize.height, &alloc) ||
    135      alloc.id == Etagere::INVALID_ALLOCATION_ID) {
    136    return nullptr;
    137  }
    138  RefPtr<SharedTextureHandle> handle = new SharedTextureHandle(
    139      alloc.id,
    140      IntRect(IntPoint(alloc.rectangle.min_x, alloc.rectangle.min_y), aSize),
    141      this);
    142  return handle.forget();
    143 }
    144 
    145 bool SharedTexture::Free(SharedTextureHandle& aHandle) {
    146  if (aHandle.mTexture != this) {
    147    return false;
    148  }
    149  if (aHandle.mAllocationId != Etagere::INVALID_ALLOCATION_ID) {
    150    if (mAtlasAllocator) {
    151      Etagere::etagere_atlas_allocator_deallocate(mAtlasAllocator,
    152                                                  aHandle.mAllocationId);
    153    }
    154    aHandle.mAllocationId = Etagere::INVALID_ALLOCATION_ID;
    155  }
    156  return true;
    157 }
    158 
    159 StandaloneTexture::StandaloneTexture(const IntSize& aSize,
    160                                     SurfaceFormat aFormat,
    161                                     const RefPtr<WebGLTexture>& aTexture)
    162    : BackingTexture(aSize, aFormat, aTexture) {}
    163 
    164 DrawTargetWebgl::DrawTargetWebgl() = default;
    165 
    166 inline void SharedContextWebgl::ClearLastTexture(bool aFullClear) {
    167  mLastTexture = nullptr;
    168  if (aFullClear) {
    169    mLastClipMask = nullptr;
    170  }
    171 }
    172 
    173 // Attempts to clear the snapshot state. If the snapshot is only referenced by
    174 // this target, then it should simply be destroyed. If it is a WebGL surface in
    175 // use by something else, then special cleanup such as reusing the texture or
    176 // copy-on-write may be possible.
    177 void DrawTargetWebgl::ClearSnapshot(bool aCopyOnWrite, bool aNeedHandle) {
    178  if (!mSnapshot) {
    179    return;
    180  }
    181  mSharedContext->ClearLastTexture();
    182  RefPtr<SourceSurfaceWebgl> snapshot = mSnapshot.forget();
    183  if (snapshot->hasOneRef()) {
    184    return;
    185  }
    186  if (aCopyOnWrite) {
    187    // WebGL snapshots must be notified that the framebuffer contents will be
    188    // changing so that it can copy the data.
    189    snapshot->DrawTargetWillChange(aNeedHandle);
    190  } else {
    191    // If not copying, then give the backing texture to the surface for reuse.
    192    snapshot->GiveTexture(
    193        mSharedContext->WrapSnapshot(GetSize(), GetFormat(), mTex.forget()));
    194  }
    195 }
    196 
    197 DrawTargetWebgl::~DrawTargetWebgl() {
    198  ClearSnapshot(false);
    199  if (mSharedContext) {
    200    // Force any Skia snapshots to copy the shmem before it deallocs.
    201    if (mSkia) {
    202      mSkia->DetachAllSnapshots();
    203    }
    204    mSharedContext->ClearLastTexture(true);
    205    if (mClipMask) {
    206      mSharedContext->RemoveUntrackedTextureMemory(mClipMask);
    207      mClipMask = nullptr;
    208    }
    209    mFramebuffer = nullptr;
    210    if (mTex) {
    211      mSharedContext->RemoveUntrackedTextureMemory(mTex);
    212      mTex = nullptr;
    213    }
    214    mSharedContext->mDrawTargetCount--;
    215    gReportedTargetCount--;
    216  }
    217 }
    218 
    219 SharedContextWebgl::SharedContextWebgl() = default;
    220 
    221 SharedContextWebgl::~SharedContextWebgl() {
    222  // Detect context loss before deletion.
    223  if (mWebgl) {
    224    ExitTlsScope();
    225    mWebgl->ActiveTexture(0);
    226    gReportedContextCount--;
    227  }
    228  if (mWGRPathBuilder) {
    229    WGR::wgr_builder_release(mWGRPathBuilder);
    230    mWGRPathBuilder = nullptr;
    231  }
    232  if (mWGROutputBuffer) {
    233    RemoveHeapData(mWGROutputBuffer.get());
    234    mWGROutputBuffer = nullptr;
    235  }
    236  if (mPathVertexBuffer) {
    237    RemoveUntrackedTextureMemory(mPathVertexBuffer);
    238    mPathVertexBuffer = nullptr;
    239  }
    240  ClearZeroBuffer();
    241  ClearAllTextures();
    242  UnlinkSurfaceTextures(true);
    243  UnlinkGlyphCaches();
    244  ClearSnapshotPBOs();
    245 }
    246 
    247 gl::GLContext* SharedContextWebgl::GetGLContext() {
    248  return mWebgl ? mWebgl->GL() : nullptr;
    249 }
    250 
    251 void SharedContextWebgl::EnterTlsScope() {
    252  if (mTlsScope.isSome()) {
    253    return;
    254  }
    255  if (gl::GLContext* gl = GetGLContext()) {
    256    mTlsScope = Some(gl->mUseTLSIsCurrent);
    257    gl::GLContext::InvalidateCurrentContext();
    258    gl->mUseTLSIsCurrent = true;
    259  }
    260 }
    261 
    262 void SharedContextWebgl::ExitTlsScope() {
    263  if (mTlsScope.isNothing()) {
    264    return;
    265  }
    266  if (gl::GLContext* gl = GetGLContext()) {
    267    gl->mUseTLSIsCurrent = mTlsScope.value();
    268  }
    269  mTlsScope = Nothing();
    270 }
    271 
    272 // Remove any SourceSurface user data associated with this TextureHandle.
    273 inline void SharedContextWebgl::UnlinkSurfaceTexture(
    274    const RefPtr<TextureHandle>& aHandle, bool aForce) {
    275  if (RefPtr<SourceSurface> surface = aHandle->GetSurface()) {
    276    // Ensure any WebGL snapshot textures get unlinked.
    277    if (surface->GetType() == SurfaceType::WEBGL) {
    278      static_cast<SourceSurfaceWebgl*>(surface.get())
    279          ->OnUnlinkTexture(this, aHandle, aForce);
    280    }
    281    surface->RemoveUserData(&mTextureHandleKey);
    282  }
    283 }
    284 
    285 // Unlinks TextureHandles from any SourceSurface user data.
    286 void SharedContextWebgl::UnlinkSurfaceTextures(bool aForce) {
    287  for (RefPtr<TextureHandle> handle = mTextureHandles.getFirst(); handle;
    288       handle = handle->getNext()) {
    289    UnlinkSurfaceTexture(handle, aForce);
    290  }
    291 }
    292 
    293 // Unlinks GlyphCaches from any ScaledFont user data.
    294 void SharedContextWebgl::UnlinkGlyphCaches() {
    295  GlyphCache* cache = mGlyphCaches.getFirst();
    296  while (cache) {
    297    ScaledFont* font = cache->GetFont();
    298    // Access the next cache before removing the user data, as it might destroy
    299    // the cache.
    300    cache = cache->getNext();
    301    font->RemoveUserData(&mGlyphCacheKey);
    302  }
    303 }
    304 
    305 void SharedContextWebgl::OnMemoryPressure() { mShouldClearCaches = true; }
    306 
    307 void SharedContextWebgl::ClearCaches() {
    308  OnMemoryPressure();
    309  ClearCachesIfNecessary();
    310 }
    311 
    312 // Clear out the entire list of texture handles from any source.
    313 void SharedContextWebgl::ClearAllTextures() {
    314  while (!mTextureHandles.isEmpty()) {
    315    PruneTextureHandle(mTextureHandles.popLast());
    316    --mNumTextureHandles;
    317  }
    318 }
    319 
    320 static inline size_t TextureMemoryUsage(WebGLTexture* aTexture) {
    321  return aTexture->MemoryUsage();
    322 }
    323 
    324 static inline size_t TextureMemoryUsage(WebGLBuffer* aBuffer) {
    325  return aBuffer->ByteLength();
    326 }
    327 
    328 inline void SharedContextWebgl::AddHeapData(const void* aBuf) {
    329  if (aBuf) {
    330    gReportedHeapData +=
    331        AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnAlloc(aBuf);
    332  }
    333 }
    334 
    335 inline void SharedContextWebgl::RemoveHeapData(const void* aBuf) {
    336  if (aBuf) {
    337    gReportedHeapData -=
    338        AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnFree(aBuf);
    339  }
    340 }
    341 
    342 inline void SharedContextWebgl::AddUntrackedTextureMemory(size_t aBytes) {
    343  gReportedTextureMemory += aBytes;
    344 }
    345 
    346 inline void SharedContextWebgl::RemoveUntrackedTextureMemory(size_t aBytes) {
    347  gReportedTextureMemory -= aBytes;
    348 }
    349 
    350 template <typename T>
    351 inline void SharedContextWebgl::AddUntrackedTextureMemory(
    352    const RefPtr<T>& aObject, size_t aBytes) {
    353  size_t usedBytes = aBytes > 0 ? aBytes : TextureMemoryUsage(aObject);
    354  AddUntrackedTextureMemory(usedBytes);
    355  gReportedHeapData += aObject->SizeOfIncludingThis(
    356      AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnAlloc);
    357 }
    358 
    359 template <typename T>
    360 inline void SharedContextWebgl::RemoveUntrackedTextureMemory(
    361    const RefPtr<T>& aObject, size_t aBytes) {
    362  size_t usedBytes = aBytes > 0 ? aBytes : TextureMemoryUsage(aObject);
    363  RemoveUntrackedTextureMemory(usedBytes);
    364  gReportedHeapData -= aObject->SizeOfIncludingThis(
    365      AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnFree);
    366 }
    367 
    368 inline void SharedContextWebgl::AddTextureMemory(BackingTexture* aTexture) {
    369  size_t usedBytes = aTexture->UsedBytes();
    370  mTotalTextureMemory += usedBytes;
    371  AddUntrackedTextureMemory(aTexture->GetWebGLTexture(), usedBytes);
    372 }
    373 
    374 inline void SharedContextWebgl::RemoveTextureMemory(BackingTexture* aTexture) {
    375  size_t usedBytes = aTexture->UsedBytes();
    376  mTotalTextureMemory -= usedBytes;
    377  RemoveUntrackedTextureMemory(aTexture->GetWebGLTexture(), usedBytes);
    378 }
    379 
    380 // Scan through the shared texture pages looking for any that are empty and
    381 // delete them.
    382 void SharedContextWebgl::ClearEmptyTextureMemory() {
    383  for (auto pos = mSharedTextures.begin(); pos != mSharedTextures.end();) {
    384    if (!(*pos)->HasAllocatedHandles()) {
    385      RefPtr<SharedTexture> shared = *pos;
    386      mEmptyTextureMemory -= shared->UsedBytes();
    387      RemoveTextureMemory(shared);
    388      pos = mSharedTextures.erase(pos);
    389    } else {
    390      ++pos;
    391    }
    392  }
    393 }
    394 
    395 void SharedContextWebgl::ClearZeroBuffer() {
    396  if (mZeroBuffer) {
    397    RemoveUntrackedTextureMemory(mZeroBuffer);
    398    mZeroBuffer = nullptr;
    399  }
    400 }
    401 
    402 // If there is a request to clear out the caches because of memory pressure,
    403 // then first clear out all the texture handles in the texture cache. If there
    404 // are still empty texture pages being kept around, then clear those too.
    405 void SharedContextWebgl::ClearCachesIfNecessary() {
    406  if (!mShouldClearCaches.exchange(false)) {
    407    return;
    408  }
    409  ClearZeroBuffer();
    410  ClearAllTextures();
    411  if (mEmptyTextureMemory) {
    412    ClearEmptyTextureMemory();
    413  }
    414  ClearLastTexture();
    415  ClearSnapshotPBOs();
    416 }
    417 
    418 // Try to initialize a new WebGL context. Verifies that the requested size does
    419 // not exceed the available texture limits and that shader creation succeeded.
    420 bool DrawTargetWebgl::Init(const IntSize& size, const SurfaceFormat format,
    421                           const RefPtr<SharedContextWebgl>& aSharedContext) {
    422  switch (format) {
    423    case SurfaceFormat::B8G8R8A8:
    424    case SurfaceFormat::B8G8R8X8:
    425      break;
    426    default:
    427      MOZ_ASSERT_UNREACHABLE("Unsupported format for DrawTargetWebgl.");
    428      return false;
    429  }
    430 
    431  mSize = size;
    432  mFormat = format;
    433 
    434  if (!aSharedContext || aSharedContext->IsContextLost() ||
    435      aSharedContext->mDrawTargetCount >=
    436          StaticPrefs::gfx_canvas_accelerated_max_draw_target_count()) {
    437    return false;
    438  }
    439  mSharedContext = aSharedContext;
    440  mSharedContext->mDrawTargetCount++;
    441  gReportedTargetCount++;
    442 
    443  if (size_t(std::max(size.width, size.height)) >
    444      mSharedContext->mMaxTextureSize) {
    445    return false;
    446  }
    447 
    448  if (!CreateFramebuffer()) {
    449    return false;
    450  }
    451 
    452  size_t byteSize = layers::ImageDataSerializer::ComputeRGBBufferSize(
    453      mSize, SurfaceFormat::B8G8R8A8);
    454  if (byteSize == 0) {
    455    return false;
    456  }
    457 
    458  size_t shmemSize = mozilla::ipc::shared_memory::PageAlignedSize(byteSize);
    459  if (NS_WARN_IF(shmemSize > UINT32_MAX)) {
    460    MOZ_ASSERT_UNREACHABLE("Buffer too big?");
    461    return false;
    462  }
    463 
    464  auto handle = mozilla::ipc::shared_memory::Create(shmemSize);
    465  if (NS_WARN_IF(!handle)) {
    466    return false;
    467  }
    468  auto mapping = handle.Map();
    469  if (NS_WARN_IF(!mapping)) {
    470    return false;
    471  }
    472 
    473  mShmemHandle = std::move(handle).ToReadOnly();
    474  mShmem = std::move(mapping);
    475 
    476  mSkia = new DrawTargetSkia;
    477  auto stride = layers::ImageDataSerializer::ComputeRGBStride(
    478      SurfaceFormat::B8G8R8A8, size.width);
    479  if (!mSkia->Init(mShmem.DataAs<uint8_t>(), size, stride,
    480                   SurfaceFormat::B8G8R8A8, true)) {
    481    return false;
    482  }
    483 
    484  // Allocate an unclipped copy of the DT pointing to its data.
    485  uint8_t* dtData = nullptr;
    486  IntSize dtSize;
    487  int32_t dtStride = 0;
    488  SurfaceFormat dtFormat = SurfaceFormat::UNKNOWN;
    489  if (!mSkia->LockBits(&dtData, &dtSize, &dtStride, &dtFormat)) {
    490    return false;
    491  }
    492  mSkiaNoClip = new DrawTargetSkia;
    493  if (!mSkiaNoClip->Init(dtData, dtSize, dtStride, dtFormat, true)) {
    494    mSkia->ReleaseBits(dtData);
    495    return false;
    496  }
    497  mSkia->ReleaseBits(dtData);
    498 
    499  SetPermitSubpixelAA(IsOpaque(format));
    500  return true;
    501 }
    502 
    503 // If a non-recoverable error occurred that would stop the canvas from initing.
    504 static Atomic<bool> sContextInitError(false);
    505 
    506 already_AddRefed<SharedContextWebgl> SharedContextWebgl::Create() {
    507  // If context initialization would fail, don't even try to create a context.
    508  if (sContextInitError) {
    509    return nullptr;
    510  }
    511  RefPtr<SharedContextWebgl> sharedContext = new SharedContextWebgl;
    512  if (!sharedContext->Initialize()) {
    513    return nullptr;
    514  }
    515  return sharedContext.forget();
    516 }
    517 
    518 bool SharedContextWebgl::Initialize() {
    519  AcceleratedCanvas2DMemoryReporter::Register();
    520 
    521  WebGLContextOptions options = {};
    522  options.alpha = true;
    523  options.depth = false;
    524  options.stencil = false;
    525  options.antialias = false;
    526  options.preserveDrawingBuffer = true;
    527  options.failIfMajorPerformanceCaveat = false;
    528 
    529  const bool resistFingerprinting = nsContentUtils::ShouldResistFingerprinting(
    530      "Fallback", RFPTarget::WebGLRenderCapability);
    531  const auto initDesc = webgl::InitContextDesc{
    532      .isWebgl2 = true,
    533      .resistFingerprinting = resistFingerprinting,
    534      .principalKey = 0,
    535      .size = {1, 1},
    536      .options = options,
    537  };
    538 
    539  webgl::InitContextResult initResult;
    540  mWebgl = WebGLContext::Create(nullptr, initDesc, &initResult);
    541  if (!mWebgl) {
    542    // There was a non-recoverable error when trying to create a host context.
    543    sContextInitError = true;
    544    mWebgl = nullptr;
    545    return false;
    546  }
    547  if (mWebgl->IsContextLost()) {
    548    mWebgl = nullptr;
    549    return false;
    550  }
    551 
    552  mMaxTextureSize = initResult.limits.maxTex2dSize;
    553 
    554  if (kIsMacOS) {
    555    mRasterizationTruncates = initResult.vendor == gl::GLVendor::ATI;
    556  }
    557 
    558  CachePrefs();
    559 
    560  if (!CreateShaders()) {
    561    // There was a non-recoverable error when trying to init shaders.
    562    sContextInitError = true;
    563    mWebgl = nullptr;
    564    return false;
    565  }
    566 
    567  mWGRPathBuilder = WGR::wgr_new_builder();
    568 
    569  gReportedContextCount++;
    570 
    571  return true;
    572 }
    573 
    574 inline void SharedContextWebgl::BlendFunc(GLenum aSrcFactor,
    575                                          GLenum aDstFactor) {
    576  mWebgl->BlendFuncSeparate({}, aSrcFactor, aDstFactor, aSrcFactor, aDstFactor);
    577 }
    578 
    579 void SharedContextWebgl::SetBlendState(CompositionOp aOp,
    580                                       const Maybe<DeviceColor>& aColor,
    581                                       uint8_t aStage) {
    582  if (aOp == mLastCompositionOp && mLastBlendColor == aColor &&
    583      mLastBlendStage == aStage) {
    584    return;
    585  }
    586  mLastCompositionOp = aOp;
    587  mLastBlendColor = aColor;
    588  mLastBlendStage = aStage;
    589  // AA is not supported for all composition ops, so switching blend modes may
    590  // cause a toggle in AA state. Certain ops such as OP_SOURCE require output
    591  // alpha that is blended separately from AA coverage. This would require two
    592  // stage blending which can incur a substantial performance penalty, so to
    593  // work around this currently we just disable AA for those ops.
    594 
    595  // Map the composition op to a WebGL blend mode, if possible.
    596  bool enabled = true;
    597  switch (aOp) {
    598    case CompositionOp::OP_OVER:
    599      if (aColor) {
    600        // If a color is supplied, then we blend subpixel text.
    601        mWebgl->BlendColor(aColor->b, aColor->g, aColor->r, 1.0f);
    602        BlendFunc(LOCAL_GL_CONSTANT_COLOR, LOCAL_GL_ONE_MINUS_SRC_COLOR);
    603      } else {
    604        BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    605      }
    606      break;
    607    case CompositionOp::OP_DEST_OVER:
    608      BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_ONE);
    609      break;
    610    case CompositionOp::OP_ADD:
    611      BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE);
    612      break;
    613    case CompositionOp::OP_DEST_OUT:
    614      BlendFunc(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    615      break;
    616    case CompositionOp::OP_ATOP:
    617      BlendFunc(LOCAL_GL_DST_ALPHA, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    618      break;
    619    case CompositionOp::OP_SOURCE:
    620      if (aColor) {
    621        // If a color is supplied, then we assume there is clipping or AA. This
    622        // requires that we still use an over blend func with the clip/AA alpha,
    623        // while filling the interior with the unaltered color. Normally this
    624        // would require dual source blending, but we can emulate it with only
    625        // a blend color.
    626        mWebgl->BlendColor(aColor->b, aColor->g, aColor->r, aColor->a);
    627        BlendFunc(LOCAL_GL_CONSTANT_COLOR, LOCAL_GL_ONE_MINUS_SRC_COLOR);
    628      } else {
    629        enabled = false;
    630      }
    631      break;
    632    case CompositionOp::OP_CLEAR:
    633      // Assume the source is an alpha mask for clearing. Be careful to blend in
    634      // the correct alpha if the target is opaque.
    635      mWebgl->BlendFuncSeparate(
    636          {}, LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
    637          IsOpaque(mCurrentTarget->GetFormat()) ? LOCAL_GL_ONE : LOCAL_GL_ZERO,
    638          LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    639      break;
    640    case CompositionOp::OP_MULTIPLY:
    641      switch (aStage) {
    642        // Single stage, assume dest is opaque alpha.
    643        case 0:
    644          BlendFunc(LOCAL_GL_DST_COLOR, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    645          break;
    646        // Multi-stage, decompose into [Cs*(1 - Ad)] + [Cd*(1 - As) + Cs*Cd]
    647        case 1:
    648          BlendFunc(LOCAL_GL_DST_COLOR, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
    649          break;
    650        case 2:
    651          BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_ONE);
    652          break;
    653      }
    654      break;
    655    case CompositionOp::OP_SCREEN:
    656      BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_COLOR);
    657      break;
    658    case CompositionOp::OP_IN:  // unbounded
    659      BlendFunc(LOCAL_GL_DST_ALPHA, LOCAL_GL_ZERO);
    660      break;
    661    case CompositionOp::OP_OUT:  // unbounded
    662      BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_ZERO);
    663      break;
    664    case CompositionOp::OP_DEST_IN:  // unbounded
    665      BlendFunc(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA);
    666      break;
    667    case CompositionOp::OP_DEST_ATOP:  // unbounded
    668      BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_SRC_ALPHA);
    669      break;
    670    default:
    671      enabled = false;
    672      break;
    673  }
    674 
    675  mWebgl->SetEnabled(LOCAL_GL_BLEND, {}, enabled);
    676 }
    677 
    678 // Ensure the WebGL framebuffer is set to the current target.
    679 bool SharedContextWebgl::SetTarget(DrawTargetWebgl* aDT,
    680                                   const RefPtr<TextureHandle>& aHandle,
    681                                   const IntSize& aViewportSize) {
    682  if (!mWebgl || mWebgl->IsContextLost()) {
    683    return false;
    684  }
    685  if (aDT != mCurrentTarget || mTargetHandle != aHandle) {
    686    mCurrentTarget = aDT;
    687    mTargetHandle = aHandle;
    688    IntRect bounds;
    689    if (aHandle) {
    690      if (!mTargetFramebuffer) {
    691        mTargetFramebuffer = mWebgl->CreateFramebuffer();
    692      }
    693      mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mTargetFramebuffer);
    694 
    695      webgl::FbAttachInfo attachInfo;
    696      attachInfo.tex = aHandle->GetBackingTexture()->GetWebGLTexture();
    697      mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER,
    698                                LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
    699                                attachInfo);
    700 
    701      bounds = aHandle->GetBounds();
    702    } else if (aDT) {
    703      mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, aDT->mFramebuffer);
    704      bounds = aDT->GetRect();
    705    }
    706    mViewportSize = !aViewportSize.IsEmpty() ? Min(aViewportSize, bounds.Size())
    707                                             : bounds.Size();
    708    mWebgl->Viewport(bounds.x, bounds.y, mViewportSize.width,
    709                     mViewportSize.height);
    710  }
    711  return true;
    712 }
    713 
    714 // Replace the current clip rect with a new potentially-AA'd clip rect.
    715 void SharedContextWebgl::SetClipRect(const Rect& aClipRect) {
    716  // Only invalidate the clip rect if it actually changes.
    717  if (!mClipAARect.IsEqualEdges(aClipRect)) {
    718    mClipAARect = aClipRect;
    719    // Store the integer-aligned bounds.
    720    mClipRect = RoundedOut(aClipRect);
    721  }
    722 }
    723 
    724 bool SharedContextWebgl::SetClipMask(const RefPtr<WebGLTexture>& aTex) {
    725  if (mLastClipMask != aTex) {
    726    if (!mWebgl) {
    727      return false;
    728    }
    729    mWebgl->ActiveTexture(1);
    730    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, aTex);
    731    mWebgl->ActiveTexture(0);
    732    mLastClipMask = aTex;
    733  }
    734  return true;
    735 }
    736 
    737 bool SharedContextWebgl::SetNoClipMask() {
    738  if (mNoClipMask) {
    739    return SetClipMask(mNoClipMask);
    740  }
    741  if (!mWebgl) {
    742    return false;
    743  }
    744  mNoClipMask = mWebgl->CreateTexture();
    745  if (!mNoClipMask) {
    746    return false;
    747  }
    748  mWebgl->ActiveTexture(1);
    749  mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, mNoClipMask);
    750  static const auto solidMask =
    751      std::array<const uint8_t, 4>{0xFF, 0xFF, 0xFF, 0xFF};
    752  mWebgl->TexImage(0, LOCAL_GL_RGBA8, {0, 0, 0},
    753                   {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE},
    754                   {LOCAL_GL_TEXTURE_2D,
    755                    {1, 1, 1},
    756                    gfxAlphaType::NonPremult,
    757                    Some(Span{solidMask})});
    758  InitTexParameters(mNoClipMask, false);
    759  mWebgl->ActiveTexture(0);
    760  mLastClipMask = mNoClipMask;
    761  return true;
    762 }
    763 
    764 inline bool DrawTargetWebgl::ClipStack::operator==(
    765    const DrawTargetWebgl::ClipStack& aOther) const {
    766  // Verify the transform and bounds match.
    767  if (!mTransform.FuzzyEquals(aOther.mTransform) ||
    768      !mRect.IsEqualInterior(aOther.mRect)) {
    769    return false;
    770  }
    771  // Verify the paths match.
    772  if (!mPath) {
    773    return !aOther.mPath;
    774  }
    775  if (!aOther.mPath ||
    776      mPath->GetBackendType() != aOther.mPath->GetBackendType()) {
    777    return false;
    778  }
    779  if (mPath->GetBackendType() != BackendType::SKIA) {
    780    return mPath == aOther.mPath;
    781  }
    782  return static_cast<const PathSkia*>(mPath.get())->GetPath() ==
    783         static_cast<const PathSkia*>(aOther.mPath.get())->GetPath();
    784 }
    785 
    786 // If the clip region can't be approximated by a simple clip rect, then we need
    787 // to generate a clip mask that can represent the clip region per-pixel. We
    788 // render to the Skia target temporarily, transparent outside the clip region,
    789 // opaque inside, and upload this to a texture that can be used by the shaders.
    790 bool DrawTargetWebgl::GenerateComplexClipMask() {
    791  if (!mClipChanged || (mClipMask && mCachedClipStack == mClipStack)) {
    792    mClipChanged = false;
    793    // If the clip mask was already generated, use the cached mask and bounds.
    794    mSharedContext->SetClipMask(mClipMask);
    795    mSharedContext->SetClipRect(mClipBounds);
    796    return true;
    797  }
    798  if (!mWebglValid) {
    799    // If the Skia target is currently being used, then we can't render the mask
    800    // in it.
    801    return false;
    802  }
    803  RefPtr<WebGLContext> webgl = mSharedContext->mWebgl;
    804  if (!webgl) {
    805    return false;
    806  }
    807  bool init = false;
    808  if (!mClipMask) {
    809    mClipMask = webgl->CreateTexture();
    810    if (!mClipMask) {
    811      return false;
    812    }
    813    init = true;
    814  }
    815  // Try to get the bounds of the clip to limit the size of the mask.
    816  if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(true)) {
    817    mClipBounds = *clip;
    818  } else {
    819    // If we can't get bounds, then just use the entire viewport.
    820    mClipBounds = GetRect();
    821  }
    822  mClipAARect = Rect(mClipBounds);
    823  // If initializing the clip mask, then allocate the entire texture to ensure
    824  // all pixels get filled with an empty mask regardless. Otherwise, restrict
    825  // uploading to only the clip region.
    826  RefPtr<DrawTargetSkia> dt = new DrawTargetSkia;
    827  if (!dt->Init(mClipBounds.Size(), SurfaceFormat::A8)) {
    828    if (init) {
    829      // Ensure that the clip mask is reinitialized on the next attempt.
    830      mClipMask = nullptr;
    831    }
    832    return false;
    833  }
    834  // Set the clip region and fill the entire inside of it
    835  // with opaque white.
    836  mCachedClipStack.clear();
    837  for (auto& clipStack : mClipStack) {
    838    // Record the current state of the clip stack for this mask.
    839    mCachedClipStack.push_back(clipStack);
    840    dt->SetTransform(
    841        Matrix(clipStack.mTransform).PostTranslate(-mClipBounds.TopLeft()));
    842    if (clipStack.mPath) {
    843      dt->PushClip(clipStack.mPath);
    844    } else {
    845      dt->PushClipRect(clipStack.mRect);
    846    }
    847  }
    848  dt->SetTransform(Matrix::Translation(-mClipBounds.TopLeft()));
    849  dt->FillRect(Rect(mClipBounds), ColorPattern(DeviceColor(1, 1, 1, 1)));
    850  // Bind the clip mask for uploading. This is done on texture unit 0 so that
    851  // we can work around an Windows Intel driver bug. If done on texture unit 1,
    852  // the driver doesn't notice that the texture contents was modified. Force a
    853  // re-latch by binding the texture on texture unit 1 only after modification.
    854  webgl->BindTexture(LOCAL_GL_TEXTURE_2D, mClipMask);
    855  if (init) {
    856    mSharedContext->InitTexParameters(mClipMask, false);
    857  }
    858  RefPtr<DataSourceSurface> data;
    859  if (RefPtr<SourceSurface> snapshot = dt->Snapshot()) {
    860    data = snapshot->GetDataSurface();
    861  }
    862  // Finally, upload the texture data and initialize texture storage if
    863  // necessary.
    864  if (init && mClipBounds.Size() != mSize) {
    865    mSharedContext->UploadSurface(nullptr, SurfaceFormat::A8, GetRect(),
    866                                  IntPoint(), true, true);
    867    init = false;
    868  }
    869  mSharedContext->UploadSurface(data, SurfaceFormat::A8,
    870                                IntRect(IntPoint(), mClipBounds.Size()),
    871                                mClipBounds.TopLeft(), init);
    872  mSharedContext->ClearLastTexture();
    873  // Bind the new clip mask to the clip sampler on texture unit 1.
    874  mSharedContext->SetClipMask(mClipMask);
    875  mSharedContext->SetClipRect(mClipBounds);
    876  // We uploaded a surface, just as if we missed the texture cache, so account
    877  // for that here.
    878  if (init) {
    879    mSharedContext->AddUntrackedTextureMemory(mClipMask);
    880  }
    881  mProfile.OnCacheMiss();
    882  return !!data;
    883 }
    884 
    885 bool DrawTargetWebgl::SetSimpleClipRect() {
    886  // Determine whether the clipping rectangle is simple enough to accelerate.
    887  // Check if there is a device space clip rectangle available from the Skia
    888  // target.
    889  if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false)) {
    890    // If the clip is empty, leave the final integer clip rectangle empty to
    891    // trivially discard the draw request.
    892    // If the clip rect is larger than the viewport, just set it to the
    893    // viewport.
    894    if (!clip->IsEmpty() && clip->Contains(GetRect())) {
    895      clip = Some(GetRect());
    896    }
    897    mSharedContext->SetClipRect(*clip);
    898    mSharedContext->SetNoClipMask();
    899    return true;
    900  }
    901 
    902  // There was no pixel-aligned clip rect available, so check the clip stack to
    903  // see if there is an AA'd axis-aligned rectangle clip.
    904  Rect rect(GetRect());
    905  for (auto& clipStack : mClipStack) {
    906    // If clip is a path or it has a non-axis-aligned transform, then it is
    907    // complex.
    908    if (clipStack.mPath ||
    909        !clipStack.mTransform.PreservesAxisAlignedRectangles()) {
    910      return false;
    911    }
    912    // Transform the rect and intersect it with the current clip.
    913    rect =
    914        clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect);
    915  }
    916  mSharedContext->SetClipRect(rect);
    917  mSharedContext->SetNoClipMask();
    918  return true;
    919 }
    920 
    921 // Installs the Skia clip rectangle, if applicable, onto the shared WebGL
    922 // context as well as sets the WebGL framebuffer to the current target.
    923 bool DrawTargetWebgl::PrepareContext(bool aClipped,
    924                                     const RefPtr<TextureHandle>& aHandle,
    925                                     const IntSize& aViewportSize) {
    926  if (!aClipped || aHandle) {
    927    // If no clipping requested, just set the clip rect to the viewport.
    928    mSharedContext->SetClipRect(
    929        aHandle
    930            ? IntRect(IntPoint(), !aViewportSize.IsEmpty()
    931                                      ? Min(aHandle->GetSize(), aViewportSize)
    932                                      : aHandle->GetSize())
    933            : GetRect());
    934    mSharedContext->SetNoClipMask();
    935    // Ensure the clip gets reset if clipping is later requested for the target.
    936    mRefreshClipState = true;
    937  } else if (mRefreshClipState || !mSharedContext->IsCurrentTarget(this)) {
    938    // Try to use a simple clip rect if possible. Otherwise, fall back to
    939    // generating a clip mask texture that can represent complex clip regions.
    940    if (!SetSimpleClipRect() && !GenerateComplexClipMask()) {
    941      return false;
    942    }
    943    mClipChanged = false;
    944    mRefreshClipState = false;
    945  }
    946  return mSharedContext->SetTarget(this, aHandle, aViewportSize);
    947 }
    948 
    949 void SharedContextWebgl::RestoreCurrentTarget(
    950    const RefPtr<WebGLTexture>& aClipMask) {
    951  if (!mCurrentTarget) {
    952    return;
    953  }
    954  mWebgl->BindFramebuffer(
    955      LOCAL_GL_FRAMEBUFFER,
    956      mTargetHandle ? mTargetFramebuffer : mCurrentTarget->mFramebuffer);
    957  IntPoint offset =
    958      mTargetHandle ? mTargetHandle->GetBounds().TopLeft() : IntPoint(0, 0);
    959  mWebgl->Viewport(offset.x, offset.y, mViewportSize.width,
    960                   mViewportSize.height);
    961  if (aClipMask) {
    962    SetClipMask(aClipMask);
    963  }
    964 }
    965 
    966 bool SharedContextWebgl::IsContextLost() const {
    967  return !mWebgl || mWebgl->IsContextLost();
    968 }
    969 
    970 // Signal to CanvasRenderingContext2D when the WebGL context is lost.
    971 bool DrawTargetWebgl::IsValid() const {
    972  return mSharedContext && !mSharedContext->IsContextLost();
    973 }
    974 
    975 bool DrawTargetWebgl::CanCreate(const IntSize& aSize, SurfaceFormat aFormat) {
    976  if (!gfxVars::UseAcceleratedCanvas2D()) {
    977    return false;
    978  }
    979 
    980  if (!Factory::AllowedSurfaceSize(aSize)) {
    981    return false;
    982  }
    983 
    984  // The interpretation of the min-size and max-size follows from the old
    985  // SkiaGL prefs. First just ensure that the context is not unreasonably
    986  // small.
    987  static const int32_t kMinDimension = 16;
    988  if (std::min(aSize.width, aSize.height) < kMinDimension) {
    989    return false;
    990  }
    991 
    992  int32_t minSize = StaticPrefs::gfx_canvas_accelerated_min_size();
    993  if (aSize.width * aSize.height < minSize * minSize) {
    994    return false;
    995  }
    996 
    997  // Maximum pref allows 3 different options:
    998  //  0 means unlimited size,
    999  //  > 0 means use value as an absolute threshold,
   1000  //  < 0 means use the number of screen pixels as a threshold.
   1001  int32_t maxSize = StaticPrefs::gfx_canvas_accelerated_max_size();
   1002  if (maxSize > 0) {
   1003    if (std::max(aSize.width, aSize.height) > maxSize) {
   1004      return false;
   1005    }
   1006  } else if (maxSize < 0) {
   1007    // Default to historical mobile screen size of 980x480, like FishIEtank.
   1008    // In addition, allow acceleration up to this size even if the screen is
   1009    // smaller. A lot content expects this size to work well. See Bug 999841
   1010    static const int32_t kScreenPixels = 980 * 480;
   1011 
   1012    if (RefPtr<widget::Screen> screen =
   1013            widget::ScreenManager::GetSingleton().GetPrimaryScreen()) {
   1014      LayoutDeviceIntSize screenSize = screen->GetRect().Size();
   1015      if (aSize.width * aSize.height >
   1016          std::max(screenSize.width * screenSize.height, kScreenPixels)) {
   1017        return false;
   1018      }
   1019    }
   1020  }
   1021 
   1022  return true;
   1023 }
   1024 
   1025 already_AddRefed<DrawTargetWebgl> DrawTargetWebgl::Create(
   1026    const IntSize& aSize, SurfaceFormat aFormat,
   1027    const RefPtr<SharedContextWebgl>& aSharedContext) {
   1028  // Validate the size and format.
   1029  if (!CanCreate(aSize, aFormat)) {
   1030    return nullptr;
   1031  }
   1032 
   1033  RefPtr<DrawTargetWebgl> dt = new DrawTargetWebgl;
   1034  if (!dt->Init(aSize, aFormat, aSharedContext) || !dt->IsValid()) {
   1035    return nullptr;
   1036  }
   1037 
   1038  return dt.forget();
   1039 }
   1040 
   1041 void* DrawTargetWebgl::GetNativeSurface(NativeSurfaceType aType) {
   1042  switch (aType) {
   1043    case NativeSurfaceType::WEBGL_CONTEXT:
   1044      // If the context is lost, then don't attempt to access it.
   1045      if (mSharedContext->IsContextLost()) {
   1046        return nullptr;
   1047      }
   1048      if (!mWebglValid) {
   1049        FlushFromSkia();
   1050      }
   1051      return mSharedContext->mWebgl.get();
   1052    default:
   1053      return nullptr;
   1054  }
   1055 }
   1056 
   1057 // Wrap a WebGL texture holding a snapshot with a texture handle. Note that
   1058 // while the texture is still in use as the backing texture of a framebuffer,
   1059 // it's texture memory is not currently tracked with other texture handles.
   1060 // Once it is finally orphaned and used as a texture handle, it must be added
   1061 // to the resource usage totals.
   1062 already_AddRefed<TextureHandle> SharedContextWebgl::WrapSnapshot(
   1063    const IntSize& aSize, SurfaceFormat aFormat, RefPtr<WebGLTexture> aTex) {
   1064  // Ensure there is enough space for the texture.
   1065  size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
   1066  PruneTextureMemory(usedBytes, false);
   1067  // Allocate a handle for the texture
   1068  RefPtr<StandaloneTexture> handle =
   1069      new StandaloneTexture(aSize, aFormat, aTex.forget());
   1070  mStandaloneTextures.push_back(handle);
   1071  mTextureHandles.insertFront(handle);
   1072  AddTextureMemory(handle);
   1073  mUsedTextureMemory += usedBytes;
   1074  ++mNumTextureHandles;
   1075  return handle.forget();
   1076 }
   1077 
   1078 void SharedContextWebgl::SetTexFilter(WebGLTexture* aTex, bool aFilter) {
   1079  mWebgl->TexParameter_base(
   1080      LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
   1081      FloatOrInt(aFilter ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST));
   1082  mWebgl->TexParameter_base(
   1083      LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
   1084      FloatOrInt(aFilter ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST));
   1085 }
   1086 
   1087 void SharedContextWebgl::InitTexParameters(WebGLTexture* aTex, bool aFilter) {
   1088  mWebgl->TexParameter_base(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
   1089                            FloatOrInt(LOCAL_GL_REPEAT));
   1090  mWebgl->TexParameter_base(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
   1091                            FloatOrInt(LOCAL_GL_REPEAT));
   1092  SetTexFilter(aTex, aFilter);
   1093 }
   1094 
   1095 // Copy the contents of the WebGL framebuffer into a WebGL texture.
   1096 already_AddRefed<TextureHandle> SharedContextWebgl::CopySnapshot(
   1097    const IntRect& aRect, TextureHandle* aHandle) {
   1098  if (!mWebgl || mWebgl->IsContextLost()) {
   1099    return nullptr;
   1100  }
   1101 
   1102  // If the target is going away, then we can just directly reuse the
   1103  // framebuffer texture since it will never change.
   1104  RefPtr<WebGLTexture> tex = mWebgl->CreateTexture();
   1105  if (!tex) {
   1106    return nullptr;
   1107  }
   1108 
   1109  // If copying from a non-DT source, we have to bind a scratch framebuffer for
   1110  // reading.
   1111  if (aHandle) {
   1112    BindScratchFramebuffer(aHandle, false);
   1113  }
   1114 
   1115  // Create a texture to hold the copy
   1116  BindAndInitRenderTex(tex, SurfaceFormat::B8G8R8A8, aRect.Size());
   1117  // Copy the framebuffer into the texture
   1118  mWebgl->CopyTexImage(LOCAL_GL_TEXTURE_2D, 0, 0, {0, 0, 0}, {aRect.x, aRect.y},
   1119                       {uint32_t(aRect.width), uint32_t(aRect.height)});
   1120 
   1121  SurfaceFormat format =
   1122      aHandle ? aHandle->GetFormat() : mCurrentTarget->GetFormat();
   1123  already_AddRefed<TextureHandle> result =
   1124      WrapSnapshot(aRect.Size(), format, tex.forget());
   1125 
   1126  // Restore the actual framebuffer after reading is done.
   1127  if (aHandle) {
   1128    RestoreCurrentTarget();
   1129  }
   1130 
   1131  return result;
   1132 }
   1133 
   1134 inline DrawTargetWebgl::AutoRestoreContext::AutoRestoreContext(
   1135    DrawTargetWebgl* aTarget)
   1136    : mTarget(aTarget),
   1137      mClipAARect(aTarget->mSharedContext->mClipAARect),
   1138      mLastClipMask(aTarget->mSharedContext->mLastClipMask) {}
   1139 
   1140 inline DrawTargetWebgl::AutoRestoreContext::~AutoRestoreContext() {
   1141  mTarget->mSharedContext->SetClipRect(mClipAARect);
   1142  if (mLastClipMask) {
   1143    mTarget->mSharedContext->SetClipMask(mLastClipMask);
   1144  }
   1145  mTarget->mRefreshClipState = true;
   1146 }
   1147 
   1148 // Utility method to install the target before copying a snapshot.
   1149 already_AddRefed<TextureHandle> DrawTargetWebgl::CopySnapshot(
   1150    const IntRect& aRect) {
   1151  AutoRestoreContext restore(this);
   1152  if (!PrepareContext(false)) {
   1153    return nullptr;
   1154  }
   1155  return mSharedContext->CopySnapshot(aRect);
   1156 }
   1157 
   1158 bool DrawTargetWebgl::HasDataSnapshot() const {
   1159  return (mSkiaValid && !mSkiaLayer) || (mSnapshot && mSnapshot->HasReadData());
   1160 }
   1161 
   1162 bool DrawTargetWebgl::PrepareSkia() {
   1163  if (!mSkiaValid) {
   1164    ReadIntoSkia();
   1165  } else if (mSkiaLayer) {
   1166    FlattenSkia();
   1167  }
   1168  return mSkiaValid;
   1169 }
   1170 
   1171 bool DrawTargetWebgl::EnsureDataSnapshot() {
   1172  // If there is already a data snapshot, there is nothing to do. If there is a
   1173  // snapshot that has a pending PBO readback, then try to force the readback.
   1174  // Otherwise, read back the WebGL framebuffer into the Skia DT.
   1175  return HasDataSnapshot() || (mSnapshot && mSnapshot->ForceReadFromPBO()) ||
   1176         PrepareSkia();
   1177 }
   1178 
   1179 void DrawTargetWebgl::PrepareShmem() { PrepareSkia(); }
   1180 
   1181 // Borrow a snapshot that may be used by another thread for composition. Only
   1182 // Skia snapshots are safe to pass around.
   1183 already_AddRefed<SourceSurface> DrawTargetWebgl::GetDataSnapshot() {
   1184  PrepareSkia();
   1185  return mSkia->Snapshot(mFormat);
   1186 }
   1187 
   1188 already_AddRefed<SourceSurface> DrawTargetWebgl::Snapshot() {
   1189  // If already using the Skia fallback, then just snapshot that.
   1190  if (mSkiaValid) {
   1191    return GetDataSnapshot();
   1192  }
   1193 
   1194  // There's no valid Skia snapshot, so we need to get one from the WebGL
   1195  // context.
   1196  if (!mSnapshot) {
   1197    // Create a copy-on-write reference to this target.
   1198    mSnapshot = new SourceSurfaceWebgl(this);
   1199  }
   1200  return do_AddRef(mSnapshot);
   1201 }
   1202 
   1203 // If we need to provide a snapshot for another DrawTargetWebgl that shares the
   1204 // same WebGL context, then it is safe to directly return a snapshot. Otherwise,
   1205 // we may be exporting to another thread and require a data snapshot.
   1206 already_AddRefed<SourceSurface> DrawTargetWebgl::GetOptimizedSnapshot(
   1207    DrawTarget* aTarget) {
   1208  if (aTarget && aTarget->GetBackendType() == BackendType::WEBGL &&
   1209      static_cast<DrawTargetWebgl*>(aTarget)->mSharedContext ==
   1210          mSharedContext) {
   1211    return Snapshot();
   1212  }
   1213  return GetDataSnapshot();
   1214 }
   1215 
   1216 // Read from the WebGL context into a buffer, either a memory buffer or a PBO.
   1217 // This handles both swizzling BGRA to RGBA and flipping the image.
   1218 bool SharedContextWebgl::ReadInto(uint8_t* aDstData, int32_t aDstStride,
   1219                                  SurfaceFormat aFormat, const IntRect& aBounds,
   1220                                  TextureHandle* aHandle,
   1221                                  const RefPtr<WebGLBuffer>& aBuffer) {
   1222  MOZ_ASSERT(aFormat == SurfaceFormat::B8G8R8A8 ||
   1223             aFormat == SurfaceFormat::B8G8R8X8 ||
   1224             aFormat == SurfaceFormat::A8);
   1225 
   1226  // If reading into a new texture, we have to bind it to a scratch framebuffer
   1227  // for reading.
   1228  if (aHandle) {
   1229    BindScratchFramebuffer(aHandle, false);
   1230  } else if (!aBuffer && mCurrentTarget && !mTargetHandle &&
   1231             mCurrentTarget->mIsClear) {
   1232    // If reading from a target that is still clear, then avoid the readback by
   1233    // just clearing the data.
   1234    SkPixmap(MakeSkiaImageInfo(aBounds.Size(), aFormat), aDstData, aDstStride)
   1235        .erase(IsOpaque(aFormat) ? SK_ColorBLACK : SK_ColorTRANSPARENT);
   1236    return true;
   1237  }
   1238 
   1239  webgl::ReadPixelsDesc desc;
   1240  desc.srcOffset = *ivec2::From(aBounds);
   1241  desc.size = *uvec2::FromSize(aBounds);
   1242  desc.packState.rowLength = aDstStride / BytesPerPixel(aFormat);
   1243  if (aBuffer) {
   1244    mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, aBuffer);
   1245    mWebgl->ReadPixelsPbo(desc, 0);
   1246    mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
   1247  } else {
   1248    Range<uint8_t> range = {aDstData, size_t(aDstStride) * aBounds.height};
   1249    mWebgl->ReadPixelsInto(desc, range);
   1250  }
   1251 
   1252  // Restore the actual framebuffer after reading is done.
   1253  if (aHandle) {
   1254    RestoreCurrentTarget();
   1255  }
   1256 
   1257  return true;
   1258 }
   1259 
   1260 already_AddRefed<DataSourceSurface> SharedContextWebgl::ReadSnapshot(
   1261    TextureHandle* aHandle, uint8_t* aData, int32_t aStride) {
   1262  // Allocate a data surface, map it, and read from the WebGL context into the
   1263  // surface.
   1264  SurfaceFormat format = SurfaceFormat::UNKNOWN;
   1265  IntRect bounds;
   1266  if (aHandle) {
   1267    format = aHandle->GetFormat();
   1268    bounds = aHandle->GetBounds();
   1269  } else {
   1270    if (!mCurrentTarget) {
   1271      return nullptr;
   1272    }
   1273    format = mCurrentTarget->GetFormat();
   1274    bounds = mCurrentTarget->GetRect();
   1275  }
   1276  RefPtr<DataSourceSurface> surface =
   1277      aData ? Factory::CreateWrappingDataSourceSurface(aData, aStride,
   1278                                                       bounds.Size(), format)
   1279            : Factory::CreateDataSourceSurface(bounds.Size(), format);
   1280  if (!surface) {
   1281    return nullptr;
   1282  }
   1283  DataSourceSurface::ScopedMap dstMap(surface, DataSourceSurface::WRITE);
   1284  if (!dstMap.IsMapped() || !ReadInto(dstMap.GetData(), dstMap.GetStride(),
   1285                                      format, bounds, aHandle)) {
   1286    return nullptr;
   1287  }
   1288  return surface.forget();
   1289 }
   1290 
   1291 static inline int32_t GetPBOStride(int32_t aWidth, SurfaceFormat aFormat) {
   1292  return GetAlignedStride<16>(aWidth, BytesPerPixel(aFormat));
   1293 }
   1294 
   1295 already_AddRefed<WebGLBuffer> SharedContextWebgl::ReadSnapshotIntoPBO(
   1296    SourceSurfaceWebgl* aOwner, TextureHandle* aHandle) {
   1297  // Allocate a PBO, and read from the WebGL context into it.
   1298  SurfaceFormat format = SurfaceFormat::UNKNOWN;
   1299  IntRect bounds;
   1300  if (aHandle) {
   1301    format = aHandle->GetFormat();
   1302    bounds = aHandle->GetBounds();
   1303  } else {
   1304    if (!mCurrentTarget) {
   1305      return nullptr;
   1306    }
   1307    format = mCurrentTarget->GetFormat();
   1308    bounds = mCurrentTarget->GetRect();
   1309  }
   1310  int32_t pboStride = GetPBOStride(bounds.width, format);
   1311  size_t bufSize = BufferSizeFromStrideAndHeight(pboStride, bounds.height);
   1312  if (!bufSize) {
   1313    return nullptr;
   1314  }
   1315 
   1316  // If the PBO is too large to fit within the memory limit by itself, then
   1317  // don't try to use a PBO.
   1318  size_t maxPBOMemory =
   1319      StaticPrefs::gfx_canvas_accelerated_max_snapshot_pbo_memory();
   1320  if (bufSize > maxPBOMemory) {
   1321    return nullptr;
   1322  }
   1323 
   1324  RefPtr<WebGLBuffer> pbo = mWebgl->CreateBuffer();
   1325  if (!pbo) {
   1326    return nullptr;
   1327  }
   1328  mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pbo);
   1329  mWebgl->UninitializedBufferData_SizeOnly(LOCAL_GL_PIXEL_PACK_BUFFER, bufSize,
   1330                                           LOCAL_GL_STREAM_READ);
   1331  mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
   1332  if (!ReadInto(nullptr, pboStride, format, bounds, aHandle, pbo)) {
   1333    return nullptr;
   1334  }
   1335 
   1336  // If there are existing snapshot PBOs, check if adding this PBO would exceed
   1337  // the memory limit for snapshot PBOs. This happens after the new PBO was set
   1338  // up and the readback initiated, in case purging an old PBO causes a stall
   1339  // which can be used to cover the latency of the readback for the new PBO.
   1340  ClearSnapshotPBOs(maxPBOMemory - std::min(bufSize, maxPBOMemory));
   1341 
   1342  mUsedSnapshotPBOMemory += bufSize;
   1343  mSnapshotPBOs.emplace_back(aOwner);
   1344  return pbo.forget();
   1345 }
   1346 
   1347 already_AddRefed<DataSourceSurface> SharedContextWebgl::ReadSnapshotFromPBO(
   1348    const RefPtr<WebGLBuffer>& aBuffer, SurfaceFormat aFormat,
   1349    const IntSize& aSize, uint8_t* aData, int32_t aStride) {
   1350  // For an existing PBO where a readback has been initiated previously, create
   1351  // a new data surface and copy the PBO's data into the data surface.
   1352  int32_t pboStride = GetPBOStride(aSize.width, aFormat);
   1353  size_t bufSize =
   1354      BufferSizeFromStrideAndHeight(aData ? aStride : pboStride, aSize.height);
   1355  if (!bufSize) {
   1356    return nullptr;
   1357  }
   1358  RefPtr<DataSourceSurface> surface =
   1359      aData ? Factory::CreateWrappingDataSourceSurface(aData, aStride, aSize,
   1360                                                       aFormat)
   1361            : Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat,
   1362                                                         pboStride);
   1363  if (!surface) {
   1364    return nullptr;
   1365  }
   1366  DataSourceSurface::ScopedMap dstMap(surface, DataSourceSurface::WRITE);
   1367  if (!dstMap.IsMapped()) {
   1368    return nullptr;
   1369  }
   1370  mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, aBuffer);
   1371  Range<uint8_t> range = {dstMap.GetData(), bufSize};
   1372  bool success = mWebgl->AsWebGL2()->GetBufferSubData(
   1373      LOCAL_GL_PIXEL_PACK_BUFFER, 0, range, aSize.height,
   1374      BytesPerPixel(aFormat) * aSize.height, pboStride, dstMap.GetStride());
   1375  mWebgl->BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
   1376  if (success) {
   1377    return surface.forget();
   1378  }
   1379  return nullptr;
   1380 }
   1381 
   1382 void SharedContextWebgl::RemoveSnapshotPBO(
   1383    SourceSurfaceWebgl* aOwner, already_AddRefed<WebGLBuffer> aBuffer) {
   1384  RefPtr<WebGLBuffer> buffer(aBuffer);
   1385  MOZ_ASSERT(aOwner && buffer);
   1386  IntSize size = aOwner->GetSize();
   1387  SurfaceFormat format = aOwner->GetFormat();
   1388  int32_t pboStride = GetPBOStride(size.width, format);
   1389  size_t bufSize = BufferSizeFromStrideAndHeight(pboStride, size.height);
   1390  // If the queue is empty, no memory should be used. Otherwise, deduct the
   1391  // usage from the queue.
   1392  if (mSnapshotPBOs.empty()) {
   1393    mUsedSnapshotPBOMemory = 0;
   1394  } else if (bufSize) {
   1395    mUsedSnapshotPBOMemory -= std::min(mUsedSnapshotPBOMemory, bufSize);
   1396  }
   1397 }
   1398 
   1399 void SharedContextWebgl::ClearSnapshotPBOs(size_t aMaxMemory) {
   1400  // Force any pending readback PBOs to convert to actual data.
   1401  while (!mSnapshotPBOs.empty() &&
   1402         (!aMaxMemory || mUsedSnapshotPBOMemory > aMaxMemory)) {
   1403    RefPtr<SourceSurfaceWebgl> snapshot(mSnapshotPBOs.front());
   1404    mSnapshotPBOs.pop_front();
   1405    if (snapshot) {
   1406      snapshot->ForceReadFromPBO();
   1407    }
   1408  }
   1409  if (mSnapshotPBOs.empty()) {
   1410    mUsedSnapshotPBOMemory = 0;
   1411  }
   1412 }
   1413 
   1414 // Utility method to install the target before reading a snapshot.
   1415 bool DrawTargetWebgl::ReadInto(uint8_t* aDstData, int32_t aDstStride) {
   1416  if (!PrepareContext(false)) {
   1417    return false;
   1418  }
   1419 
   1420  return mSharedContext->ReadInto(aDstData, aDstStride, GetFormat(), GetRect());
   1421 }
   1422 
   1423 // Utility method to install the target before reading a snapshot.
   1424 already_AddRefed<DataSourceSurface> DrawTargetWebgl::ReadSnapshot(
   1425    uint8_t* aData, int32_t aStride) {
   1426  AutoRestoreContext restore(this);
   1427  if (!PrepareContext(false)) {
   1428    return nullptr;
   1429  }
   1430  mProfile.OnReadback();
   1431  return mSharedContext->ReadSnapshot(nullptr, aData, aStride);
   1432 }
   1433 
   1434 already_AddRefed<WebGLBuffer> DrawTargetWebgl::ReadSnapshotIntoPBO(
   1435    SourceSurfaceWebgl* aOwner) {
   1436  AutoRestoreContext restore(this);
   1437  if (!PrepareContext(false)) {
   1438    return nullptr;
   1439  }
   1440  mProfile.OnReadback();
   1441  return mSharedContext->ReadSnapshotIntoPBO(aOwner);
   1442 }
   1443 
   1444 already_AddRefed<SourceSurface> DrawTargetWebgl::GetBackingSurface() {
   1445  return Snapshot();
   1446 }
   1447 
   1448 void DrawTargetWebgl::DetachAllSnapshots() {
   1449  mSkia->DetachAllSnapshots();
   1450  ClearSnapshot();
   1451 }
   1452 
   1453 // Prepare the framebuffer for accelerated drawing. Any cached snapshots will
   1454 // be invalidated if not detached and copied here. Ensure the WebGL
   1455 // framebuffer's contents are updated if still somehow stored in the Skia
   1456 // framebuffer.
   1457 bool DrawTargetWebgl::MarkChanged() {
   1458  if (mSnapshot) {
   1459    // Try to copy the target into a new texture if possible.
   1460    ClearSnapshot(true, true);
   1461  }
   1462  if (!mWebglValid && !FlushFromSkia()) {
   1463    return false;
   1464  }
   1465  mSkiaValid = false;
   1466  mIsClear = false;
   1467  return true;
   1468 }
   1469 
   1470 void DrawTargetWebgl::MarkSkiaChanged(bool aOverwrite) {
   1471  if (aOverwrite) {
   1472    mSkiaValid = true;
   1473    mSkiaLayer = false;
   1474  } else if (!mSkiaValid) {
   1475    if (ReadIntoSkia()) {
   1476      // Signal that we've hit a complete software fallback.
   1477      mProfile.OnFallback();
   1478    }
   1479  } else if (mSkiaLayer && !mLayerDepth) {
   1480    FlattenSkia();
   1481  }
   1482  mWebglValid = false;
   1483  mIsClear = false;
   1484 }
   1485 
   1486 // Whether a given composition operator is associative and thus allows drawing
   1487 // into a separate layer that can be later composited back into the WebGL
   1488 // context.
   1489 static inline bool SupportsLayering(const DrawOptions& aOptions) {
   1490  switch (aOptions.mCompositionOp) {
   1491    case CompositionOp::OP_OVER:
   1492      // Layering is only supported for the default source-over composition op.
   1493      return true;
   1494    default:
   1495      return false;
   1496  }
   1497 }
   1498 
   1499 void DrawTargetWebgl::MarkSkiaChanged(const DrawOptions& aOptions) {
   1500  if (SupportsLayering(aOptions)) {
   1501    if (!mSkiaValid) {
   1502      // If the Skia context needs initialization, clear it and enable layering.
   1503      mSkiaValid = true;
   1504      if (mWebglValid) {
   1505        mProfile.OnLayer();
   1506        mSkiaLayer = true;
   1507        mSkiaLayerClear = mIsClear;
   1508        mSkia->DetachAllSnapshots();
   1509        if (mSkiaLayerClear) {
   1510          // Avoid blending later by making sure the layer background is filled
   1511          // with opaque alpha values if necessary.
   1512          mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
   1513                                DrawOptions(1.0f, CompositionOp::OP_SOURCE));
   1514        } else {
   1515          mSkiaNoClip->ClearRect(Rect(mSkiaNoClip->GetRect()));
   1516        }
   1517      }
   1518    }
   1519    // The WebGL context is no longer up-to-date.
   1520    mWebglValid = false;
   1521    mIsClear = false;
   1522  } else {
   1523    // For other composition ops, just overwrite the Skia data.
   1524    MarkSkiaChanged();
   1525  }
   1526 }
   1527 
   1528 bool DrawTargetWebgl::LockBits(uint8_t** aData, IntSize* aSize,
   1529                               int32_t* aStride, SurfaceFormat* aFormat,
   1530                               IntPoint* aOrigin) {
   1531  // Can only access pixels if there is valid, flattened Skia data.
   1532  if (mSkiaValid && !mSkiaLayer) {
   1533    MarkSkiaChanged();
   1534    return mSkia->LockBits(aData, aSize, aStride, aFormat, aOrigin);
   1535  }
   1536  return false;
   1537 }
   1538 
   1539 void DrawTargetWebgl::ReleaseBits(uint8_t* aData) {
   1540  // Can only access pixels if there is valid, flattened Skia data.
   1541  if (mSkiaValid && !mSkiaLayer) {
   1542    mSkia->ReleaseBits(aData);
   1543  }
   1544 }
   1545 
   1546 // Format is x, y, alpha
   1547 static const float kRectVertexData[12] = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
   1548                                          1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};
   1549 
   1550 // Orphans the contents of the path vertex buffer. The beginning of the buffer
   1551 // always contains data for a simple rectangle draw to avoid needing to switch
   1552 // buffers.
   1553 void SharedContextWebgl::ResetPathVertexBuffer() {
   1554  if (!mPathVertexBuffer) {
   1555    MOZ_ASSERT(false);
   1556    return;
   1557  }
   1558 
   1559  size_t oldCapacity = mPathVertexBuffer->ByteLength();
   1560  RemoveUntrackedTextureMemory(oldCapacity);
   1561 
   1562  mWebgl->BindBuffer(LOCAL_GL_ARRAY_BUFFER, mPathVertexBuffer.get());
   1563  mWebgl->UninitializedBufferData_SizeOnly(
   1564      LOCAL_GL_ARRAY_BUFFER,
   1565      std::max(size_t(mPathVertexCapacity), sizeof(kRectVertexData)),
   1566      LOCAL_GL_DYNAMIC_DRAW);
   1567  mWebgl->BufferSubData(LOCAL_GL_ARRAY_BUFFER, 0, sizeof(kRectVertexData),
   1568                        (const uint8_t*)kRectVertexData);
   1569  mPathVertexOffset = sizeof(kRectVertexData);
   1570 
   1571  size_t newCapacity = mPathVertexBuffer->ByteLength();
   1572  AddUntrackedTextureMemory(newCapacity);
   1573 
   1574  if (mWGROutputBuffer &&
   1575      (!mPathVertexCapacity || newCapacity != oldCapacity)) {
   1576    RemoveHeapData(mWGROutputBuffer.get());
   1577    mWGROutputBuffer = nullptr;
   1578  }
   1579 
   1580  if (mPathVertexCapacity > 0 && !mWGROutputBuffer) {
   1581    mWGROutputBuffer.reset(new (
   1582        fallible) WGR::OutputVertex[newCapacity / sizeof(WGR::OutputVertex)]);
   1583    AddHeapData(mWGROutputBuffer.get());
   1584  }
   1585 }
   1586 
   1587 // Sigma below which contribution of neighbor pixels is visually insignificant.
   1588 #define BLUR_ACCEL_SIGMA_MIN 0.27f
   1589 // Maximum blur sigma allowed by shadows and filters currently.
   1590 #define BLUR_ACCEL_SIGMA_MAX 100
   1591 #define BLUR_ACCEL_RADIUS(sigma) (int(ceil(1.5 * (sigma))) * 2)
   1592 #define BLUR_ACCEL_RADIUS_MAX (3 * BLUR_ACCEL_SIGMA_MAX)
   1593 
   1594 // Threshold for when to downscale blur inputs.
   1595 #define BLUR_ACCEL_DOWNSCALE_SIGMA 20
   1596 #define BLUR_ACCEL_DOWNSCALE_SIZE 32
   1597 // How much to downscale blur inputs.
   1598 #define BLUR_ACCEL_DOWNSCALE_ITERS 2
   1599 
   1600 // Attempts to create all shaders and resources to be used for drawing commands.
   1601 // Returns whether or not this succeeded.
   1602 bool SharedContextWebgl::CreateShaders() {
   1603  if (!mPathVertexArray) {
   1604    mPathVertexArray = mWebgl->CreateVertexArray();
   1605  }
   1606  if (!mPathVertexBuffer) {
   1607    mPathVertexBuffer = mWebgl->CreateBuffer();
   1608    AddUntrackedTextureMemory(mPathVertexBuffer);
   1609    mWebgl->BindVertexArray(mPathVertexArray.get());
   1610    ResetPathVertexBuffer();
   1611    mWebgl->EnableVertexAttribArray(0);
   1612 
   1613    webgl::VertAttribPointerDesc attribDesc;
   1614    attribDesc.channels = 3;
   1615    attribDesc.type = LOCAL_GL_FLOAT;
   1616    attribDesc.normalized = false;
   1617    mWebgl->VertexAttribPointer(0, attribDesc);
   1618  }
   1619  if (!mSolidProgram) {
   1620    // AA is computed by using the basis vectors of the transform to determine
   1621    // both the scale and orientation. The scale is then used to extrude the
   1622    // rectangle outward by 1 screen-space pixel to account for the AA region.
   1623    // The distance to the rectangle edges is passed to the fragment shader in
   1624    // an interpolant, biased by 0.5 so it represents the desired coverage. The
   1625    // minimum coverage is then chosen by the fragment shader to use as an AA
   1626    // coverage value to modulate the color.
   1627    auto vsSource =
   1628        "attribute vec3 a_vertex;\n"
   1629        "uniform vec2 u_transform[3];\n"
   1630        "uniform vec2 u_viewport;\n"
   1631        "uniform vec4 u_clipbounds;\n"
   1632        "uniform float u_aa;\n"
   1633        "varying vec2 v_cliptc;\n"
   1634        "varying vec4 v_clipdist;\n"
   1635        "varying vec4 v_dist;\n"
   1636        "varying float v_alpha;\n"
   1637        "void main() {\n"
   1638        "   vec2 scale = vec2(dot(u_transform[0], u_transform[0]),\n"
   1639        "                     dot(u_transform[1], u_transform[1]));\n"
   1640        "   vec2 invScale = u_aa * inversesqrt(scale + 1.0e-6);\n"
   1641        "   scale *= invScale;\n"
   1642        "   vec2 extrude = a_vertex.xy +\n"
   1643        "                  invScale * (2.0 * a_vertex.xy - 1.0);\n"
   1644        "   vec2 vertex = u_transform[0] * extrude.x +\n"
   1645        "                 u_transform[1] * extrude.y +\n"
   1646        "                 u_transform[2];\n"
   1647        "   gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
   1648        "   v_cliptc = vertex / u_viewport;\n"
   1649        "   v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
   1650        "                     u_clipbounds.zw - vertex);\n"
   1651        "   float noAA = 1.0 - u_aa;\n"
   1652        "   v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 0.5 + noAA;\n"
   1653        "   v_alpha = min(a_vertex.z,\n"
   1654        "                 min(scale.x, 1.0) * min(scale.y, 1.0) + noAA);\n"
   1655        "}\n";
   1656    auto fsSource =
   1657        "precision mediump float;\n"
   1658        "uniform vec4 u_color;\n"
   1659        "uniform sampler2D u_clipmask;\n"
   1660        "varying highp vec2 v_cliptc;\n"
   1661        "varying vec4 v_clipdist;\n"
   1662        "varying vec4 v_dist;\n"
   1663        "varying float v_alpha;\n"
   1664        "void main() {\n"
   1665        "   float clip = texture2D(u_clipmask, v_cliptc).r;\n"
   1666        "   vec4 dist = min(v_dist, v_clipdist);\n"
   1667        "   dist.xy = min(dist.xy, dist.zw);\n"
   1668        "   float aa = clamp(min(dist.x, dist.y), 0.0, v_alpha);\n"
   1669        "   gl_FragColor = clip * aa * u_color;\n"
   1670        "}\n";
   1671    RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER);
   1672    mWebgl->ShaderSource(*vsId, vsSource);
   1673    mWebgl->CompileShader(*vsId);
   1674    if (!mWebgl->GetCompileResult(*vsId).success) {
   1675      return false;
   1676    }
   1677    RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER);
   1678    mWebgl->ShaderSource(*fsId, fsSource);
   1679    mWebgl->CompileShader(*fsId);
   1680    if (!mWebgl->GetCompileResult(*fsId).success) {
   1681      return false;
   1682    }
   1683    mSolidProgram = mWebgl->CreateProgram();
   1684    mWebgl->AttachShader(*mSolidProgram, *vsId);
   1685    mWebgl->AttachShader(*mSolidProgram, *fsId);
   1686    mWebgl->BindAttribLocation(*mSolidProgram, 0, "a_vertex");
   1687    mWebgl->LinkProgram(*mSolidProgram);
   1688    if (!mWebgl->GetLinkResult(*mSolidProgram).success) {
   1689      return false;
   1690    }
   1691    mSolidProgramViewport = GetUniformLocation(mSolidProgram, "u_viewport");
   1692    mSolidProgramAA = GetUniformLocation(mSolidProgram, "u_aa");
   1693    mSolidProgramTransform = GetUniformLocation(mSolidProgram, "u_transform");
   1694    mSolidProgramColor = GetUniformLocation(mSolidProgram, "u_color");
   1695    mSolidProgramClipMask = GetUniformLocation(mSolidProgram, "u_clipmask");
   1696    mSolidProgramClipBounds = GetUniformLocation(mSolidProgram, "u_clipbounds");
   1697    if (!mSolidProgramViewport || !mSolidProgramAA || !mSolidProgramTransform ||
   1698        !mSolidProgramColor || !mSolidProgramClipMask ||
   1699        !mSolidProgramClipBounds) {
   1700      return false;
   1701    }
   1702    mWebgl->UseProgram(mSolidProgram);
   1703    UniformData(LOCAL_GL_INT, mSolidProgramClipMask, Array<int32_t, 1>{1});
   1704  }
   1705 
   1706  if (!mImageProgram) {
   1707    auto vsSource =
   1708        "attribute vec3 a_vertex;\n"
   1709        "uniform vec2 u_viewport;\n"
   1710        "uniform vec4 u_clipbounds;\n"
   1711        "uniform float u_aa;\n"
   1712        "uniform vec2 u_transform[3];\n"
   1713        "uniform vec2 u_texmatrix[3];\n"
   1714        "varying vec2 v_cliptc;\n"
   1715        "varying vec2 v_texcoord;\n"
   1716        "varying vec4 v_clipdist;\n"
   1717        "varying vec4 v_dist;\n"
   1718        "varying float v_alpha;\n"
   1719        "void main() {\n"
   1720        "   vec2 scale = vec2(dot(u_transform[0], u_transform[0]),\n"
   1721        "                     dot(u_transform[1], u_transform[1]));\n"
   1722        "   vec2 invScale = u_aa * inversesqrt(scale + 1.0e-6);\n"
   1723        "   scale *= invScale;\n"
   1724        "   vec2 extrude = a_vertex.xy +\n"
   1725        "                  invScale * (2.0 * a_vertex.xy - 1.0);\n"
   1726        "   vec2 vertex = u_transform[0] * extrude.x +\n"
   1727        "                 u_transform[1] * extrude.y +\n"
   1728        "                 u_transform[2];\n"
   1729        "   gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
   1730        "   v_cliptc = vertex / u_viewport;\n"
   1731        "   v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
   1732        "                     u_clipbounds.zw - vertex);\n"
   1733        "   v_texcoord = u_texmatrix[0] * extrude.x +\n"
   1734        "                u_texmatrix[1] * extrude.y +\n"
   1735        "                u_texmatrix[2];\n"
   1736        "   float noAA = 1.0 - u_aa;\n"
   1737        "   v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 0.5 + noAA;\n"
   1738        "   v_alpha = min(a_vertex.z,\n"
   1739        "                 min(scale.x, 1.0) * min(scale.y, 1.0) + noAA);\n"
   1740        "}\n";
   1741    auto fsSource =
   1742        "precision mediump float;\n"
   1743        "uniform vec4 u_texbounds;\n"
   1744        "uniform vec4 u_color;\n"
   1745        "uniform float u_swizzle;\n"
   1746        "uniform sampler2D u_sampler;\n"
   1747        "uniform sampler2D u_clipmask;\n"
   1748        "varying highp vec2 v_cliptc;\n"
   1749        "varying highp vec2 v_texcoord;\n"
   1750        "varying vec4 v_clipdist;\n"
   1751        "varying vec4 v_dist;\n"
   1752        "varying float v_alpha;\n"
   1753        "void main() {\n"
   1754        "   highp vec2 tc = clamp(v_texcoord, u_texbounds.xy,\n"
   1755        "                         u_texbounds.zw);\n"
   1756        "   vec4 image = texture2D(u_sampler, tc);\n"
   1757        "   float clip = texture2D(u_clipmask, v_cliptc).r;\n"
   1758        "   vec4 dist = min(v_dist, v_clipdist);\n"
   1759        "   dist.xy = min(dist.xy, dist.zw);\n"
   1760        "   float aa = clamp(min(dist.x, dist.y), 0.0, v_alpha);\n"
   1761        "   gl_FragColor = clip * aa * u_color *\n"
   1762        "                  mix(image, image.rrrr, u_swizzle);\n"
   1763        "}\n";
   1764    RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER);
   1765    mWebgl->ShaderSource(*vsId, vsSource);
   1766    mWebgl->CompileShader(*vsId);
   1767    if (!mWebgl->GetCompileResult(*vsId).success) {
   1768      return false;
   1769    }
   1770    RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER);
   1771    mWebgl->ShaderSource(*fsId, fsSource);
   1772    mWebgl->CompileShader(*fsId);
   1773    if (!mWebgl->GetCompileResult(*fsId).success) {
   1774      return false;
   1775    }
   1776    mImageProgram = mWebgl->CreateProgram();
   1777    mWebgl->AttachShader(*mImageProgram, *vsId);
   1778    mWebgl->AttachShader(*mImageProgram, *fsId);
   1779    mWebgl->BindAttribLocation(*mImageProgram, 0, "a_vertex");
   1780    mWebgl->LinkProgram(*mImageProgram);
   1781    if (!mWebgl->GetLinkResult(*mImageProgram).success) {
   1782      return false;
   1783    }
   1784    mImageProgramViewport = GetUniformLocation(mImageProgram, "u_viewport");
   1785    mImageProgramAA = GetUniformLocation(mImageProgram, "u_aa");
   1786    mImageProgramTransform = GetUniformLocation(mImageProgram, "u_transform");
   1787    mImageProgramTexMatrix = GetUniformLocation(mImageProgram, "u_texmatrix");
   1788    mImageProgramTexBounds = GetUniformLocation(mImageProgram, "u_texbounds");
   1789    mImageProgramSwizzle = GetUniformLocation(mImageProgram, "u_swizzle");
   1790    mImageProgramColor = GetUniformLocation(mImageProgram, "u_color");
   1791    mImageProgramSampler = GetUniformLocation(mImageProgram, "u_sampler");
   1792    mImageProgramClipMask = GetUniformLocation(mImageProgram, "u_clipmask");
   1793    mImageProgramClipBounds = GetUniformLocation(mImageProgram, "u_clipbounds");
   1794    if (!mImageProgramViewport || !mImageProgramAA || !mImageProgramTransform ||
   1795        !mImageProgramTexMatrix || !mImageProgramTexBounds ||
   1796        !mImageProgramSwizzle || !mImageProgramColor || !mImageProgramSampler ||
   1797        !mImageProgramClipMask || !mImageProgramClipBounds) {
   1798      return false;
   1799    }
   1800    mWebgl->UseProgram(mImageProgram);
   1801    UniformData(LOCAL_GL_INT, mImageProgramSampler, Array<int32_t, 1>{0});
   1802    UniformData(LOCAL_GL_INT, mImageProgramClipMask, Array<int32_t, 1>{1});
   1803  }
   1804  if (!mBlurProgram) {
   1805    auto vsSource =
   1806        "#version 300 es\n"
   1807        "uniform vec2 u_viewport;\n"
   1808        "uniform vec4 u_clipbounds;\n"
   1809        "uniform vec4 u_transform;\n"
   1810        "uniform vec4 u_texmatrix;\n"
   1811        "uniform vec4 u_texbounds;\n"
   1812        "uniform vec2 u_offsetscale;\n"
   1813        "uniform float u_sigma;\n"
   1814        "in vec3 a_vertex;\n"
   1815        "out vec2 v_cliptc;\n"
   1816        "out vec2 v_texcoord;\n"
   1817        "out vec4 v_texbounds;\n"
   1818        "out vec4 v_clipdist;\n"
   1819        "flat out vec2 v_gauss_coeffs;\n"
   1820        "flat out ivec2 v_support;\n"
   1821        "void calculate_gauss_coeffs(float sigma) {\n"
   1822        "  v_gauss_coeffs = vec2(1.0 / (sqrt(2.0 * 3.14159265) * sigma),\n"
   1823        "                        exp(-0.5 / (sigma * sigma)));\n"
   1824        "  vec3 gauss_coeff = vec3(v_gauss_coeffs,\n"
   1825        "                           v_gauss_coeffs.y * v_gauss_coeffs.y);\n"
   1826        "  float gauss_coeff_total = gauss_coeff.x;\n"
   1827        "  for (int i = 1; i <= v_support.x; i += 2) {\n"
   1828        "    gauss_coeff.xy *= gauss_coeff.yz;\n"
   1829        "    float gauss_coeff_subtotal = gauss_coeff.x;\n"
   1830        "    gauss_coeff.xy *= gauss_coeff.yz;\n"
   1831        "    gauss_coeff_subtotal += gauss_coeff.x;\n"
   1832        "    gauss_coeff_total += 2.0 * gauss_coeff_subtotal;\n"
   1833        "  }\n"
   1834        "  v_gauss_coeffs.x /= gauss_coeff_total;\n"
   1835        "}\n"
   1836        "void main() {\n"
   1837        "  vec2 vertex = u_transform.xy * a_vertex.xy + u_transform.zw;\n"
   1838        "  gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
   1839        "  v_cliptc = vertex / u_viewport;\n"
   1840        "  v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
   1841        "                    u_clipbounds.zw - vertex);\n"
   1842        "  v_texcoord = u_texmatrix.xy * a_vertex.xy + u_texmatrix.zw;\n"
   1843        "  vec4 texbounds = vec4(v_texcoord - u_texbounds.xy,\n"
   1844        "                        u_texbounds.zw - v_texcoord);\n"
   1845        "  v_texbounds = u_offsetscale.x != 0.0 ?\n"
   1846        "     vec4(texbounds.xz / u_offsetscale.x, texbounds.yw) :\n"
   1847        "     vec4(texbounds.yw / u_offsetscale.y, texbounds.xz);\n"
   1848        "  v_support.x = " MOZ_STRINGIFY(BLUR_ACCEL_RADIUS(u_sigma)) ";\n"
   1849        "  calculate_gauss_coeffs(u_sigma);\n"
   1850        "}\n";
   1851    auto fsSource =
   1852        "#version 300 es\n"
   1853        "precision mediump float;\n"
   1854        "uniform vec4 u_color;\n"
   1855        "uniform float u_swizzle;\n"
   1856        "uniform highp vec4 u_texbounds;\n"
   1857        "uniform highp vec2 u_offsetscale;\n"
   1858        "uniform sampler2D u_sampler;\n"
   1859        "uniform sampler2D u_clipmask;\n"
   1860        "in highp vec2 v_cliptc;\n"
   1861        "in highp vec2 v_texcoord;\n"
   1862        "in highp vec4 v_texbounds;\n"
   1863        "in vec4 v_clipdist;\n"
   1864        "flat in vec2 v_gauss_coeffs;\n"
   1865        "flat in ivec2 v_support;\n"
   1866        "out vec4 out_FragColor;\n"
   1867        "void main() {\n"
   1868        "  vec3 gauss_coeff = vec3(v_gauss_coeffs,\n"
   1869        "                          v_gauss_coeffs.y * v_gauss_coeffs.y);\n"
   1870        "  bvec4 inside = greaterThanEqual(v_texbounds, vec4(0.0));\n"
   1871        "  vec4 avg_color = texture(u_sampler, v_texcoord) *\n"
   1872        "                   (all(inside.xy) ? gauss_coeff.x : 0.0);\n"
   1873        "  int support = min(v_support.x,\n"
   1874        "                    " MOZ_STRINGIFY(BLUR_ACCEL_RADIUS_MAX) ");\n"
   1875        "  for (int i = 1; i <= support; i += 2) {\n"
   1876        "    gauss_coeff.xy *= gauss_coeff.yz;\n"
   1877        "    float gauss_coeff_subtotal = gauss_coeff.x;\n"
   1878        "    gauss_coeff.xy *= gauss_coeff.yz;\n"
   1879        "    gauss_coeff_subtotal += gauss_coeff.x;\n"
   1880        "    float gauss_ratio = gauss_coeff.x / gauss_coeff_subtotal;\n"
   1881        "    vec4 curbounds = v_texbounds.xyxy + vec4(-1.0, 1.0, 1.0, -1.0) * float(i);\n"
   1882        "    bvec4 inside0 = greaterThanEqual(curbounds.xyxy, vec4(0.0, 0.0, 1.0, -1.0));\n"
   1883        "    bvec4 inside1 = greaterThanEqual(curbounds.zwzw, vec4(0.0, 0.0, -1.0, 1.0));\n"
   1884        "    vec2 weights0 =\n"
   1885        "      (all(inside0.xy) ? vec2(1.0, gauss_ratio) : vec2(gauss_ratio, 1.0)) -\n"
   1886        "        (all(inside0.zw) ? 0.0 : gauss_ratio);\n"
   1887        "    vec2 weights1 =\n"
   1888        "      (all(inside1.xy) ? vec2(1.0, gauss_ratio) : vec2(gauss_ratio, 1.0)) -\n"
   1889        "        (all(inside1.zw) ? 0.0 : gauss_ratio);\n"
   1890        "    vec2 tc0 = v_texcoord - u_offsetscale * (float(i) + weights0.y);\n"
   1891        "    vec2 tc1 = v_texcoord + u_offsetscale * (float(i) + weights1.y);\n"
   1892        "    avg_color += gauss_coeff_subtotal * (\n"
   1893        "      texture(u_sampler, tc0) * weights0.x +\n"
   1894        "      texture(u_sampler, tc1) * weights1.x);\n"
   1895        "  }\n"
   1896        "  float clip = texture(u_clipmask, v_cliptc).r;\n"
   1897        "  vec2 dist = min(v_clipdist.xy, v_clipdist.zw);\n"
   1898        "  float aa = clamp(min(dist.x, dist.y), 0.0, 1.0);\n"
   1899        "  out_FragColor = clip * aa * u_color * float(all(inside.zw)) *\n"
   1900        "                  mix(avg_color, avg_color.rrrr, u_swizzle);\n"
   1901        "}\n";
   1902    RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER);
   1903    mWebgl->ShaderSource(*vsId, vsSource);
   1904    mWebgl->CompileShader(*vsId);
   1905    if (!mWebgl->GetCompileResult(*vsId).success) {
   1906      return false;
   1907    }
   1908    RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER);
   1909    mWebgl->ShaderSource(*fsId, fsSource);
   1910    mWebgl->CompileShader(*fsId);
   1911    if (!mWebgl->GetCompileResult(*fsId).success) {
   1912      return false;
   1913    }
   1914    mBlurProgram = mWebgl->CreateProgram();
   1915    mWebgl->AttachShader(*mBlurProgram, *vsId);
   1916    mWebgl->AttachShader(*mBlurProgram, *fsId);
   1917    mWebgl->BindAttribLocation(*mBlurProgram, 0, "a_vertex");
   1918    mWebgl->LinkProgram(*mBlurProgram);
   1919    if (!mWebgl->GetLinkResult(*mBlurProgram).success) {
   1920      return false;
   1921    }
   1922    mBlurProgramViewport = GetUniformLocation(mBlurProgram, "u_viewport");
   1923    mBlurProgramTransform = GetUniformLocation(mBlurProgram, "u_transform");
   1924    mBlurProgramTexMatrix = GetUniformLocation(mBlurProgram, "u_texmatrix");
   1925    mBlurProgramTexBounds = GetUniformLocation(mBlurProgram, "u_texbounds");
   1926    mBlurProgramOffsetScale = GetUniformLocation(mBlurProgram, "u_offsetscale");
   1927    mBlurProgramSigma = GetUniformLocation(mBlurProgram, "u_sigma");
   1928    mBlurProgramColor = GetUniformLocation(mBlurProgram, "u_color");
   1929    mBlurProgramSwizzle = GetUniformLocation(mBlurProgram, "u_swizzle");
   1930    mBlurProgramSampler = GetUniformLocation(mBlurProgram, "u_sampler");
   1931    mBlurProgramClipMask = GetUniformLocation(mBlurProgram, "u_clipmask");
   1932    mBlurProgramClipBounds = GetUniformLocation(mBlurProgram, "u_clipbounds");
   1933    if (!mBlurProgramViewport || !mBlurProgramTransform ||
   1934        !mBlurProgramTexMatrix || !mBlurProgramTexBounds ||
   1935        !mBlurProgramOffsetScale || !mBlurProgramSigma || !mBlurProgramColor ||
   1936        !mBlurProgramSwizzle || !mBlurProgramSampler || !mBlurProgramClipMask ||
   1937        !mBlurProgramClipBounds) {
   1938      return false;
   1939    }
   1940    mWebgl->UseProgram(mBlurProgram);
   1941    UniformData(LOCAL_GL_INT, mBlurProgramSampler, Array<int32_t, 1>{0});
   1942    UniformData(LOCAL_GL_INT, mBlurProgramClipMask, Array<int32_t, 1>{1});
   1943  }
   1944  if (!mFilterProgram) {
   1945    auto vsSource =
   1946        "uniform vec2 u_viewport;\n"
   1947        "uniform vec4 u_clipbounds;\n"
   1948        "uniform vec4 u_transform;\n"
   1949        "uniform vec4 u_texmatrix;\n"
   1950        "attribute vec3 a_vertex;\n"
   1951        "varying vec2 v_cliptc;\n"
   1952        "varying vec2 v_texcoord;\n"
   1953        "varying vec4 v_clipdist;\n"
   1954        "void main() {\n"
   1955        "  vec2 vertex = u_transform.xy * a_vertex.xy + u_transform.zw;\n"
   1956        "  gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
   1957        "  v_cliptc = vertex / u_viewport;\n"
   1958        "  v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
   1959        "                    u_clipbounds.zw - vertex);\n"
   1960        "  v_texcoord = u_texmatrix.xy * a_vertex.xy + u_texmatrix.zw;\n"
   1961        "}\n";
   1962    auto fsSource =
   1963        "precision mediump float;\n"
   1964        "uniform vec4 u_texbounds;\n"
   1965        "uniform mat4 u_colormatrix;\n"
   1966        "uniform vec4 u_coloroffset;\n"
   1967        "uniform sampler2D u_sampler;\n"
   1968        "uniform sampler2D u_clipmask;\n"
   1969        "varying highp vec2 v_cliptc;\n"
   1970        "varying highp vec2 v_texcoord;\n"
   1971        "varying vec4 v_clipdist;\n"
   1972        "bool check_bounds(vec2 tc) {\n"
   1973        "  return all(greaterThanEqual(\n"
   1974        "             vec4(tc, u_texbounds.zw), vec4(u_texbounds.xy, tc)));\n"
   1975        "}\n"
   1976        "void main() {\n"
   1977        "  vec4 color = check_bounds(v_texcoord) ?\n"
   1978        "      texture2D(u_sampler, v_texcoord) : vec4(0.0);\n"
   1979        "  if (color.a != 0.0) color.rgb /= color.a;\n"
   1980        "  color = clamp(u_colormatrix * color + u_coloroffset, 0.0, 1.0);\n"
   1981        "  color.rgb *= color.a;\n"
   1982        "  float clip = texture2D(u_clipmask, v_cliptc).r;\n"
   1983        "  vec2 dist = min(v_clipdist.xy, v_clipdist.zw);\n"
   1984        "  float aa = clamp(min(dist.x, dist.y), 0.0, 1.0);\n"
   1985        "  gl_FragColor = clip * aa * color;\n"
   1986        "}\n";
   1987    RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER);
   1988    mWebgl->ShaderSource(*vsId, vsSource);
   1989    mWebgl->CompileShader(*vsId);
   1990    if (!mWebgl->GetCompileResult(*vsId).success) {
   1991      return false;
   1992    }
   1993    RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER);
   1994    mWebgl->ShaderSource(*fsId, fsSource);
   1995    mWebgl->CompileShader(*fsId);
   1996    if (!mWebgl->GetCompileResult(*fsId).success) {
   1997      return false;
   1998    }
   1999    mFilterProgram = mWebgl->CreateProgram();
   2000    mWebgl->AttachShader(*mFilterProgram, *vsId);
   2001    mWebgl->AttachShader(*mFilterProgram, *fsId);
   2002    mWebgl->BindAttribLocation(*mFilterProgram, 0, "a_vertex");
   2003    mWebgl->LinkProgram(*mFilterProgram);
   2004    if (!mWebgl->GetLinkResult(*mFilterProgram).success) {
   2005      return false;
   2006    }
   2007    mFilterProgramViewport = GetUniformLocation(mFilterProgram, "u_viewport");
   2008    mFilterProgramTransform = GetUniformLocation(mFilterProgram, "u_transform");
   2009    mFilterProgramTexMatrix = GetUniformLocation(mFilterProgram, "u_texmatrix");
   2010    mFilterProgramTexBounds = GetUniformLocation(mFilterProgram, "u_texbounds");
   2011    mFilterProgramColorMatrix =
   2012        GetUniformLocation(mFilterProgram, "u_colormatrix");
   2013    mFilterProgramColorOffset =
   2014        GetUniformLocation(mFilterProgram, "u_coloroffset");
   2015    mFilterProgramSampler = GetUniformLocation(mFilterProgram, "u_sampler");
   2016    mFilterProgramClipMask = GetUniformLocation(mFilterProgram, "u_clipmask");
   2017    mFilterProgramClipBounds =
   2018        GetUniformLocation(mFilterProgram, "u_clipbounds");
   2019    if (!mFilterProgramViewport || !mFilterProgramTransform ||
   2020        !mFilterProgramTexMatrix || !mFilterProgramTexBounds ||
   2021        !mFilterProgramColorMatrix || !mFilterProgramColorOffset ||
   2022        !mFilterProgramSampler || !mFilterProgramClipMask ||
   2023        !mFilterProgramClipBounds) {
   2024      return false;
   2025    }
   2026    mWebgl->UseProgram(mFilterProgram);
   2027    UniformData(LOCAL_GL_INT, mFilterProgramSampler, Array<int32_t, 1>{0});
   2028    UniformData(LOCAL_GL_INT, mFilterProgramClipMask, Array<int32_t, 1>{1});
   2029  }
   2030  return true;
   2031 }
   2032 
   2033 void SharedContextWebgl::EnableScissor(const IntRect& aRect, bool aForce) {
   2034  // Only update scissor state if it actually changes.
   2035  IntRect rect = !aForce && mTargetHandle
   2036                     ? aRect + mTargetHandle->GetBounds().TopLeft()
   2037                     : aRect;
   2038  if (!mLastScissor.IsEqualEdges(rect)) {
   2039    mLastScissor = rect;
   2040    mWebgl->Scissor(rect.x, rect.y, rect.width, rect.height);
   2041  }
   2042  if (!mScissorEnabled) {
   2043    mScissorEnabled = true;
   2044    mWebgl->SetEnabled(LOCAL_GL_SCISSOR_TEST, {}, true);
   2045  }
   2046 }
   2047 
   2048 void SharedContextWebgl::DisableScissor(bool aForce) {
   2049  if (!aForce && mTargetHandle) {
   2050    EnableScissor(IntRect(IntPoint(), mViewportSize));
   2051    return;
   2052  }
   2053  if (mScissorEnabled) {
   2054    mScissorEnabled = false;
   2055    mWebgl->SetEnabled(LOCAL_GL_SCISSOR_TEST, {}, false);
   2056  }
   2057 }
   2058 
   2059 inline ColorPattern DrawTargetWebgl::GetClearPattern() const {
   2060  return ColorPattern(
   2061      DeviceColor(0.0f, 0.0f, 0.0f, IsOpaque(mFormat) ? 1.0f : 0.0f));
   2062 }
   2063 
   2064 template <typename R>
   2065 inline RectDouble DrawTargetWebgl::TransformDouble(const R& aRect) const {
   2066  return MatrixDouble(mTransform).TransformBounds(WidenToDouble(aRect));
   2067 }
   2068 
   2069 // Check if the transformed rect clips to the viewport.
   2070 inline Maybe<Rect> DrawTargetWebgl::RectClippedToViewport(
   2071    const RectDouble& aRect) const {
   2072  if (!mTransform.PreservesAxisAlignedRectangles()) {
   2073    return Nothing();
   2074  }
   2075 
   2076  return Some(NarrowToFloat(aRect.SafeIntersect(RectDouble(GetRect()))));
   2077 }
   2078 
   2079 // Ensure that the rect, after transform, is within reasonable precision limits
   2080 // such that when transformed and clipped in the shader it will not round bits
   2081 // from the mantissa in a way that will diverge in a noticeable way from path
   2082 // geometry calculated by the path fallback.
   2083 template <typename R>
   2084 static inline bool RectInsidePrecisionLimits(const R& aRect) {
   2085  return R(-(1 << 20), -(1 << 20), 2 << 20, 2 << 20).Contains(aRect);
   2086 }
   2087 
   2088 void DrawTargetWebgl::ClearRect(const Rect& aRect) {
   2089  if (mIsClear) {
   2090    // No need to clear anything if the entire framebuffer is already clear.
   2091    return;
   2092  }
   2093 
   2094  RectDouble xformRect = TransformDouble(aRect);
   2095  bool containsViewport = false;
   2096  if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
   2097    // If the rect clips to viewport, just clear the clipped rect
   2098    // to avoid transform issues.
   2099    containsViewport = clipped->Size() == Size(GetSize());
   2100    DrawRect(*clipped, GetClearPattern(),
   2101             DrawOptions(1.0f, CompositionOp::OP_CLEAR), Nothing(), nullptr,
   2102             false);
   2103  } else if (RectInsidePrecisionLimits(xformRect)) {
   2104    // If the rect transform won't stress precision, then just use it.
   2105    DrawRect(aRect, GetClearPattern(),
   2106             DrawOptions(1.0f, CompositionOp::OP_CLEAR));
   2107  } else {
   2108    // Otherwise, using the transform in the shader may lead to inaccuracies, so
   2109    // just fall back.
   2110    MarkSkiaChanged();
   2111    mSkia->ClearRect(aRect);
   2112  }
   2113 
   2114  // If the clear rectangle encompasses the entire viewport and is not clipped,
   2115  // then mark the target as entirely clear.
   2116  if (containsViewport && mSharedContext->IsCurrentTarget(this) &&
   2117      !mSharedContext->HasClipMask() &&
   2118      mSharedContext->mClipAARect.Contains(Rect(GetRect()))) {
   2119    mIsClear = true;
   2120  }
   2121 }
   2122 
   2123 static inline DeviceColor PremultiplyColor(const DeviceColor& aColor,
   2124                                           float aAlpha = 1.0f) {
   2125  float a = aColor.a * aAlpha;
   2126  return DeviceColor(aColor.r * a, aColor.g * a, aColor.b * a, a);
   2127 }
   2128 
   2129 // Attempts to create the framebuffer used for drawing and also any relevant
   2130 // non-shared resources. Returns whether or not this succeeded.
   2131 bool DrawTargetWebgl::CreateFramebuffer() {
   2132  RefPtr<WebGLContext> webgl = mSharedContext->mWebgl;
   2133  if (!mFramebuffer) {
   2134    mFramebuffer = webgl->CreateFramebuffer();
   2135  }
   2136  if (!mTex) {
   2137    mTex = webgl->CreateTexture();
   2138    mSharedContext->BindAndInitRenderTex(mTex, SurfaceFormat::B8G8R8A8, mSize);
   2139    webgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mFramebuffer);
   2140    webgl::FbAttachInfo attachInfo;
   2141    attachInfo.tex = mTex;
   2142    webgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
   2143                             LOCAL_GL_TEXTURE_2D, attachInfo);
   2144    webgl->Viewport(0, 0, mSize.width, mSize.height);
   2145    mSharedContext->DisableScissor();
   2146    DeviceColor color = PremultiplyColor(GetClearPattern().mColor);
   2147    webgl->ClearColor(color.b, color.g, color.r, color.a);
   2148    webgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
   2149    mSharedContext->ClearTarget();
   2150    mSharedContext->AddUntrackedTextureMemory(mTex);
   2151  }
   2152  return true;
   2153 }
   2154 
   2155 void DrawTargetWebgl::CopySurface(SourceSurface* aSurface,
   2156                                  const IntRect& aSourceRect,
   2157                                  const IntPoint& aDestination) {
   2158  // Intersect the source and destination rectangles with the viewport bounds.
   2159  IntRect destRect =
   2160      IntRect(aDestination, aSourceRect.Size()).SafeIntersect(GetRect());
   2161  IntRect srcRect = destRect - aDestination + aSourceRect.TopLeft();
   2162  if (srcRect.IsEmpty()) {
   2163    return;
   2164  }
   2165 
   2166  if (mSkiaValid) {
   2167    if (mSkiaLayer) {
   2168      if (destRect.Contains(GetRect())) {
   2169        // If the the destination would override the entire layer, discard the
   2170        // layer.
   2171        mSkiaLayer = false;
   2172      } else if (!IsOpaque(aSurface->GetFormat())) {
   2173        // If the surface is not opaque, copying it into the layer results in
   2174        // unintended blending rather than a copy to the destination.
   2175        FlattenSkia();
   2176      }
   2177    } else {
   2178      // If there is no layer, copying is safe.
   2179      MarkSkiaChanged();
   2180    }
   2181    mSkia->CopySurface(aSurface, srcRect, destRect.TopLeft());
   2182    return;
   2183  }
   2184 
   2185  IntRect samplingRect;
   2186  if (!mSharedContext->IsCompatibleSurface(aSurface)) {
   2187    // If this data surface completely overwrites the framebuffer, then just
   2188    // copy it to the Skia target.
   2189    if (destRect.Contains(GetRect())) {
   2190      MarkSkiaChanged(true);
   2191      mSkia->DetachAllSnapshots();
   2192      mSkiaNoClip->CopySurface(aSurface, srcRect, destRect.TopLeft());
   2193      return;
   2194    }
   2195 
   2196    // CopySurface usually only samples a surface once, so don't cache the
   2197    // entire surface as it is unlikely to be reused. Limit it to the used
   2198    // source rectangle instead.
   2199    IntRect surfaceRect = aSurface->GetRect();
   2200    if (!srcRect.IsEqualEdges(surfaceRect)) {
   2201      samplingRect = srcRect.SafeIntersect(surfaceRect) - surfaceRect.TopLeft();
   2202    }
   2203  }
   2204 
   2205  Matrix matrix = Matrix::Translation(destRect.TopLeft() - srcRect.TopLeft());
   2206  SurfacePattern pattern(aSurface, ExtendMode::CLAMP, matrix,
   2207                         SamplingFilter::POINT, samplingRect);
   2208  DrawRect(Rect(destRect), pattern, DrawOptions(1.0f, CompositionOp::OP_SOURCE),
   2209           Nothing(), nullptr, false, false);
   2210 }
   2211 
   2212 void DrawTargetWebgl::PushClip(const Path* aPath) {
   2213  if (aPath && aPath->GetBackendType() == BackendType::SKIA) {
   2214    // Detect if the path is really just a rect to simplify caching.
   2215    if (Maybe<Rect> rect = aPath->AsRect()) {
   2216      PushClipRect(*rect);
   2217      return;
   2218    }
   2219  }
   2220 
   2221  mClipChanged = true;
   2222  mRefreshClipState = true;
   2223  mSkia->PushClip(aPath);
   2224 
   2225  mClipStack.push_back({GetTransform(), Rect(), aPath});
   2226 }
   2227 
   2228 void DrawTargetWebgl::PushClipRect(const Rect& aRect) {
   2229  mClipChanged = true;
   2230  mRefreshClipState = true;
   2231  mSkia->PushClipRect(aRect);
   2232 
   2233  mClipStack.push_back({GetTransform(), aRect, nullptr});
   2234 }
   2235 
   2236 void DrawTargetWebgl::PushDeviceSpaceClipRects(const IntRect* aRects,
   2237                                               uint32_t aCount) {
   2238  mClipChanged = true;
   2239  mRefreshClipState = true;
   2240  mSkia->PushDeviceSpaceClipRects(aRects, aCount);
   2241 
   2242  for (uint32_t i = 0; i < aCount; i++) {
   2243    mClipStack.push_back({Matrix(), Rect(aRects[i]), nullptr});
   2244  }
   2245 }
   2246 
   2247 void DrawTargetWebgl::PopClip() {
   2248  if (mClipStack.empty()) {
   2249    return;
   2250  }
   2251 
   2252  mClipChanged = true;
   2253  mRefreshClipState = true;
   2254  mSkia->PopClip();
   2255 
   2256  mClipStack.pop_back();
   2257 }
   2258 
   2259 bool DrawTargetWebgl::RemoveAllClips() {
   2260  if (mClipStack.empty()) {
   2261    return true;
   2262  }
   2263  if (!mSkia->RemoveAllClips()) {
   2264    return false;
   2265  }
   2266  mClipChanged = true;
   2267  mRefreshClipState = true;
   2268  mClipStack.clear();
   2269  return true;
   2270 }
   2271 
   2272 bool DrawTargetWebgl::CopyToFallback(DrawTarget* aDT) {
   2273  aDT->RemoveAllClips();
   2274  for (auto& clipStack : mClipStack) {
   2275    aDT->SetTransform(clipStack.mTransform);
   2276    if (clipStack.mPath) {
   2277      aDT->PushClip(clipStack.mPath);
   2278    } else {
   2279      aDT->PushClipRect(clipStack.mRect);
   2280    }
   2281  }
   2282  aDT->SetTransform(GetTransform());
   2283 
   2284  // An existing data snapshot is required for fallback, as we have to avoid
   2285  // trying to touch the WebGL context, which is assumed to be invalid and not
   2286  // suitable for readback.
   2287  if (HasDataSnapshot()) {
   2288    if (RefPtr<SourceSurface> snapshot = Snapshot()) {
   2289      aDT->CopySurface(snapshot, snapshot->GetRect(), gfx::IntPoint(0, 0));
   2290      return true;
   2291    }
   2292  }
   2293  return false;
   2294 }
   2295 
   2296 enum class SupportsDrawOptionsStatus { No, UnboundedBlend, Yes };
   2297 
   2298 // Whether a given composition operator can be mapped to a WebGL blend mode.
   2299 static inline SupportsDrawOptionsStatus SupportsDrawOptions(
   2300    const DrawOptions& aOptions) {
   2301  switch (aOptions.mCompositionOp) {
   2302    case CompositionOp::OP_OVER:
   2303    case CompositionOp::OP_DEST_OVER:
   2304    case CompositionOp::OP_ADD:
   2305    case CompositionOp::OP_DEST_OUT:
   2306    case CompositionOp::OP_ATOP:
   2307    case CompositionOp::OP_SOURCE:
   2308    case CompositionOp::OP_CLEAR:
   2309    case CompositionOp::OP_MULTIPLY:
   2310    case CompositionOp::OP_SCREEN:
   2311      return SupportsDrawOptionsStatus::Yes;
   2312    case CompositionOp::OP_IN:
   2313    case CompositionOp::OP_OUT:
   2314    case CompositionOp::OP_DEST_IN:
   2315    case CompositionOp::OP_DEST_ATOP:
   2316      return SupportsDrawOptionsStatus::UnboundedBlend;
   2317    default:
   2318      return SupportsDrawOptionsStatus::No;
   2319  }
   2320 }
   2321 
   2322 bool DrawTargetWebgl::SupportsDrawOptions(const DrawOptions& aOptions,
   2323                                          const Rect& aRect) {
   2324  switch (mozilla::gfx::SupportsDrawOptions(aOptions)) {
   2325    case SupportsDrawOptionsStatus::Yes:
   2326      return true;
   2327    case SupportsDrawOptionsStatus::UnboundedBlend:
   2328      if (aRect.IsEmpty()) {
   2329        return false;
   2330      }
   2331      if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false)) {
   2332        if (!clip->IsEmpty() && clip->Contains(GetRect())) {
   2333          clip = Some(GetRect());
   2334        }
   2335        Rect clipF(*clip);
   2336        if (aRect.Contains(clipF) || aRect.WithinEpsilonOf(clipF, 1e-3f)) {
   2337          return true;
   2338        }
   2339        return false;
   2340      }
   2341      return false;
   2342    default:
   2343      return false;
   2344  }
   2345 }
   2346 
   2347 // Whether the composition operator requires multiple blend stages to
   2348 // approximate with WebGL blend modes.
   2349 inline uint8_t SharedContextWebgl::RequiresMultiStageBlend(
   2350    const DrawOptions& aOptions, DrawTargetWebgl* aDT) {
   2351  switch (aOptions.mCompositionOp) {
   2352    case CompositionOp::OP_MULTIPLY:
   2353      return !IsOpaque(aDT ? aDT->GetFormat()
   2354                           : (mTargetHandle ? mTargetHandle->GetFormat()
   2355                                            : mCurrentTarget->GetFormat()))
   2356                 ? 2
   2357                 : 0;
   2358    default:
   2359      return 0;
   2360  }
   2361 }
   2362 
   2363 static inline bool SupportsExtendMode(const SurfacePattern& aPattern) {
   2364  switch (aPattern.mExtendMode) {
   2365    case ExtendMode::CLAMP:
   2366      return true;
   2367    case ExtendMode::REPEAT:
   2368    case ExtendMode::REPEAT_X:
   2369    case ExtendMode::REPEAT_Y:
   2370      if ((!aPattern.mSurface ||
   2371           aPattern.mSurface->GetUnderlyingType() == SurfaceType::WEBGL) &&
   2372          !aPattern.mSamplingRect.IsEmpty()) {
   2373        return false;
   2374      }
   2375      return true;
   2376    default:
   2377      return false;
   2378  }
   2379 }
   2380 
   2381 // Whether a pattern can be mapped to an available WebGL shader.
   2382 bool SharedContextWebgl::SupportsPattern(const Pattern& aPattern) {
   2383  switch (aPattern.GetType()) {
   2384    case PatternType::COLOR:
   2385      return true;
   2386    case PatternType::SURFACE: {
   2387      auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
   2388      if (!SupportsExtendMode(surfacePattern)) {
   2389        return false;
   2390      }
   2391      if (surfacePattern.mSurface) {
   2392        // If the surface is already uploaded to a texture, then just use it.
   2393        if (IsCompatibleSurface(surfacePattern.mSurface)) {
   2394          return true;
   2395        }
   2396 
   2397        IntSize size = surfacePattern.mSurface->GetSize();
   2398        // The maximum size a surface can be before triggering a fallback to
   2399        // software. Bound the maximum surface size by the actual texture size
   2400        // limit.
   2401        int32_t maxSize = int32_t(
   2402            std::min(StaticPrefs::gfx_canvas_accelerated_max_surface_size(),
   2403                     mMaxTextureSize));
   2404        // Check if either of the surface dimensions or the sampling rect,
   2405        // if supplied, exceed the maximum.
   2406        if (std::max(size.width, size.height) > maxSize &&
   2407            (surfacePattern.mSamplingRect.IsEmpty() ||
   2408             std::max(surfacePattern.mSamplingRect.width,
   2409                      surfacePattern.mSamplingRect.height) > maxSize)) {
   2410          return false;
   2411        }
   2412      }
   2413      return true;
   2414    }
   2415    default:
   2416      // Patterns other than colors and surfaces are currently not accelerated.
   2417      return false;
   2418  }
   2419 }
   2420 
   2421 bool DrawTargetWebgl::DrawRect(const Rect& aRect, const Pattern& aPattern,
   2422                               const DrawOptions& aOptions,
   2423                               Maybe<DeviceColor> aMaskColor,
   2424                               RefPtr<TextureHandle>* aHandle,
   2425                               bool aTransformed, bool aClipped,
   2426                               bool aAccelOnly, bool aForceUpdate,
   2427                               const StrokeOptions* aStrokeOptions) {
   2428  // If there is nothing to draw, then don't draw...
   2429  if (aRect.IsEmpty() || mSkia->IsClipEmpty()) {
   2430    return true;
   2431  }
   2432 
   2433  // If we're already drawing directly to the WebGL context, then we want to
   2434  // continue to do so. However, if we're drawing into a Skia layer over the
   2435  // WebGL context, then we need to be careful to avoid repeatedly clearing
   2436  // and flushing the layer if we hit a drawing request that can be accelerated
   2437  // in between layered drawing requests, as clearing and flushing the layer
   2438  // can be significantly expensive when repeated. So when a Skia layer is
   2439  // active, if it is possible to continue drawing into the layer, then don't
   2440  // accelerate the drawing request.
   2441  if (mWebglValid || (mSkiaLayer && !mLayerDepth &&
   2442                      (aAccelOnly || !SupportsLayering(aOptions)))) {
   2443    // If we get here, either the WebGL context is being directly drawn to
   2444    // or we are going to flush the Skia layer to it before doing so. The shared
   2445    // context still needs to be claimed and prepared for drawing. If this
   2446    // fails, we just fall back to drawing with Skia below.
   2447    if (SupportsDrawOptions(aOptions, aRect) && PrepareContext(aClipped)) {
   2448      // The shared context is claimed and the framebuffer is now valid, so try
   2449      // accelerated drawing.
   2450      return mSharedContext->DrawRectAccel(
   2451          aRect, aPattern, aOptions, aMaskColor, aHandle, aTransformed,
   2452          aClipped, aAccelOnly, aForceUpdate, aStrokeOptions);
   2453    }
   2454  }
   2455 
   2456  // Either there is no valid WebGL target to draw into, or we failed to prepare
   2457  // it for drawing. The only thing we can do at this point is fall back to
   2458  // drawing with Skia. If the request explicitly requires accelerated drawing,
   2459  // then draw nothing before returning failure.
   2460  if (!aAccelOnly) {
   2461    DrawRectFallback(aRect, aPattern, aOptions, aMaskColor, aTransformed,
   2462                     aClipped, aStrokeOptions);
   2463  }
   2464  return false;
   2465 }
   2466 
   2467 void DrawTargetWebgl::DrawRectFallback(const Rect& aRect,
   2468                                       const Pattern& aPattern,
   2469                                       const DrawOptions& aOptions,
   2470                                       Maybe<DeviceColor> aMaskColor,
   2471                                       bool aTransformed, bool aClipped,
   2472                                       const StrokeOptions* aStrokeOptions) {
   2473  // Invalidate the WebGL target and prepare the Skia target for drawing.
   2474  MarkSkiaChanged(aOptions);
   2475 
   2476  if (aTransformed) {
   2477    // If transforms are requested, then just translate back to FillRect.
   2478    if (aMaskColor) {
   2479      mSkia->Mask(ColorPattern(*aMaskColor), aPattern, aOptions);
   2480    } else if (aStrokeOptions) {
   2481      mSkia->StrokeRect(aRect, aPattern, *aStrokeOptions, aOptions);
   2482    } else {
   2483      mSkia->FillRect(aRect, aPattern, aOptions);
   2484    }
   2485  } else if (aClipped) {
   2486    // If no transform was requested but clipping is still required, then
   2487    // temporarily reset the transform before translating to FillRect.
   2488    mSkia->SetTransform(Matrix());
   2489    if (aMaskColor) {
   2490      auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
   2491      if (surfacePattern.mSamplingRect.IsEmpty()) {
   2492        mSkia->MaskSurface(ColorPattern(*aMaskColor), surfacePattern.mSurface,
   2493                           aRect.TopLeft(), aOptions);
   2494      } else {
   2495        mSkia->Mask(ColorPattern(*aMaskColor), aPattern, aOptions);
   2496      }
   2497    } else if (aStrokeOptions) {
   2498      mSkia->StrokeRect(aRect, aPattern, *aStrokeOptions, aOptions);
   2499    } else {
   2500      mSkia->FillRect(aRect, aPattern, aOptions);
   2501    }
   2502    mSkia->SetTransform(mTransform);
   2503  } else if (aPattern.GetType() == PatternType::SURFACE) {
   2504    // No transform nor clipping was requested, so it is essentially just a
   2505    // copy.
   2506    auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
   2507    IntRect destRect = RoundedOut(aRect);
   2508    IntRect srcRect =
   2509        destRect - IntPoint::Round(surfacePattern.mMatrix.GetTranslation());
   2510    mSkia->CopySurface(surfacePattern.mSurface, srcRect, destRect.TopLeft());
   2511  } else {
   2512    MOZ_ASSERT(false);
   2513  }
   2514 }
   2515 
   2516 inline already_AddRefed<WebGLTexture> SharedContextWebgl::GetCompatibleSnapshot(
   2517    SourceSurface* aSurface, RefPtr<TextureHandle>* aHandle,
   2518    bool aCheckTarget) const {
   2519  if (aSurface->GetUnderlyingType() == SurfaceType::WEBGL) {
   2520    RefPtr<SourceSurfaceWebgl> webglSurf =
   2521        aSurface->GetUnderlyingSurface().downcast<SourceSurfaceWebgl>();
   2522    if (this == webglSurf->mSharedContext) {
   2523      // If there is a snapshot copy in a texture handle, use that.
   2524      if (webglSurf->mHandle) {
   2525        if (aHandle) {
   2526          *aHandle = webglSurf->mHandle;
   2527        }
   2528        return do_AddRef(
   2529            webglSurf->mHandle->GetBackingTexture()->GetWebGLTexture());
   2530      }
   2531      if (RefPtr<DrawTargetWebgl> webglDT = webglSurf->GetTarget()) {
   2532        // If there is a copy-on-write reference to a target, use its backing
   2533        // texture directly. This is only safe if the targets don't match, but
   2534        // MarkChanged should ensure that any snapshots were copied into a
   2535        // texture handle before we ever get here.
   2536        if (!aCheckTarget || !IsCurrentTarget(webglDT)) {
   2537          return do_AddRef(webglDT->mTex);
   2538        }
   2539      }
   2540    }
   2541  }
   2542  return nullptr;
   2543 }
   2544 
   2545 inline bool SharedContextWebgl::IsCompatibleSurface(
   2546    SourceSurface* aSurface) const {
   2547  return bool(RefPtr<WebGLTexture>(GetCompatibleSnapshot(aSurface)));
   2548 }
   2549 
   2550 bool SharedContextWebgl::UploadSurface(DataSourceSurface* aData,
   2551                                       SurfaceFormat aFormat,
   2552                                       const IntRect& aSrcRect,
   2553                                       const IntPoint& aDstOffset, bool aInit,
   2554                                       bool aZero,
   2555                                       const RefPtr<WebGLTexture>& aTex) {
   2556  webgl::TexUnpackBlobDesc texDesc = {LOCAL_GL_TEXTURE_2D};
   2557  IntRect srcRect(aSrcRect);
   2558  IntPoint dstOffset(aDstOffset);
   2559  if (srcRect.IsEmpty()) {
   2560    return true;
   2561  }
   2562  if (aData) {
   2563    // If the source rect could not possibly overlap the surface, then it is
   2564    // effectively empty with nothing to upload.
   2565    srcRect = srcRect.SafeIntersect(IntRect(IntPoint(0, 0), aData->GetSize()));
   2566    if (srcRect.IsEmpty()) {
   2567      return true;
   2568    }
   2569    // If there is a non-empty rect remaining, then ensure the dest offset
   2570    // reflects the change in source rect.
   2571    dstOffset += srcRect.TopLeft() - aSrcRect.TopLeft();
   2572 
   2573    // Ensure source data matches the expected format size.
   2574    int32_t bpp = BytesPerPixel(aFormat);
   2575    if (bpp != BytesPerPixel(aData->GetFormat())) {
   2576      return false;
   2577    }
   2578 
   2579    // The surface needs to be uploaded to its backing texture either to
   2580    // initialize or update the texture handle contents. Map the data
   2581    // contents of the surface so it can be read.
   2582    DataSourceSurface::ScopedMap map(aData, DataSourceSurface::READ);
   2583    if (!map.IsMapped()) {
   2584      return false;
   2585    }
   2586    int32_t stride = map.GetStride();
   2587    // Get the data pointer range considering the sampling rect offset and
   2588    // size.
   2589    Span<const uint8_t> range(
   2590        map.GetData() + srcRect.y * size_t(stride) + srcRect.x * bpp,
   2591        std::max(srcRect.height - 1, 0) * size_t(stride) + srcRect.width * bpp);
   2592    texDesc.cpuData = Some(range);
   2593    // If the stride happens to be 4 byte aligned, assume that is the
   2594    // desired alignment regardless of format (even A8). Otherwise, we
   2595    // default to byte alignment.
   2596    texDesc.unpacking.alignmentInTypeElems = stride % 4 ? 1 : 4;
   2597    texDesc.unpacking.rowLength = stride / bpp;
   2598  } else if (aZero) {
   2599    // Create a PBO filled with zero data to initialize the texture data and
   2600    // avoid slow initialization inside WebGL.
   2601    if (srcRect.TopLeft() != IntPoint(0, 0)) {
   2602      MOZ_ASSERT_UNREACHABLE("Invalid origin for texture initialization.");
   2603      return false;
   2604    }
   2605    int32_t stride = GetAlignedStride<4>(srcRect.width, BytesPerPixel(aFormat));
   2606    if (stride <= 0) {
   2607      MOZ_ASSERT_UNREACHABLE("Invalid stride for texture initialization.");
   2608      return false;
   2609    }
   2610    size_t size = size_t(stride) * srcRect.height;
   2611    if (!mZeroBuffer || size > mZeroSize) {
   2612      ClearZeroBuffer();
   2613      mZeroBuffer = mWebgl->CreateBuffer();
   2614      mZeroSize = size;
   2615      mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mZeroBuffer);
   2616      // WebGL will zero initialize the empty buffer, so we don't send zero data
   2617      // explicitly.
   2618      mWebgl->BufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER, size, nullptr,
   2619                         LOCAL_GL_STATIC_DRAW);
   2620      AddUntrackedTextureMemory(mZeroBuffer);
   2621    } else {
   2622      mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mZeroBuffer);
   2623    }
   2624    texDesc.pboOffset = Some(0);
   2625  }
   2626  texDesc.size = uvec3(uint32_t(srcRect.width), uint32_t(srcRect.height), 1);
   2627  // Upload as RGBA8 to avoid swizzling during upload. Surfaces provide
   2628  // data as BGRA, but we manually swizzle that in the shader. An A8
   2629  // surface will be stored as an R8 texture that will also be swizzled
   2630  // in the shader.
   2631  GLenum intFormat =
   2632      aFormat == SurfaceFormat::A8 ? LOCAL_GL_R8 : LOCAL_GL_RGBA8;
   2633  GLenum extFormat =
   2634      aFormat == SurfaceFormat::A8 ? LOCAL_GL_RED : LOCAL_GL_RGBA;
   2635  webgl::PackingInfo texPI = {extFormat, LOCAL_GL_UNSIGNED_BYTE};
   2636  // Do the (partial) upload for the shared or standalone texture.
   2637  if (aTex) {
   2638    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, aTex);
   2639  }
   2640  mWebgl->TexImage(0, aInit ? intFormat : 0,
   2641                   {uint32_t(dstOffset.x), uint32_t(dstOffset.y), 0}, texPI,
   2642                   texDesc);
   2643  if (aTex) {
   2644    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, mLastTexture);
   2645  }
   2646  if (!aData && aZero) {
   2647    mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
   2648  }
   2649  return true;
   2650 }
   2651 
   2652 void SharedContextWebgl::UploadSurfaceToHandle(
   2653    const RefPtr<DataSourceSurface>& aData, const IntPoint& aSrcOffset,
   2654    const RefPtr<TextureHandle>& aHandle) {
   2655  BackingTexture* backing = aHandle->GetBackingTexture();
   2656  RefPtr<WebGLTexture> tex = backing->GetWebGLTexture();
   2657  if (mLastTexture != tex) {
   2658    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
   2659    mLastTexture = tex;
   2660  }
   2661  if (!backing->IsInitialized()) {
   2662    backing->MarkInitialized();
   2663    InitTexParameters(tex);
   2664    if (aHandle->GetSize() != backing->GetSize()) {
   2665      UploadSurface(nullptr, backing->GetFormat(),
   2666                    IntRect(IntPoint(), backing->GetSize()), IntPoint(), true,
   2667                    true);
   2668    }
   2669  }
   2670  UploadSurface(
   2671      aData, aHandle->GetFormat(), IntRect(aSrcOffset, aHandle->GetSize()),
   2672      aHandle->GetBounds().TopLeft(), aHandle->GetSize() == backing->GetSize());
   2673  // Signal that we had to upload new data to the texture cache.
   2674  mCurrentTarget->mProfile.OnCacheMiss();
   2675 }
   2676 
   2677 void SharedContextWebgl::BindAndInitRenderTex(const RefPtr<WebGLTexture>& aTex,
   2678                                              SurfaceFormat aFormat,
   2679                                              const IntSize& aSize) {
   2680  mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, aTex);
   2681  mWebgl->TexStorage(
   2682      LOCAL_GL_TEXTURE_2D, 1,
   2683      aFormat == SurfaceFormat::A8 ? LOCAL_GL_R8 : LOCAL_GL_RGBA8,
   2684      {uint32_t(aSize.width), uint32_t(aSize.height), 1});
   2685  InitTexParameters(aTex);
   2686  ClearLastTexture();
   2687 }
   2688 
   2689 void SharedContextWebgl::InitRenderTex(BackingTexture* aBacking) {
   2690  // Initialize the backing texture if necessary.
   2691  if (!aBacking->IsInitialized()) {
   2692    // If the backing texture is uninitialized, it needs its sampling parameters
   2693    // set for later use.
   2694    BindAndInitRenderTex(aBacking->GetWebGLTexture(), aBacking->GetFormat(),
   2695                         aBacking->GetSize());
   2696  }
   2697 }
   2698 
   2699 void SharedContextWebgl::ClearRenderTex(BackingTexture* aBacking) {
   2700  if (!aBacking->IsInitialized()) {
   2701    aBacking->MarkInitialized();
   2702    // WebGL implicitly clears the backing texture the first time it is used.
   2703  } else {
   2704    // Ensure the mask background is clear.
   2705    mWebgl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   2706    mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
   2707  }
   2708 }
   2709 
   2710 void SharedContextWebgl::BindScratchFramebuffer(TextureHandle* aHandle,
   2711                                                bool aInit,
   2712                                                const IntSize& aViewportSize) {
   2713  BackingTexture* backing = aHandle->GetBackingTexture();
   2714  if (aInit) {
   2715    InitRenderTex(backing);
   2716  }
   2717 
   2718  // Set up a scratch framebuffer to render to the appropriate sub-texture of
   2719  // the backing texture.
   2720  if (!mScratchFramebuffer) {
   2721    mScratchFramebuffer = mWebgl->CreateFramebuffer();
   2722  }
   2723  mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mScratchFramebuffer);
   2724  webgl::FbAttachInfo attachInfo;
   2725  attachInfo.tex = backing->GetWebGLTexture();
   2726  mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
   2727                            LOCAL_GL_TEXTURE_2D, attachInfo);
   2728  IntRect bounds = aHandle->GetBounds();
   2729  if (!aViewportSize.IsEmpty()) {
   2730    bounds.SizeTo(Min(bounds.Size(), aViewportSize));
   2731  }
   2732  mWebgl->Viewport(bounds.x, bounds.y, bounds.width, bounds.height);
   2733 
   2734  if (aInit) {
   2735    EnableScissor(bounds, true);
   2736    ClearRenderTex(backing);
   2737  }
   2738 }
   2739 
   2740 // Allocate a new texture handle backed by either a standalone texture or as a
   2741 // sub-texture of a larger shared texture.
   2742 already_AddRefed<TextureHandle> SharedContextWebgl::AllocateTextureHandle(
   2743    SurfaceFormat aFormat, const IntSize& aSize, bool aAllowShared,
   2744    bool aRenderable, const WebGLTexture* aAvoid) {
   2745  RefPtr<TextureHandle> handle;
   2746  // Calculate the bytes that would be used by this texture handle, and prune
   2747  // enough other textures to ensure we have that much usable texture space
   2748  // available to allocate.
   2749  size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
   2750  PruneTextureMemory(usedBytes, false);
   2751  // The requested page size for shared textures.
   2752  int32_t pageSize = int32_t(std::min(
   2753      StaticPrefs::gfx_canvas_accelerated_shared_page_size(), mMaxTextureSize));
   2754  if (aAllowShared && std::max(aSize.width, aSize.height) <= pageSize / 2) {
   2755    // Ensure that the surface is no bigger than a quadrant of a shared texture
   2756    // page. If so, try to allocate it to a shared texture. Look for any
   2757    // existing shared texture page with a matching format and allocate
   2758    // from that if possible.
   2759    for (auto& shared : mSharedTextures) {
   2760      if (shared->GetFormat() == aFormat &&
   2761          shared->IsRenderable() == aRenderable &&
   2762          shared->GetWebGLTexture() != aAvoid) {
   2763        bool wasEmpty = !shared->HasAllocatedHandles();
   2764        handle = shared->Allocate(aSize);
   2765        if (handle) {
   2766          if (wasEmpty) {
   2767            // If the page was previously empty, then deduct it from the
   2768            // empty memory reserves.
   2769            mEmptyTextureMemory -= shared->UsedBytes();
   2770          }
   2771          break;
   2772        }
   2773      }
   2774    }
   2775    // If we couldn't find an existing shared texture page with matching
   2776    // format, then allocate a new page to put the request in.
   2777    if (!handle) {
   2778      if (RefPtr<WebGLTexture> tex = mWebgl->CreateTexture()) {
   2779        RefPtr<SharedTexture> shared =
   2780            new SharedTexture(IntSize(pageSize, pageSize), aFormat, tex);
   2781        if (aRenderable) {
   2782          shared->MarkRenderable();
   2783        }
   2784        mSharedTextures.push_back(shared);
   2785        AddTextureMemory(shared);
   2786        handle = shared->Allocate(aSize);
   2787      }
   2788    }
   2789  } else {
   2790    // The surface wouldn't fit in a shared texture page, so we need to
   2791    // allocate a standalone texture for it instead.
   2792    if (RefPtr<WebGLTexture> tex = mWebgl->CreateTexture()) {
   2793      RefPtr<StandaloneTexture> standalone =
   2794          new StandaloneTexture(aSize, aFormat, tex);
   2795      if (aRenderable) {
   2796        standalone->MarkRenderable();
   2797      }
   2798      mStandaloneTextures.push_back(standalone);
   2799      AddTextureMemory(standalone);
   2800      handle = standalone;
   2801    }
   2802  }
   2803 
   2804  if (!handle) {
   2805    return nullptr;
   2806  }
   2807 
   2808  // Insert the new texture handle into the front of the MRU list and
   2809  // update used space for it.
   2810  mTextureHandles.insertFront(handle);
   2811  ++mNumTextureHandles;
   2812  mUsedTextureMemory += handle->UsedBytes();
   2813 
   2814  return handle.forget();
   2815 }
   2816 
   2817 static inline SamplingFilter GetSamplingFilter(const Pattern& aPattern) {
   2818  return aPattern.GetType() == PatternType::SURFACE
   2819             ? static_cast<const SurfacePattern&>(aPattern).mSamplingFilter
   2820             : SamplingFilter::GOOD;
   2821 }
   2822 
   2823 static inline bool UseNearestFilter(const Pattern& aPattern) {
   2824  return GetSamplingFilter(aPattern) == SamplingFilter::POINT;
   2825 }
   2826 
   2827 // Determine if the rectangle is still axis-aligned and pixel-aligned.
   2828 static inline Maybe<IntRect> IsAlignedRect(bool aTransformed,
   2829                                           const Matrix& aCurrentTransform,
   2830                                           const Rect& aRect) {
   2831  if (!aTransformed || aCurrentTransform.HasOnlyIntegerTranslation()) {
   2832    auto intRect = RoundedToInt(aRect);
   2833    if (aRect.WithinEpsilonOf(Rect(intRect), 1.0e-3f)) {
   2834      if (aTransformed) {
   2835        intRect += RoundedToInt(aCurrentTransform.GetTranslation());
   2836      }
   2837      return Some(intRect);
   2838    }
   2839  }
   2840  return Nothing();
   2841 }
   2842 
   2843 Maybe<uint32_t> SharedContextWebgl::GetUniformLocation(
   2844    const RefPtr<WebGLProgram>& aProg, const std::string& aName) const {
   2845  if (!aProg || !aProg->LinkInfo()) {
   2846    return Nothing();
   2847  }
   2848 
   2849  for (const auto& activeUniform : aProg->LinkInfo()->active.activeUniforms) {
   2850    if (activeUniform.block_index != -1) continue;
   2851 
   2852    auto locName = activeUniform.name;
   2853    const auto indexed = webgl::ParseIndexed(locName);
   2854    if (indexed) {
   2855      locName = indexed->name;
   2856    }
   2857 
   2858    const auto baseLength = locName.size();
   2859    for (const auto& pair : activeUniform.locByIndex) {
   2860      if (indexed) {
   2861        locName.erase(baseLength);  // Erase previous "[N]".
   2862        locName += '[';
   2863        locName += std::to_string(pair.first);
   2864        locName += ']';
   2865      }
   2866      if (locName == aName || locName == aName + "[0]") {
   2867        return Some(pair.second);
   2868      }
   2869    }
   2870  }
   2871 
   2872  return Nothing();
   2873 }
   2874 
   2875 template <class T>
   2876 struct IsUniformDataValT : std::false_type {};
   2877 template <>
   2878 struct IsUniformDataValT<webgl::UniformDataVal> : std::true_type {};
   2879 template <>
   2880 struct IsUniformDataValT<float> : std::true_type {};
   2881 template <>
   2882 struct IsUniformDataValT<int32_t> : std::true_type {};
   2883 template <>
   2884 struct IsUniformDataValT<uint32_t> : std::true_type {};
   2885 
   2886 template <typename T, typename = std::enable_if_t<IsUniformDataValT<T>::value>>
   2887 inline Range<const webgl::UniformDataVal> AsUniformDataVal(
   2888    const Range<const T>& data) {
   2889  return {data.begin().template ReinterpretCast<const webgl::UniformDataVal>(),
   2890          data.end().template ReinterpretCast<const webgl::UniformDataVal>()};
   2891 }
   2892 
   2893 template <class T, size_t N>
   2894 inline void SharedContextWebgl::UniformData(GLenum aFuncElemType,
   2895                                            const Maybe<uint32_t>& aLoc,
   2896                                            const Array<T, N>& aData) {
   2897  // We currently always pass false for transpose. If in the future we need
   2898  // support for transpose then caching needs to take that in to account.
   2899  mWebgl->UniformData(*aLoc, false,
   2900                      AsUniformDataVal(Range<const T>(Span<const T>(aData))));
   2901 }
   2902 
   2903 template <class T, size_t N>
   2904 void SharedContextWebgl::MaybeUniformData(GLenum aFuncElemType,
   2905                                          const Maybe<uint32_t>& aLoc,
   2906                                          const Array<T, N>& aData,
   2907                                          Maybe<Array<T, N>>& aCached) {
   2908  if (aCached.isNothing() || !(*aCached == aData)) {
   2909    aCached = Some(aData);
   2910    UniformData(aFuncElemType, aLoc, aData);
   2911  }
   2912 }
   2913 
   2914 inline void SharedContextWebgl::DrawQuad() {
   2915  mWebgl->DrawArraysInstanced(LOCAL_GL_TRIANGLE_FAN, 0, 4, 1);
   2916 }
   2917 
   2918 void SharedContextWebgl::DrawTriangles(const PathVertexRange& aRange) {
   2919  mWebgl->DrawArraysInstanced(LOCAL_GL_TRIANGLES, GLint(aRange.mOffset),
   2920                              GLsizei(aRange.mLength), 1);
   2921 }
   2922 
   2923 // Common rectangle and pattern drawing function shared by many DrawTarget
   2924 // commands. If aMaskColor is specified, the provided surface pattern will be
   2925 // treated as a mask. If aHandle is specified, then the surface pattern's
   2926 // texture will be cached in the supplied handle, as opposed to using the
   2927 // surface's user data. If aTransformed or aClipped are false, then transforms
   2928 // and/or clipping will be disabled. If aAccelOnly is specified, then this
   2929 // function will return before it would have otherwise drawn without
   2930 // acceleration. If aForceUpdate is specified, then the provided texture handle
   2931 // will be respecified with the provided surface.
   2932 bool SharedContextWebgl::DrawRectAccel(
   2933    const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions,
   2934    Maybe<DeviceColor> aMaskColor, RefPtr<TextureHandle>* aHandle,
   2935    bool aTransformed, bool aClipped, bool aAccelOnly, bool aForceUpdate,
   2936    const StrokeOptions* aStrokeOptions, const PathVertexRange* aVertexRange,
   2937    const Matrix* aRectXform, uint8_t aBlendStage) {
   2938  // If the rect or clip rect is empty, then there is nothing to draw.
   2939  if (aRect.IsEmpty() || mClipRect.IsEmpty()) {
   2940    return true;
   2941  }
   2942 
   2943  // Check if the drawing options and the pattern support acceleration. Also
   2944  // ensure the framebuffer is prepared for drawing. If not, fall back to using
   2945  // the Skia target. When we need to forcefully update a texture, we must be
   2946  // careful to override any pattern limits, as the caller ensures the pattern
   2947  // is otherwise a supported type.
   2948  if (SupportsDrawOptions(aOptions) == SupportsDrawOptionsStatus::No ||
   2949      (!aForceUpdate && !SupportsPattern(aPattern)) || aStrokeOptions ||
   2950      (!mTargetHandle && !mCurrentTarget->MarkChanged())) {
   2951    // If only accelerated drawing was requested, bail out without software
   2952    // drawing fallback.
   2953    if (!aAccelOnly) {
   2954      MOZ_ASSERT(!aVertexRange);
   2955      mCurrentTarget->DrawRectFallback(aRect, aPattern, aOptions, aMaskColor,
   2956                                       aTransformed, aClipped, aStrokeOptions);
   2957    }
   2958    return false;
   2959  }
   2960 
   2961  if (!aBlendStage) {
   2962    if (uint8_t numStages = RequiresMultiStageBlend(aOptions)) {
   2963      for (uint8_t stage = 1; stage <= numStages; ++stage) {
   2964        if (!DrawRectAccel(aRect, aPattern, aOptions, aMaskColor, aHandle,
   2965                           aTransformed, aClipped, aAccelOnly, aForceUpdate,
   2966                           aStrokeOptions, aVertexRange, aRectXform, stage)) {
   2967          return false;
   2968        }
   2969      }
   2970      return true;
   2971    }
   2972  }
   2973 
   2974  const Matrix& currentTransform = mCurrentTarget->GetTransform();
   2975  // rectXform only applies to the rect, but should not apply to the pattern,
   2976  // as it might inadvertently alter the pattern.
   2977  Matrix rectXform = currentTransform;
   2978  if (aRectXform) {
   2979    rectXform.PreMultiply(*aRectXform);
   2980  }
   2981 
   2982  if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE && aClipped &&
   2983      (aVertexRange ||
   2984       !(aTransformed
   2985             ? rectXform.PreservesAxisAlignedRectangles() &&
   2986                   rectXform.TransformBounds(aRect).Contains(mClipAARect)
   2987             : aRect.IsEqualEdges(Rect(mClipRect)) ||
   2988                   aRect.Contains(mClipAARect)) ||
   2989       (aPattern.GetType() == PatternType::SURFACE &&
   2990        (HasClipMask() || !IsAlignedRect(false, Matrix(), mClipAARect))))) {
   2991    // Clear outside the mask region for masks that are not bounded by clip.
   2992    return DrawRectAccel(Rect(mClipRect), ColorPattern(DeviceColor(0, 0, 0, 0)),
   2993                         DrawOptions(1.0f, CompositionOp::OP_SOURCE,
   2994                                     aOptions.mAntialiasMode),
   2995                         Nothing(), nullptr, false, aClipped, aAccelOnly) &&
   2996           DrawRectAccel(aRect, aPattern,
   2997                         DrawOptions(aOptions.mAlpha, CompositionOp::OP_ADD,
   2998                                     aOptions.mAntialiasMode),
   2999                         aMaskColor, aHandle, aTransformed, aClipped,
   3000                         aAccelOnly, aForceUpdate, aStrokeOptions, aVertexRange,
   3001                         aRectXform);
   3002  }
   3003  if (aOptions.mCompositionOp == CompositionOp::OP_CLEAR &&
   3004      aPattern.GetType() == PatternType::SURFACE && !aMaskColor) {
   3005    // If the surface being drawn with clear is not a mask, then its contents
   3006    // needs to be ignored. Just use a color pattern instead.
   3007    return DrawRectAccel(aRect, ColorPattern(DeviceColor(1, 1, 1, 1)), aOptions,
   3008                         Nothing(), aHandle, aTransformed, aClipped, aAccelOnly,
   3009                         aForceUpdate, aStrokeOptions, aVertexRange,
   3010                         aRectXform);
   3011  }
   3012 
   3013  // Set up the scissor test to reflect the clipping rectangle, if supplied.
   3014  if (!mClipRect.Contains(IntRect(IntPoint(), mViewportSize))) {
   3015    EnableScissor(mClipRect);
   3016  } else {
   3017    DisableScissor();
   3018  }
   3019 
   3020  bool success = false;
   3021 
   3022  // Now try to actually draw the pattern...
   3023  switch (aPattern.GetType()) {
   3024    case PatternType::COLOR: {
   3025      if (!aVertexRange) {
   3026        // Only an uncached draw if not using the vertex cache.
   3027        mCurrentTarget->mProfile.OnUncachedDraw();
   3028      }
   3029      DeviceColor color = PremultiplyColor(
   3030          static_cast<const ColorPattern&>(aPattern).mColor, aOptions.mAlpha);
   3031      if (((color.a == 1.0f &&
   3032            aOptions.mCompositionOp == CompositionOp::OP_OVER) ||
   3033           aOptions.mCompositionOp == CompositionOp::OP_SOURCE ||
   3034           aOptions.mCompositionOp == CompositionOp::OP_CLEAR) &&
   3035          !aStrokeOptions && !aVertexRange && !HasClipMask() &&
   3036          mClipAARect.IsEqualEdges(Rect(mClipRect))) {
   3037        // Certain color patterns can be mapped to scissored clears. The
   3038        // composition op must effectively overwrite the destination, and the
   3039        // transform must map to an axis-aligned integer rectangle.
   3040        if (Maybe<IntRect> intRect =
   3041                IsAlignedRect(aTransformed, rectXform, aRect)) {
   3042          // Only use a clear if the area is larger than a quarter or the
   3043          // viewport.
   3044          if (intRect->Area() >=
   3045              (mViewportSize.width / 2) * (mViewportSize.height / 2)) {
   3046            if (!intRect->Contains(mClipRect)) {
   3047              EnableScissor(intRect->Intersect(mClipRect));
   3048            }
   3049            if (aOptions.mCompositionOp == CompositionOp::OP_CLEAR) {
   3050              color =
   3051                  PremultiplyColor(mCurrentTarget->GetClearPattern().mColor);
   3052            }
   3053            mWebgl->ClearColor(color.b, color.g, color.r, color.a);
   3054            mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
   3055            success = true;
   3056            break;
   3057          }
   3058        }
   3059      }
   3060      // Map the composition op to a WebGL blend mode, if possible.
   3061      Maybe<DeviceColor> blendColor;
   3062      if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE ||
   3063          aOptions.mCompositionOp == CompositionOp::OP_CLEAR) {
   3064        // The source operator can support clipping and AA by emulating it with
   3065        // the over op. Supply the color with blend state, and set the shader
   3066        // color to white, to avoid needing dual-source blending.
   3067        blendColor = Some(color);
   3068        // Both source and clear operators should output a mask from the shader.
   3069        color = DeviceColor(1, 1, 1, 1);
   3070      }
   3071      SetBlendState(aOptions.mCompositionOp, blendColor, aBlendStage);
   3072      // Since it couldn't be mapped to a scissored clear, we need to use the
   3073      // solid color shader with supplied transform.
   3074      if (mLastProgram != mSolidProgram) {
   3075        mWebgl->UseProgram(mSolidProgram);
   3076        mLastProgram = mSolidProgram;
   3077      }
   3078      Array<float, 2> viewportData = {float(mViewportSize.width),
   3079                                      float(mViewportSize.height)};
   3080      MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, viewportData,
   3081                       mSolidProgramUniformState.mViewport);
   3082 
   3083      // Generated paths provide their own AA as vertex alpha.
   3084      Array<float, 1> aaData = {aVertexRange ? 0.0f : 1.0f};
   3085      MaybeUniformData(LOCAL_GL_FLOAT, mSolidProgramAA, aaData,
   3086                       mSolidProgramUniformState.mAA);
   3087 
   3088      // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
   3089      // boundary.
   3090      Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
   3091                                  mClipAARect.XMost() + 0.5f,
   3092                                  mClipAARect.YMost() + 0.5f};
   3093      MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, clipData,
   3094                       mSolidProgramUniformState.mClipBounds);
   3095 
   3096      Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
   3097      Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
   3098      if (aTransformed) {
   3099        xform *= rectXform;
   3100      }
   3101      Array<float, 6> xformData = {xform._11, xform._12, xform._21,
   3102                                   xform._22, xform._31, xform._32};
   3103      MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, xformData,
   3104                       mSolidProgramUniformState.mTransform);
   3105 
   3106      MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, colorData,
   3107                       mSolidProgramUniformState.mColor);
   3108 
   3109      // Finally draw the colored rectangle.
   3110      if (aVertexRange) {
   3111        // If there's a vertex range, then we need to draw triangles within from
   3112        // generated from a path stored in the path vertex buffer.
   3113        DrawTriangles(*aVertexRange);
   3114      } else {
   3115        // Otherwise we're drawing a simple filled rectangle.
   3116        DrawQuad();
   3117      }
   3118      success = true;
   3119      break;
   3120    }
   3121    case PatternType::SURFACE: {
   3122      auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
   3123      // If a texture handle was supplied, or if the surface already has an
   3124      // assigned texture handle stashed in its used data, try to use it.
   3125      RefPtr<SourceSurface> underlyingSurface =
   3126          surfacePattern.mSurface
   3127              ? surfacePattern.mSurface->GetUnderlyingSurface()
   3128              : nullptr;
   3129      RefPtr<TextureHandle> handle =
   3130          aHandle
   3131              ? aHandle->get()
   3132              : (underlyingSurface
   3133                     ? static_cast<TextureHandle*>(
   3134                           underlyingSurface->GetUserData(&mTextureHandleKey))
   3135                     : nullptr);
   3136      IntSize texSize;
   3137      IntPoint offset;
   3138      SurfaceFormat format;
   3139      // Check if the found handle is still valid and if its sampling rect
   3140      // matches the requested sampling rect.
   3141      if (handle && handle->IsValid() &&
   3142          (surfacePattern.mSamplingRect.IsEmpty() ||
   3143           handle->GetSamplingRect().IsEqualEdges(
   3144               surfacePattern.mSamplingRect)) &&
   3145          (surfacePattern.mExtendMode == ExtendMode::CLAMP ||
   3146           handle->GetType() == TextureHandle::STANDALONE)) {
   3147        texSize = handle->GetSize();
   3148        format = handle->GetFormat();
   3149        offset = handle->GetSamplingOffset();
   3150      } else {
   3151        // Otherwise, there is no handle that can be used yet, so extract
   3152        // information from the surface pattern.
   3153        handle = nullptr;
   3154        if (!underlyingSurface) {
   3155          // If there was no actual surface supplied, then we tried to draw
   3156          // using a texture handle, but the texture handle wasn't valid.
   3157          break;
   3158        }
   3159        texSize = underlyingSurface->GetSize();
   3160        format = underlyingSurface->GetFormat();
   3161        if (!surfacePattern.mSamplingRect.IsEmpty()) {
   3162          texSize = surfacePattern.mSamplingRect.Size();
   3163          offset = surfacePattern.mSamplingRect.TopLeft();
   3164        }
   3165      }
   3166 
   3167      // We need to be able to transform from local space into texture space.
   3168      Matrix invMatrix = surfacePattern.mMatrix;
   3169      // Determine if the requested surface itself is offset from the underlying
   3170      // surface.
   3171      if (surfacePattern.mSurface) {
   3172        invMatrix.PreTranslate(surfacePattern.mSurface->GetRect().TopLeft());
   3173      }
   3174      // If drawing a pre-transformed vertex range, then we need to ensure the
   3175      // user-space pattern is still transformed to screen-space.
   3176      if (aVertexRange && !aTransformed) {
   3177        invMatrix *= currentTransform;
   3178      }
   3179      if (!invMatrix.Invert()) {
   3180        break;
   3181      }
   3182      if (aRectXform) {
   3183        // If there is aRectXform, it must be applied to the source rectangle to
   3184        // generate the proper input coordinates for the inverse pattern matrix.
   3185        invMatrix.PreMultiply(*aRectXform);
   3186      }
   3187 
   3188      RefPtr<WebGLTexture> tex;
   3189      IntRect bounds;
   3190      IntSize backingSize;
   3191      if (handle) {
   3192        if (aForceUpdate) {
   3193          RefPtr<DataSourceSurface> data = underlyingSurface->GetDataSurface();
   3194          if (!data) {
   3195            break;
   3196          }
   3197          UploadSurfaceToHandle(data, offset, handle);
   3198          // The size of the texture may change if we update contents.
   3199          mUsedTextureMemory -= handle->UsedBytes();
   3200          handle->UpdateSize(texSize);
   3201          mUsedTextureMemory += handle->UsedBytes();
   3202          handle->SetSamplingOffset(surfacePattern.mSamplingRect.TopLeft());
   3203        } else {
   3204          // Count reusing a snapshot texture (no readback) as a cache hit.
   3205          mCurrentTarget->mProfile.OnCacheHit();
   3206        }
   3207        // If using an existing handle, move it to the front of the MRU list.
   3208        handle->remove();
   3209        mTextureHandles.insertFront(handle);
   3210      } else if ((tex = GetCompatibleSnapshot(underlyingSurface, &handle))) {
   3211        backingSize = underlyingSurface->GetSize();
   3212        bounds = IntRect(offset, texSize);
   3213        // Count reusing a snapshot texture (no readback) as a cache hit.
   3214        mCurrentTarget->mProfile.OnCacheHit();
   3215        if (aHandle) {
   3216          *aHandle = handle;
   3217        }
   3218      } else {
   3219        // If we get here, we need a data surface for a texture upload.
   3220        RefPtr<DataSourceSurface> data = underlyingSurface->GetDataSurface();
   3221        if (!data) {
   3222          break;
   3223        }
   3224        // There is no existing handle. Try to allocate a new one. If the
   3225        // surface size may change via a forced update, then don't allocate
   3226        // from a shared texture page.
   3227        handle = AllocateTextureHandle(
   3228            format, texSize,
   3229            !aForceUpdate && surfacePattern.mExtendMode == ExtendMode::CLAMP);
   3230        if (!handle) {
   3231          MOZ_ASSERT(false);
   3232          break;
   3233        }
   3234        UploadSurfaceToHandle(data, offset, handle);
   3235        // Link the handle to the surface's user data.
   3236        handle->SetSamplingOffset(surfacePattern.mSamplingRect.TopLeft());
   3237        if (aHandle) {
   3238          *aHandle = handle;
   3239        } else {
   3240          handle->SetSurface(underlyingSurface);
   3241          underlyingSurface->AddUserData(&mTextureHandleKey, handle.get(),
   3242                                         nullptr);
   3243        }
   3244      }
   3245      if (handle) {
   3246        BackingTexture* backing = handle->GetBackingTexture();
   3247        if (!tex) {
   3248          tex = backing->GetWebGLTexture();
   3249        }
   3250        bounds = bounds.IsEmpty() ? handle->GetBounds()
   3251                                  : handle->GetBounds().SafeIntersect(
   3252                                        bounds + handle->GetBounds().TopLeft());
   3253        backingSize = backing->GetSize();
   3254      }
   3255 
   3256      // Map the composition op to a WebGL blend mode, if possible. If there is
   3257      // a mask color and a texture with multiple channels, assume subpixel
   3258      // blending. If we encounter the source op here, then assume the surface
   3259      // is opaque (non-opaque is handled above) and emulate it with over.
   3260      SetBlendState(aOptions.mCompositionOp,
   3261                    format != SurfaceFormat::A8 ? aMaskColor : Nothing(),
   3262                    aBlendStage);
   3263      // Switch to the image shader and set up relevant transforms.
   3264      if (mLastProgram != mImageProgram) {
   3265        mWebgl->UseProgram(mImageProgram);
   3266        mLastProgram = mImageProgram;
   3267      }
   3268 
   3269      Array<float, 2> viewportData = {float(mViewportSize.width),
   3270                                      float(mViewportSize.height)};
   3271      MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramViewport, viewportData,
   3272                       mImageProgramUniformState.mViewport);
   3273 
   3274      // AA is not supported for OP_SOURCE. Generated paths provide their own
   3275      // AA as vertex alpha.
   3276      Array<float, 1> aaData = {
   3277          mLastCompositionOp == CompositionOp::OP_SOURCE || aVertexRange
   3278              ? 0.0f
   3279              : 1.0f};
   3280      MaybeUniformData(LOCAL_GL_FLOAT, mImageProgramAA, aaData,
   3281                       mImageProgramUniformState.mAA);
   3282 
   3283      // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
   3284      // boundary.
   3285      Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
   3286                                  mClipAARect.XMost() + 0.5f,
   3287                                  mClipAARect.YMost() + 0.5f};
   3288      MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramClipBounds, clipData,
   3289                       mImageProgramUniformState.mClipBounds);
   3290 
   3291      DeviceColor color =
   3292          mLastCompositionOp == CompositionOp::OP_CLEAR
   3293              ? DeviceColor(1, 1, 1, 1)
   3294              : PremultiplyColor(
   3295                    aMaskColor && format != SurfaceFormat::A8
   3296                        ? DeviceColor::Mask(1.0f, aMaskColor->a)
   3297                        : aMaskColor.valueOr(DeviceColor(1, 1, 1, 1)),
   3298                    aOptions.mAlpha);
   3299      Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
   3300      Array<float, 1> swizzleData = {format == SurfaceFormat::A8 ? 1.0f : 0.0f};
   3301      Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
   3302      if (aTransformed) {
   3303        xform *= rectXform;
   3304      }
   3305      Array<float, 6> xformData = {xform._11, xform._12, xform._21,
   3306                                   xform._22, xform._31, xform._32};
   3307      MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTransform, xformData,
   3308                       mImageProgramUniformState.mTransform);
   3309 
   3310      MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramColor, colorData,
   3311                       mImageProgramUniformState.mColor);
   3312 
   3313      MaybeUniformData(LOCAL_GL_FLOAT, mImageProgramSwizzle, swizzleData,
   3314                       mImageProgramUniformState.mSwizzle);
   3315 
   3316      // Start binding the WebGL state for the texture.
   3317      if (mLastTexture != tex) {
   3318        mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
   3319        mLastTexture = tex;
   3320      }
   3321 
   3322      // Set up the texture coordinate matrix to map from the input rectangle to
   3323      // the backing texture subrect.
   3324      Size backingSizeF(backingSize);
   3325      Matrix uvMatrix(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
   3326      uvMatrix *= invMatrix;
   3327      uvMatrix *= Matrix(1.0f / backingSizeF.width, 0.0f, 0.0f,
   3328                         1.0f / backingSizeF.height,
   3329                         float(bounds.x - offset.x) / backingSizeF.width,
   3330                         float(bounds.y - offset.y) / backingSizeF.height);
   3331      Array<float, 6> uvData = {uvMatrix._11, uvMatrix._12, uvMatrix._21,
   3332                                uvMatrix._22, uvMatrix._31, uvMatrix._32};
   3333      MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTexMatrix, uvData,
   3334                       mImageProgramUniformState.mTexMatrix);
   3335 
   3336      // Clamp sampling to within the bounds of the backing texture subrect.
   3337      Array<float, 4> texBounds = {
   3338          (bounds.x + 0.5f) / backingSizeF.width,
   3339          (bounds.y + 0.5f) / backingSizeF.height,
   3340          (bounds.XMost() - 0.5f) / backingSizeF.width,
   3341          (bounds.YMost() - 0.5f) / backingSizeF.height,
   3342      };
   3343      switch (surfacePattern.mExtendMode) {
   3344        case ExtendMode::REPEAT:
   3345          texBounds[0] = -1e16f;
   3346          texBounds[1] = -1e16f;
   3347          texBounds[2] = 1e16f;
   3348          texBounds[3] = 1e16f;
   3349          break;
   3350        case ExtendMode::REPEAT_X:
   3351          texBounds[0] = -1e16f;
   3352          texBounds[2] = 1e16f;
   3353          break;
   3354        case ExtendMode::REPEAT_Y:
   3355          texBounds[1] = -1e16f;
   3356          texBounds[3] = 1e16f;
   3357          break;
   3358        default:
   3359          break;
   3360      }
   3361      MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramTexBounds, texBounds,
   3362                       mImageProgramUniformState.mTexBounds);
   3363 
   3364      // Ensure we use nearest filtering when no antialiasing is requested.
   3365      if (UseNearestFilter(surfacePattern)) {
   3366        SetTexFilter(tex, false);
   3367      }
   3368 
   3369      // Finally draw the image rectangle.
   3370      if (aVertexRange) {
   3371        // If there's a vertex range, then we need to draw triangles within from
   3372        // generated from a path stored in the path vertex buffer.
   3373        DrawTriangles(*aVertexRange);
   3374      } else {
   3375        // Otherwise we're drawing a simple filled rectangle.
   3376        DrawQuad();
   3377      }
   3378 
   3379      // Restore the default linear filter if overridden.
   3380      if (UseNearestFilter(surfacePattern)) {
   3381        SetTexFilter(tex, true);
   3382      }
   3383 
   3384      success = true;
   3385      break;
   3386    }
   3387    default:
   3388      gfxWarning() << "Unknown DrawTargetWebgl::DrawRect pattern type: "
   3389                   << (int)aPattern.GetType();
   3390      break;
   3391  }
   3392 
   3393  return success;
   3394 }
   3395 
   3396 // Get an appropriate input texture for a given surface within the source rect.
   3397 already_AddRefed<WebGLTexture> SharedContextWebgl::GetFilterInputTexture(
   3398    const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
   3399    RefPtr<TextureHandle>* aHandle, IntPoint& aOffset, SurfaceFormat& aFormat,
   3400    IntRect& aBounds, IntSize& aBackingSize) {
   3401  // If a texture handle was supplied, or if the surface already has an
   3402  // assigned texture handle stashed in its used data, try to use it.
   3403  RefPtr<SourceSurface> underlyingSurface =
   3404      aSurface ? aSurface->GetUnderlyingSurface() : nullptr;
   3405  RefPtr<TextureHandle> handle =
   3406      aHandle ? aHandle->get()
   3407              : (underlyingSurface
   3408                     ? static_cast<TextureHandle*>(
   3409                           underlyingSurface->GetUserData(&mTextureHandleKey))
   3410                     : nullptr);
   3411  IntSize texSize;
   3412  IntPoint offset;
   3413  SurfaceFormat format;
   3414  // Check if the found handle is still valid.
   3415  if (handle && handle->IsValid() &&
   3416      (aSourceRect.IsEmpty() ||
   3417       handle->GetSamplingRect().IsEqualEdges(aSourceRect))) {
   3418    texSize = handle->GetSize();
   3419    format = handle->GetFormat();
   3420    offset = handle->GetSamplingOffset();
   3421  } else {
   3422    // Otherwise, there is no handle that can be used yet, so extract
   3423    // information from the surface pattern.
   3424    handle = nullptr;
   3425    if (!underlyingSurface) {
   3426      // If there was no actual surface supplied, then we tried to draw
   3427      // using a texture handle, but the texture handle wasn't valid.
   3428      return nullptr;
   3429    }
   3430    texSize = underlyingSurface->GetSize();
   3431    format = underlyingSurface->GetFormat();
   3432    if (!aSourceRect.IsEmpty()) {
   3433      texSize = aSourceRect.Size();
   3434      offset = aSourceRect.TopLeft();
   3435    }
   3436  }
   3437 
   3438  RefPtr<WebGLTexture> tex;
   3439  IntRect bounds;
   3440  IntSize backingSize;
   3441  if (handle) {
   3442    // If using an existing handle, move it to the front of the MRU list.
   3443    handle->remove();
   3444    mTextureHandles.insertFront(handle);
   3445    // Count reusing a snapshot texture (no readback) as a cache hit.
   3446    mCurrentTarget->mProfile.OnCacheHit();
   3447  } else if ((tex = GetCompatibleSnapshot(underlyingSurface, &handle))) {
   3448    backingSize = underlyingSurface->GetSize();
   3449    bounds = IntRect(offset, texSize);
   3450    // Count reusing a snapshot texture (no readback) as a cache hit.
   3451    mCurrentTarget->mProfile.OnCacheHit();
   3452  } else {
   3453    // If we get here, we need a data surface for a texture upload.
   3454    RefPtr<DataSourceSurface> data = underlyingSurface->GetDataSurface();
   3455    if (!data) {
   3456      return nullptr;
   3457    }
   3458    // There is no existing handle. Try to allocate a new one. If the
   3459    // surface size may change via a forced update, then don't allocate
   3460    // from a shared texture page.
   3461    handle = AllocateTextureHandle(format, texSize);
   3462    if (!handle) {
   3463      MOZ_ASSERT(false);
   3464      return nullptr;
   3465    }
   3466    UploadSurfaceToHandle(data, offset, handle);
   3467    // Link the handle to the surface's user data.
   3468    handle->SetSamplingOffset(aSourceRect.TopLeft());
   3469    if (aHandle) {
   3470      *aHandle = handle;
   3471    } else {
   3472      handle->SetSurface(underlyingSurface);
   3473      underlyingSurface->AddUserData(&mTextureHandleKey, handle.get(), nullptr);
   3474    }
   3475  }
   3476 
   3477  if (handle) {
   3478    BackingTexture* backing = handle->GetBackingTexture();
   3479    if (!tex) {
   3480      tex = backing->GetWebGLTexture();
   3481    }
   3482    bounds = bounds.IsEmpty() ? handle->GetBounds()
   3483                              : handle->GetBounds().SafeIntersect(
   3484                                    bounds + handle->GetBounds().TopLeft());
   3485    backingSize = backing->GetSize();
   3486  }
   3487 
   3488  aOffset = offset;
   3489  aFormat = format;
   3490  aBounds = bounds;
   3491  aBackingSize = backingSize;
   3492  return tex.forget();
   3493 }
   3494 
   3495 // Implements any filter that can be computed with a 5x4 color matrix.
   3496 bool SharedContextWebgl::FilterRect(const Rect& aDestRect,
   3497                                    const Matrix5x4& aColorMatrix,
   3498                                    const RefPtr<SourceSurface>& aSurface,
   3499                                    const IntRect& aSourceRect,
   3500                                    const DrawOptions& aOptions,
   3501                                    RefPtr<TextureHandle>* aHandle,
   3502                                    RefPtr<TextureHandle>* aTargetHandle) {
   3503  if (!aTargetHandle && !mCurrentTarget->MarkChanged()) {
   3504    return false;
   3505  }
   3506 
   3507  IntPoint offset;
   3508  SurfaceFormat format;
   3509  IntRect bounds;
   3510  IntSize backingSize;
   3511  RefPtr<WebGLTexture> tex = GetFilterInputTexture(
   3512      aSurface, aSourceRect, aHandle, offset, format, bounds, backingSize);
   3513  if (!tex) {
   3514    return false;
   3515  }
   3516 
   3517  IntSize viewportSize = mViewportSize;
   3518  bool needTarget = !!aTargetHandle;
   3519  if (needTarget) {
   3520    IntSize targetSize = IntSize::Ceil(aDestRect.Size());
   3521    viewportSize = targetSize;
   3522    // Blur filters need to render to a color target, whereas shadows will only
   3523    // sample alpha.
   3524    // If sourcing from a texture handle as input, be careful not to render to
   3525    // a handle with the same exact backing texture, which is not allowed in
   3526    // WebGL.
   3527    RefPtr<TextureHandle> targetHandle =
   3528        AllocateTextureHandle(format, targetSize, true, true, tex);
   3529    if (!targetHandle) {
   3530      MOZ_ASSERT(false);
   3531      return false;
   3532    }
   3533 
   3534    *aTargetHandle = targetHandle;
   3535 
   3536    BindScratchFramebuffer(targetHandle, true, targetSize);
   3537 
   3538    SetBlendState(CompositionOp::OP_OVER);
   3539  } else {
   3540    // Set up the scissor test to reflect the clipping rectangle, if supplied.
   3541    if (!mClipRect.Contains(IntRect(IntPoint(), mViewportSize))) {
   3542      EnableScissor(mClipRect);
   3543    } else {
   3544      DisableScissor();
   3545    }
   3546 
   3547    // Map the composition op to a WebGL blend mode, if possible.
   3548    SetBlendState(aOptions.mCompositionOp);
   3549  }
   3550 
   3551  // Switch to the filter shader and set up relevant transforms.
   3552  if (mLastProgram != mFilterProgram) {
   3553    mWebgl->UseProgram(mFilterProgram);
   3554    mLastProgram = mFilterProgram;
   3555  }
   3556 
   3557  Array<float, 2> viewportData = {float(viewportSize.width),
   3558                                  float(viewportSize.height)};
   3559  MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mFilterProgramViewport, viewportData,
   3560                   mFilterProgramUniformState.mViewport);
   3561 
   3562  Rect xformRect;
   3563  if (needTarget) {
   3564    // If rendering to a temporary target for an intermediate pass, then fill
   3565    // the entire framebuffer.
   3566    xformRect = Rect(IntRect(IntPoint(), viewportSize));
   3567  } else {
   3568    // If doing a final composite, then render to the requested rectangle.
   3569    xformRect = aDestRect;
   3570  }
   3571  Array<float, 4> xformData = {xformRect.width, xformRect.height, xformRect.x,
   3572                               xformRect.y};
   3573  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTransform, xformData,
   3574                   mFilterProgramUniformState.mTransform);
   3575 
   3576  Rect clipRect;
   3577  if (needTarget) {
   3578    // Disable any AA clipping.
   3579    clipRect = xformRect;
   3580  } else {
   3581    clipRect = mClipAARect;
   3582  }
   3583  // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
   3584  // boundary.
   3585  clipRect.Inflate(0.5f);
   3586  Array<float, 4> clipData = {clipRect.x, clipRect.y, clipRect.XMost(),
   3587                              clipRect.YMost()};
   3588  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramClipBounds, clipData,
   3589                   mFilterProgramUniformState.mClipBounds);
   3590 
   3591  Array<float, 16> colorMatData = {
   3592      aColorMatrix._11, aColorMatrix._12, aColorMatrix._13, aColorMatrix._14,
   3593      aColorMatrix._21, aColorMatrix._22, aColorMatrix._23, aColorMatrix._24,
   3594      aColorMatrix._31, aColorMatrix._32, aColorMatrix._33, aColorMatrix._34,
   3595      aColorMatrix._41, aColorMatrix._42, aColorMatrix._43, aColorMatrix._44};
   3596  MaybeUniformData(LOCAL_GL_FLOAT_MAT4, mFilterProgramColorMatrix, colorMatData,
   3597                   mFilterProgramUniformState.mColorMatrix);
   3598  Array<float, 4> colorOffData = {aColorMatrix._51, aColorMatrix._52,
   3599                                  aColorMatrix._53, aColorMatrix._54};
   3600  MaybeUniformData(LOCAL_GL_FLOAT_MAT4, mFilterProgramColorOffset, colorOffData,
   3601                   mFilterProgramUniformState.mColorOffset);
   3602 
   3603  // Start binding the WebGL state for the texture.
   3604  if (mLastTexture != tex) {
   3605    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
   3606    mLastTexture = tex;
   3607  }
   3608 
   3609  // Set up the texture coordinate matrix to map from the input rectangle to
   3610  // the backing texture subrect.
   3611  Size backingSizeF(backingSize);
   3612  Rect uvXform((bounds.x - offset.x) / backingSizeF.width,
   3613               (bounds.y - offset.y) / backingSizeF.height,
   3614               xformRect.width / backingSizeF.width,
   3615               xformRect.height / backingSizeF.height);
   3616  Array<float, 4> uvData = {uvXform.width, uvXform.height, uvXform.x,
   3617                            uvXform.y};
   3618  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTexMatrix, uvData,
   3619                   mFilterProgramUniformState.mTexMatrix);
   3620 
   3621  // Bounds for inclusion testing. These are not offset by half a pixel because
   3622  // they are not used for clamping, but rather denote pixel thresholds.
   3623  Array<float, 4> texBounds = {
   3624      bounds.x / backingSizeF.width,
   3625      bounds.y / backingSizeF.height,
   3626      bounds.XMost() / backingSizeF.width,
   3627      bounds.YMost() / backingSizeF.height,
   3628  };
   3629  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTexBounds, texBounds,
   3630                   mFilterProgramUniformState.mTexBounds);
   3631 
   3632  RefPtr<WebGLTexture> prevClipMask;
   3633  if (needTarget) {
   3634    // Ensure the current clip mask is ignored.
   3635    prevClipMask = mLastClipMask;
   3636    SetNoClipMask();
   3637  }
   3638 
   3639  DrawQuad();
   3640 
   3641  if (needTarget) {
   3642    // Restore the previous framebuffer state.
   3643    RestoreCurrentTarget(prevClipMask);
   3644  }
   3645 
   3646  return true;
   3647 }
   3648 
   3649 // Filters a surface and draws the result at the specified offset.
   3650 bool DrawTargetWebgl::FilterSurface(const Matrix5x4& aColorMatrix,
   3651                                    SourceSurface* aSurface,
   3652                                    const IntRect& aSourceRect,
   3653                                    const Point& aDest,
   3654                                    const DrawOptions& aOptions) {
   3655  IntRect sourceRect =
   3656      aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
   3657  if (ShouldAccelPath(aOptions, nullptr,
   3658                      Rect(aDest, Size(sourceRect.Size())))) {
   3659    if (mTransform.IsTranslation() &&
   3660        !mSharedContext->RequiresMultiStageBlend(aOptions, this)) {
   3661      // If the transform doesn't require resampling and the blend op is simple,
   3662      // then draw the filter directly to the canvas.
   3663      return mSharedContext->FilterRect(
   3664          Rect(aDest + mTransform.GetTranslation(), Size(sourceRect.Size())),
   3665          aColorMatrix, aSurface, sourceRect, aOptions, nullptr, nullptr);
   3666    }
   3667    // There is a complex transform or blend op, so the filter must be drawn to
   3668    // an intermediate texture first before resampling.
   3669    RefPtr<TextureHandle> resultHandle;
   3670    if (mSharedContext->FilterRect(Rect(Point(0, 0), Size(sourceRect.Size())),
   3671                                   aColorMatrix, aSurface, sourceRect,
   3672                                   DrawOptions(), nullptr, &resultHandle) &&
   3673        resultHandle) {
   3674      SurfacePattern filterPattern(nullptr, ExtendMode::CLAMP,
   3675                                   Matrix::Translation(aDest));
   3676      return mSharedContext->DrawRectAccel(
   3677          Rect(aDest, Size(resultHandle->GetSize())), filterPattern, aOptions,
   3678          Nothing(), &resultHandle, true, true, true);
   3679    }
   3680  }
   3681  return false;
   3682 }
   3683 
   3684 // Provides a single pass of a separable blur.
   3685 bool SharedContextWebgl::BlurRectPass(
   3686    const Rect& aDestRect, const Point& aSigma, bool aHorizontal,
   3687    const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
   3688    const DrawOptions& aOptions, Maybe<DeviceColor> aMaskColor,
   3689    RefPtr<TextureHandle>* aHandle, RefPtr<TextureHandle>* aTargetHandle,
   3690    bool aFilter) {
   3691  IntPoint offset;
   3692  SurfaceFormat format;
   3693  IntRect bounds;
   3694  IntSize backingSize;
   3695  RefPtr<WebGLTexture> tex = GetFilterInputTexture(
   3696      aSurface, aSourceRect, aHandle, offset, format, bounds, backingSize);
   3697  if (!tex) {
   3698    return false;
   3699  }
   3700 
   3701  IntSize viewportSize = mViewportSize;
   3702  IntSize blurRadius(BLUR_ACCEL_RADIUS(aSigma.x), BLUR_ACCEL_RADIUS(aSigma.y));
   3703  bool needTarget = !!aTargetHandle;
   3704  if (needTarget) {
   3705    // For the initial horizontal pass, and also for the second pass of filters,
   3706    // we need to render to a temporary framebuffer that has been inflated to
   3707    // accommodate blurred pixels in the margins.
   3708    IntSize targetSize(
   3709        int(ceil(aDestRect.width)) + blurRadius.width * 2,
   3710        aHorizontal ? bounds.height
   3711                    : int(ceil(aDestRect.height)) + blurRadius.height * 2);
   3712    viewportSize = targetSize;
   3713    // Blur filters need to render to a color target, whereas shadows will only
   3714    // sample alpha.
   3715    // If sourcing from a texture handle as input, be careful not to render to
   3716    // a handle with the same exact backing texture, which is not allowed in
   3717    // WebGL.
   3718    RefPtr<TextureHandle> targetHandle = AllocateTextureHandle(
   3719        aFilter ? format : SurfaceFormat::A8, targetSize, true, true, tex);
   3720    if (!targetHandle) {
   3721      MOZ_ASSERT(false);
   3722      return false;
   3723    }
   3724 
   3725    *aTargetHandle = targetHandle;
   3726 
   3727    BindScratchFramebuffer(targetHandle, true, targetSize);
   3728 
   3729    SetBlendState(CompositionOp::OP_OVER);
   3730  } else {
   3731    // Set up the scissor test to reflect the clipping rectangle, if supplied.
   3732    if (!mClipRect.Contains(IntRect(IntPoint(), mViewportSize))) {
   3733      EnableScissor(mClipRect);
   3734    } else {
   3735      DisableScissor();
   3736    }
   3737 
   3738    // Map the composition op to a WebGL blend mode, if possible.
   3739    SetBlendState(aOptions.mCompositionOp);
   3740  }
   3741 
   3742  // Switch to the blur shader and set up relevant transforms.
   3743  if (mLastProgram != mBlurProgram) {
   3744    mWebgl->UseProgram(mBlurProgram);
   3745    mLastProgram = mBlurProgram;
   3746  }
   3747 
   3748  Array<float, 2> viewportData = {float(viewportSize.width),
   3749                                  float(viewportSize.height)};
   3750  MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mBlurProgramViewport, viewportData,
   3751                   mBlurProgramUniformState.mViewport);
   3752 
   3753  Rect xformRect;
   3754  if (needTarget) {
   3755    // If rendering to a temporary target for an intermediate pass, then fill
   3756    // the entire framebuffer.
   3757    xformRect = Rect(IntRect(IntPoint(), viewportSize));
   3758  } else {
   3759    // If doing a final composite, then render to the requested rectangle,
   3760    // inflated for the blurred margin pixels.
   3761    xformRect = aDestRect;
   3762    xformRect.Inflate(Size(blurRadius));
   3763  }
   3764  Array<float, 4> xformData = {xformRect.width, xformRect.height, xformRect.x,
   3765                               xformRect.y};
   3766  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mBlurProgramTransform, xformData,
   3767                   mBlurProgramUniformState.mTransform);
   3768 
   3769  Rect clipRect;
   3770  if (needTarget) {
   3771    // Disable any AA clipping.
   3772    clipRect = xformRect;
   3773  } else {
   3774    clipRect = mClipAARect;
   3775  }
   3776  // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
   3777  // boundary.
   3778  clipRect.Inflate(0.5f);
   3779  Array<float, 4> clipData = {clipRect.x, clipRect.y, clipRect.XMost(),
   3780                              clipRect.YMost()};
   3781  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mBlurProgramClipBounds, clipData,
   3782                   mBlurProgramUniformState.mClipBounds);
   3783 
   3784  DeviceColor color =
   3785      needTarget ? DeviceColor(1, 1, 1, 1)
   3786                 : PremultiplyColor(aMaskColor.valueOr(DeviceColor(1, 1, 1, 1)),
   3787                                    aOptions.mAlpha);
   3788  Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
   3789  Array<float, 1> swizzleData = {format == SurfaceFormat::A8 ? 1.0f : 0.0f};
   3790  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mBlurProgramColor, colorData,
   3791                   mBlurProgramUniformState.mColor);
   3792  MaybeUniformData(LOCAL_GL_FLOAT, mBlurProgramSwizzle, swizzleData,
   3793                   mBlurProgramUniformState.mSwizzle);
   3794 
   3795  // Start binding the WebGL state for the texture.
   3796  if (mLastTexture != tex) {
   3797    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
   3798    mLastTexture = tex;
   3799  }
   3800 
   3801  // Set up the texture coordinate matrix to map from the input rectangle to
   3802  // the backing texture subrect.
   3803  Size backingSizeF(backingSize);
   3804  Rect uvXform((bounds.x - offset.x - (xformRect.width - bounds.width) / 2) /
   3805                   backingSizeF.width,
   3806               (bounds.y - offset.y - (xformRect.height - bounds.height) / 2) /
   3807                   backingSizeF.height,
   3808               xformRect.width / backingSizeF.width,
   3809               xformRect.height / backingSizeF.height);
   3810  Array<float, 4> uvData = {uvXform.width, uvXform.height, uvXform.x,
   3811                            uvXform.y};
   3812  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mBlurProgramTexMatrix, uvData,
   3813                   mBlurProgramUniformState.mTexMatrix);
   3814 
   3815  // Bounds for inclusion testing. These are not offset by half a pixel because
   3816  // they are not used for clamping, but rather denote pixel thresholds.
   3817  Array<float, 4> texBounds = {
   3818      bounds.x / backingSizeF.width,
   3819      bounds.y / backingSizeF.height,
   3820      bounds.XMost() / backingSizeF.width,
   3821      bounds.YMost() / backingSizeF.height,
   3822  };
   3823  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mBlurProgramTexBounds, texBounds,
   3824                   mBlurProgramUniformState.mTexBounds);
   3825 
   3826  Array<float, 1> sigmaData = {aHorizontal ? aSigma.x : aSigma.y};
   3827  MaybeUniformData(LOCAL_GL_FLOAT, mBlurProgramSigma, sigmaData,
   3828                   mBlurProgramUniformState.mSigma);
   3829 
   3830  Array<float, 2> offsetScale =
   3831      aHorizontal ? Array<float, 2>{1.0f / backingSizeF.width, 0.0f}
   3832                  : Array<float, 2>{0.0f, 1.0f / backingSizeF.height};
   3833  MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mBlurProgramOffsetScale, offsetScale,
   3834                   mBlurProgramUniformState.mOffsetScale);
   3835 
   3836  RefPtr<WebGLTexture> prevClipMask;
   3837  if (needTarget) {
   3838    // Ensure the current clip mask is ignored.
   3839    prevClipMask = mLastClipMask;
   3840    SetNoClipMask();
   3841  }
   3842 
   3843  DrawQuad();
   3844 
   3845  if (needTarget) {
   3846    // Restore the previous framebuffer state.
   3847    RestoreCurrentTarget(prevClipMask);
   3848  }
   3849 
   3850  return true;
   3851 }
   3852 
   3853 // Utility function to schedule multiple blur passes of a separable blur.
   3854 bool SharedContextWebgl::BlurRectAccel(
   3855    const Rect& aDestRect, const Point& aSigma,
   3856    const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
   3857    const DrawOptions& aOptions, Maybe<DeviceColor> aMaskColor,
   3858    RefPtr<TextureHandle>* aHandle, RefPtr<TextureHandle>* aTargetHandle,
   3859    RefPtr<TextureHandle>* aResultHandle, bool aFilter) {
   3860  if (!aResultHandle && !mCurrentTarget->MarkChanged()) {
   3861    return false;
   3862  }
   3863  RefPtr<TextureHandle> targetHandle =
   3864      aTargetHandle ? aTargetHandle->get() : nullptr;
   3865  if (targetHandle && targetHandle->IsValid() &&
   3866      BlurRectPass(aDestRect, aSigma, false, nullptr, IntRect(), aOptions,
   3867                   aMaskColor, &targetHandle, aResultHandle, aFilter)) {
   3868    return true;
   3869  }
   3870 
   3871  RefPtr<TextureHandle> handle = aHandle ? aHandle->get() : nullptr;
   3872  if (BlurRectPass(aDestRect, aSigma, true, aSurface, aSourceRect,
   3873                   DrawOptions(), Nothing(), &handle, &targetHandle, aFilter) &&
   3874      targetHandle &&
   3875      BlurRectPass(aDestRect, aSigma, false, nullptr, IntRect(), aOptions,
   3876                   aMaskColor, &targetHandle, aResultHandle, aFilter)) {
   3877    if (aHandle) {
   3878      *aHandle = handle.forget();
   3879    }
   3880    if (aTargetHandle) {
   3881      *aTargetHandle = targetHandle.forget();
   3882    }
   3883    return true;
   3884  }
   3885  return false;
   3886 }
   3887 
   3888 // Halves the dimensions of a blur input texture for sufficiently large blurs
   3889 // where scaling won't significantly impact resultant blur quality.
   3890 already_AddRefed<SourceSurface> SharedContextWebgl::DownscaleBlurInput(
   3891    SourceSurface* aSurface, const IntRect& aSourceRect, int aIters) {
   3892  if (std::max(aSourceRect.width, aSourceRect.height) <= 1) {
   3893    return nullptr;
   3894  }
   3895  RefPtr<TextureHandle> fullHandle;
   3896  // First check if the source surface is actually a texture.
   3897  if (RefPtr<WebGLTexture> fullTex =
   3898          GetCompatibleSnapshot(aSurface, &fullHandle)) {
   3899    IntRect sourceRect = aSourceRect;
   3900    for (int i = 0; i < aIters; ++i) {
   3901      IntSize halfSize = (sourceRect.Size() + IntSize(1, 1)) / 2;
   3902      // Allocate a half-size texture for the downscale target.
   3903      RefPtr<TextureHandle> halfHandle = AllocateTextureHandle(
   3904          aSurface->GetFormat(), halfSize, true, true, fullTex);
   3905      if (!halfHandle) {
   3906        break;
   3907      }
   3908 
   3909      // Set up the read framebuffer for the full-size texture.
   3910      if (!mScratchFramebuffer) {
   3911        mScratchFramebuffer = mWebgl->CreateFramebuffer();
   3912      }
   3913      mWebgl->BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mScratchFramebuffer);
   3914      webgl::FbAttachInfo readInfo;
   3915      readInfo.tex = fullTex;
   3916      mWebgl->FramebufferAttach(LOCAL_GL_READ_FRAMEBUFFER,
   3917                                LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
   3918                                readInfo);
   3919 
   3920      // Set up the draw framebuffer for the half-size texture.
   3921      if (!mTargetFramebuffer) {
   3922        mTargetFramebuffer = mWebgl->CreateFramebuffer();
   3923      }
   3924      BackingTexture* halfBacking = halfHandle->GetBackingTexture();
   3925      InitRenderTex(halfBacking);
   3926 
   3927      mWebgl->BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mTargetFramebuffer);
   3928      webgl::FbAttachInfo drawInfo;
   3929      drawInfo.tex = halfBacking->GetWebGLTexture();
   3930      mWebgl->FramebufferAttach(LOCAL_GL_DRAW_FRAMEBUFFER,
   3931                                LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
   3932                                drawInfo);
   3933 
   3934      IntRect halfBounds = halfHandle->GetBounds();
   3935      EnableScissor(halfBounds, true);
   3936      if (!halfBacking->IsInitialized()) {
   3937        halfBacking->MarkInitialized();
   3938        // WebGL implicitly clears the backing texture the first time it is
   3939        // used.
   3940      } else if (i == 0 && !aSurface->GetRect().Contains(sourceRect)) {
   3941        // Only clear if the blit does not completely fill the framebuffer.
   3942        ClearRenderTex(halfBacking);
   3943      }
   3944 
   3945      // Do a linear-scaled blit from the full-size to the half-size texture.
   3946      IntRect fullBounds = sourceRect;
   3947      if (fullHandle) {
   3948        fullBounds += fullHandle->GetBounds().TopLeft();
   3949      }
   3950      mWebgl->AsWebGL2()->BlitFramebuffer(
   3951          fullBounds.x, fullBounds.y, fullBounds.XMost(), fullBounds.YMost(),
   3952          halfBounds.x, halfBounds.y, halfBounds.XMost(), halfBounds.YMost(),
   3953          LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_LINEAR);
   3954 
   3955      fullHandle = halfHandle;
   3956      fullTex = halfBacking->GetWebGLTexture();
   3957      sourceRect = IntRect(IntPoint(), halfBounds.Size());
   3958    }
   3959    RestoreCurrentTarget();
   3960    if (fullHandle) {
   3961      if (sourceRect.IsEqualEdges(aSourceRect)) {
   3962        return nullptr;
   3963      }
   3964      // Wrap the half-size texture with a surface.
   3965      RefPtr<SourceSurfaceWebgl> surface = new SourceSurfaceWebgl(this);
   3966      surface->SetHandle(fullHandle);
   3967      return surface.forget();
   3968    }
   3969  }
   3970 
   3971  // If the full-size source surface is not actually a texture, downscale it
   3972  // with a software filter as it will still improve upload performance while
   3973  // reducing the final blur cost.
   3974  IntRect sourceRect = aSourceRect;
   3975  RefPtr<SourceSurface> fullSurface = aSurface;
   3976  for (int i = 0; i < aIters; ++i) {
   3977    IntSize halfSize = (sourceRect.Size() + IntSize(1, 1)) / 2;
   3978    RefPtr<DrawTarget> halfDT = Factory::CreateDrawTarget(
   3979        BackendType::SKIA, halfSize, aSurface->GetFormat());
   3980    if (!halfDT) {
   3981      break;
   3982    }
   3983    halfDT->DrawSurface(
   3984        fullSurface, Rect(halfDT->GetRect()), Rect(sourceRect),
   3985        DrawSurfaceOptions(SamplingFilter::LINEAR),
   3986        DrawOptions(1.0f, aSurface->GetFormat() == SurfaceFormat::A8
   3987                              ? CompositionOp::OP_OVER
   3988                              : CompositionOp::OP_SOURCE));
   3989    RefPtr<SourceSurface> halfSurface = halfDT->Snapshot();
   3990    if (!halfSurface) {
   3991      break;
   3992    }
   3993    fullSurface = halfSurface;
   3994    sourceRect = halfSurface->GetRect();
   3995  }
   3996  if (sourceRect.IsEqualEdges(aSourceRect)) {
   3997    return nullptr;
   3998  }
   3999  return fullSurface.forget();
   4000 }
   4001 
   4002 // Blurs a surface and draws the result at the specified offset.
   4003 bool DrawTargetWebgl::BlurSurface(float aSigma, SourceSurface* aSurface,
   4004                                  const IntRect& aSourceRect,
   4005                                  const Point& aDest,
   4006                                  const DrawOptions& aOptions,
   4007                                  const DeviceColor& aColor) {
   4008  IntRect sourceRect =
   4009      aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
   4010  if (aSigma >= 0.0f && aSigma <= BLUR_ACCEL_SIGMA_MAX &&
   4011      ShouldAccelPath(aOptions, nullptr,
   4012                      Rect(aDest, Size(sourceRect.Size())))) {
   4013    Maybe<DeviceColor> maskColor =
   4014        aSurface->GetFormat() == SurfaceFormat::A8 ? Some(aColor) : Nothing();
   4015    if (aSigma < BLUR_ACCEL_SIGMA_MIN) {
   4016      SurfacePattern maskPattern(aSurface, ExtendMode::CLAMP,
   4017                                 Matrix::Translation(aDest));
   4018      if (!sourceRect.IsEqualEdges(aSurface->GetRect())) {
   4019        maskPattern.mSamplingRect = sourceRect;
   4020      }
   4021      return DrawRect(Rect(aDest, Size(sourceRect.Size())), maskPattern,
   4022                      aOptions, maskColor);
   4023    }
   4024    // For large blurs, attempt to downscale the input texture so that
   4025    // the blur radius can also be reduced to help performance.
   4026    if (aSigma >= BLUR_ACCEL_DOWNSCALE_SIGMA &&
   4027        std::max(sourceRect.width, sourceRect.height) >=
   4028            BLUR_ACCEL_DOWNSCALE_SIZE) {
   4029      if (RefPtr<SourceSurface> scaleSurf = mSharedContext->DownscaleBlurInput(
   4030              aSurface, sourceRect, BLUR_ACCEL_DOWNSCALE_ITERS)) {
   4031        // Approximate the large blur with a smaller blur that scales the sigma
   4032        // proportionally to the surface size.
   4033        IntSize scaleSize = scaleSurf->GetSize();
   4034        Point scale(float(sourceRect.width) / float(scaleSize.width),
   4035                    float(sourceRect.height) / float(scaleSize.height));
   4036        RefPtr<TextureHandle> resultHandle;
   4037        if (mSharedContext->BlurRectAccel(
   4038                Rect(scaleSurf->GetRect()),
   4039                Point(aSigma / scale.x, aSigma / scale.y), scaleSurf,
   4040                scaleSurf->GetRect(), DrawOptions(), Nothing(), nullptr,
   4041                nullptr, &resultHandle, true) &&
   4042            resultHandle) {
   4043          IntSize blurMargin = (resultHandle->GetSize() - scaleSize) / 2;
   4044          Point blurOrigin = aDest - Point(blurMargin.width * scale.x,
   4045                                           blurMargin.height * scale.y);
   4046          SurfacePattern blurPattern(
   4047              nullptr, ExtendMode::CLAMP,
   4048              Matrix::Scaling(scale.x, scale.y).PostTranslate(blurOrigin));
   4049          return mSharedContext->DrawRectAccel(
   4050              Rect(blurOrigin,
   4051                   Size(resultHandle->GetSize()) * Size(scale.x, scale.y)),
   4052              blurPattern,
   4053              DrawOptions(aOptions.mAlpha, aOptions.mCompositionOp,
   4054                          AntialiasMode::DEFAULT),
   4055              maskColor, &resultHandle, true, true, true);
   4056        }
   4057      }
   4058    }
   4059    if (mTransform.IsTranslation() &&
   4060        !mSharedContext->RequiresMultiStageBlend(aOptions, this)) {
   4061      return mSharedContext->BlurRectAccel(
   4062          Rect(aDest + mTransform.GetTranslation(), Size(sourceRect.Size())),
   4063          Point(aSigma, aSigma), aSurface, sourceRect, aOptions, maskColor,
   4064          nullptr, nullptr, nullptr, true);
   4065    }
   4066    RefPtr<TextureHandle> resultHandle;
   4067    if (mSharedContext->BlurRectAccel(
   4068            Rect(Point(0, 0), Size(sourceRect.Size())), Point(aSigma, aSigma),
   4069            aSurface, sourceRect, DrawOptions(), Nothing(), nullptr, nullptr,
   4070            &resultHandle, true) &&
   4071        resultHandle) {
   4072      IntSize blurMargin = (resultHandle->GetSize() - sourceRect.Size()) / 2;
   4073      Point blurOrigin = aDest - Point(blurMargin.width, blurMargin.height);
   4074      SurfacePattern blurPattern(nullptr, ExtendMode::CLAMP,
   4075                                 Matrix::Translation(blurOrigin));
   4076      return mSharedContext->DrawRectAccel(
   4077          Rect(blurOrigin, Size(resultHandle->GetSize())), blurPattern,
   4078          aOptions, maskColor, &resultHandle, true, true, true);
   4079    }
   4080  }
   4081  return false;
   4082 }
   4083 
   4084 static inline int RoundToFactor(int aDim, int aFactor) {
   4085  // If the size is either greater than the factor or not power-of-two, round it
   4086  // up to the round factor.
   4087  int mask = aFactor - 1;
   4088  return aDim > 1 && (aDim > mask || (aDim & (aDim - 1)))
   4089             ? (aDim + mask) & ~mask
   4090             : aDim;
   4091 }
   4092 
   4093 already_AddRefed<TextureHandle> SharedContextWebgl::ResolveFilterInputAccel(
   4094    DrawTargetWebgl* aDT, const Path* aPath, const Pattern& aPattern,
   4095    const IntRect& aSourceRect, const Matrix& aDestTransform,
   4096    const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions,
   4097    SurfaceFormat aFormat) {
   4098  if (SupportsDrawOptions(aOptions) != SupportsDrawOptionsStatus::Yes) {
   4099    return nullptr;
   4100  }
   4101  if (IsContextLost()) {
   4102    return nullptr;
   4103  }
   4104  // Round size to account for potential mipping from blur filters.
   4105  int roundFactor = 2 << BLUR_ACCEL_DOWNSCALE_ITERS;
   4106  IntSize roundSize =
   4107      std::max(aSourceRect.width, aSourceRect.height) >=
   4108              BLUR_ACCEL_DOWNSCALE_SIZE
   4109          ? IntSize(RoundToFactor(aSourceRect.width, roundFactor),
   4110                    RoundToFactor(aSourceRect.height, roundFactor))
   4111          : aSourceRect.Size();
   4112  RefPtr<TextureHandle> handle =
   4113      AllocateTextureHandle(aFormat, roundSize, true, true);
   4114  if (!handle) {
   4115    return nullptr;
   4116  }
   4117 
   4118  BackingTexture* targetBacking = handle->GetBackingTexture();
   4119  InitRenderTex(targetBacking);
   4120  if (!aDT->PrepareContext(false, handle)) {
   4121    return nullptr;
   4122  }
   4123  DisableScissor();
   4124  ClearRenderTex(targetBacking);
   4125 
   4126  AutoRestoreTransform restore(aDT);
   4127  aDT->SetTransform(
   4128      Matrix(aDestTransform).PostTranslate(-aSourceRect.TopLeft()));
   4129 
   4130  const SkPath& skiaPath = static_cast<const PathSkia*>(aPath)->GetPath();
   4131  SkRect skiaRect = SkRect::MakeEmpty();
   4132  // Draw the path as a simple rectangle with a supported pattern when
   4133  // possible.
   4134  if (!aStrokeOptions && skiaPath.isRect(&skiaRect)) {
   4135    RectDouble rect = SkRectToRectDouble(skiaRect);
   4136    RectDouble xformRect = aDT->TransformDouble(rect);
   4137    if (aPattern.GetType() == PatternType::COLOR) {
   4138      if (Maybe<Rect> clipped = aDT->RectClippedToViewport(xformRect)) {
   4139        // If the pattern is transform-invariant and the rect clips to
   4140        // the viewport, just clip drawing to the viewport to avoid
   4141        // transform issues.
   4142        if (DrawRectAccel(*clipped, aPattern, aOptions, Nothing(), nullptr,
   4143                          false, false, true)) {
   4144          return handle.forget();
   4145        }
   4146        return nullptr;
   4147      }
   4148    }
   4149    if (RectInsidePrecisionLimits(xformRect)) {
   4150      if (SupportsPattern(aPattern)) {
   4151        if (DrawRectAccel(NarrowToFloat(rect), aPattern, aOptions, Nothing(),
   4152                          nullptr, true, true, true)) {
   4153          return handle.forget();
   4154        }
   4155        return nullptr;
   4156      }
   4157      if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
   4158        if (Maybe<SurfacePattern> surface =
   4159                aDT->LinearGradientToSurface(xformRect, aPattern)) {
   4160          if (DrawRectAccel(NarrowToFloat(rect), *surface, aOptions, Nothing(),
   4161                            nullptr, true, true, true)) {
   4162            return handle.forget();
   4163          }
   4164          return nullptr;
   4165        }
   4166      }
   4167    }
   4168  }
   4169  if (DrawPathAccel(aPath, aPattern, aOptions, aStrokeOptions)) {
   4170    return handle.forget();
   4171  }
   4172  return nullptr;
   4173 }
   4174 
   4175 already_AddRefed<SourceSurfaceWebgl> DrawTargetWebgl::ResolveFilterInputAccel(
   4176    const Path* aPath, const Pattern& aPattern, const IntRect& aSourceRect,
   4177    const Matrix& aDestTransform, const DrawOptions& aOptions,
   4178    const StrokeOptions* aStrokeOptions, SurfaceFormat aFormat) {
   4179  if (RefPtr<TextureHandle> handle = mSharedContext->ResolveFilterInputAccel(
   4180          this, aPath, aPattern, aSourceRect, aDestTransform, aOptions,
   4181          aStrokeOptions, aFormat)) {
   4182    RefPtr<SourceSurfaceWebgl> surface = new SourceSurfaceWebgl(mSharedContext);
   4183    surface->SetHandle(handle);
   4184    return surface.forget();
   4185  }
   4186  return nullptr;
   4187 }
   4188 
   4189 bool SharedContextWebgl::RemoveSharedTexture(
   4190    const RefPtr<SharedTexture>& aTexture) {
   4191  auto pos =
   4192      std::find(mSharedTextures.begin(), mSharedTextures.end(), aTexture);
   4193  if (pos == mSharedTextures.end()) {
   4194    return false;
   4195  }
   4196  // Keep around a reserve of empty pages to avoid initialization costs from
   4197  // allocating shared pages. If still below the limit of reserved pages, then
   4198  // just add it to the reserve. Otherwise, erase the empty texture page.
   4199  size_t maxBytes = StaticPrefs::gfx_canvas_accelerated_reserve_empty_cache()
   4200                    << 20;
   4201  size_t totalEmpty = mEmptyTextureMemory + aTexture->UsedBytes();
   4202  if (totalEmpty <= maxBytes) {
   4203    mEmptyTextureMemory = totalEmpty;
   4204  } else {
   4205    RemoveTextureMemory(aTexture);
   4206    mSharedTextures.erase(pos);
   4207    ClearLastTexture();
   4208  }
   4209  return true;
   4210 }
   4211 
   4212 void SharedTextureHandle::Cleanup(SharedContextWebgl& aContext) {
   4213  mTexture->Free(*this);
   4214 
   4215  // Check if the shared handle's owning page has no more allocated handles
   4216  // after we freed it. If so, remove the empty shared texture page also.
   4217  if (!mTexture->HasAllocatedHandles()) {
   4218    aContext.RemoveSharedTexture(mTexture);
   4219  }
   4220 }
   4221 
   4222 bool SharedContextWebgl::RemoveStandaloneTexture(
   4223    const RefPtr<StandaloneTexture>& aTexture) {
   4224  auto pos = std::find(mStandaloneTextures.begin(), mStandaloneTextures.end(),
   4225                       aTexture);
   4226  if (pos == mStandaloneTextures.end()) {
   4227    return false;
   4228  }
   4229  RemoveTextureMemory(aTexture);
   4230  mStandaloneTextures.erase(pos);
   4231  ClearLastTexture();
   4232  return true;
   4233 }
   4234 
   4235 void StandaloneTexture::Cleanup(SharedContextWebgl& aContext) {
   4236  aContext.RemoveStandaloneTexture(this);
   4237 }
   4238 
   4239 // Prune a given texture handle and release its associated resources.
   4240 void SharedContextWebgl::PruneTextureHandle(
   4241    const RefPtr<TextureHandle>& aHandle) {
   4242  // Invalidate the handle so nothing will subsequently use its contents.
   4243  aHandle->Invalidate();
   4244  // If the handle has an associated SourceSurface, unlink it.
   4245  UnlinkSurfaceTexture(aHandle);
   4246  // If the handle has an associated CacheEntry, unlink it.
   4247  if (RefPtr<CacheEntry> entry = aHandle->GetCacheEntry()) {
   4248    entry->Unlink();
   4249  }
   4250  // Deduct the used space from the total.
   4251  mUsedTextureMemory -= aHandle->UsedBytes();
   4252  // Ensure any allocated shared or standalone texture regions get freed.
   4253  aHandle->Cleanup(*this);
   4254 }
   4255 
   4256 // Prune any texture memory above the limit (or margin below the limit) or any
   4257 // least-recently-used handles that are no longer associated with any usable
   4258 // surface.
   4259 bool SharedContextWebgl::PruneTextureMemory(size_t aMargin, bool aPruneUnused) {
   4260  // The maximum amount of texture memory that may be used by textures.
   4261  size_t maxBytes = StaticPrefs::gfx_canvas_accelerated_cache_size() << 20;
   4262  maxBytes -= std::min(maxBytes, aMargin);
   4263  size_t maxItems = StaticPrefs::gfx_canvas_accelerated_cache_items();
   4264  size_t oldItems = mNumTextureHandles;
   4265  while (!mTextureHandles.isEmpty() &&
   4266         (mUsedTextureMemory > maxBytes || mNumTextureHandles > maxItems ||
   4267          (aPruneUnused && !mTextureHandles.getLast()->IsUsed()))) {
   4268    PruneTextureHandle(mTextureHandles.popLast());
   4269    --mNumTextureHandles;
   4270  }
   4271  return mNumTextureHandles < oldItems;
   4272 }
   4273 
   4274 // Attempt to convert a linear gradient to a 1D ramp texture.
   4275 Maybe<SurfacePattern> DrawTargetWebgl::LinearGradientToSurface(
   4276    const RectDouble& aBounds, const Pattern& aPattern) {
   4277  MOZ_ASSERT(aPattern.GetType() == PatternType::LINEAR_GRADIENT);
   4278  const auto& gradient = static_cast<const LinearGradientPattern&>(aPattern);
   4279  // The gradient points must be transformed by the gradient's matrix.
   4280  Point gradBegin = gradient.mMatrix.TransformPoint(gradient.mBegin);
   4281  Point gradEnd = gradient.mMatrix.TransformPoint(gradient.mEnd);
   4282  // Get the gradient points in user-space.
   4283  Point begin = mTransform.TransformPoint(gradBegin);
   4284  Point end = mTransform.TransformPoint(gradEnd);
   4285  // Find the normalized direction of the gradient and its length.
   4286  Point dir = end - begin;
   4287  float len = dir.Length();
   4288  dir = dir / len;
   4289  // Restrict the rendered bounds to fall within the canvas.
   4290  Rect visBounds = NarrowToFloat(aBounds.SafeIntersect(RectDouble(GetRect())));
   4291  // Calculate the distances along the gradient direction of the bounds.
   4292  float dist0 = (visBounds.TopLeft() - begin).DotProduct(dir);
   4293  float distX = visBounds.width * dir.x;
   4294  float distY = visBounds.height * dir.y;
   4295  float minDist = floorf(
   4296      std::max(dist0 + std::min(distX, 0.0f) + std::min(distY, 0.0f), 0.0f));
   4297  float maxDist = ceilf(
   4298      std::min(dist0 + std::max(distX, 0.0f) + std::max(distY, 0.0f), len));
   4299  // Calculate the approximate size of the ramp texture, and see if it would be
   4300  // sufficiently smaller than just rendering the primitive.
   4301  float subLen = maxDist - minDist;
   4302  if (subLen > 0 && subLen < 0.5f * visBounds.Area()) {
   4303    // Create a 1D texture to contain the gradient ramp. Reserve two extra
   4304    // texels at the beginning and end of the ramp to account for clamping.
   4305    RefPtr<DrawTargetSkia> dt = new DrawTargetSkia;
   4306    if (dt->Init(IntSize(int32_t(subLen + 2), 1), SurfaceFormat::B8G8R8A8)) {
   4307      // Fill the section of the gradient ramp that is actually used.
   4308      dt->FillRect(Rect(dt->GetRect()),
   4309                   LinearGradientPattern(Point(1 - minDist, 0.0f),
   4310                                         Point(len + 1 - minDist, 0.0f),
   4311                                         gradient.mStops));
   4312      if (RefPtr<SourceSurface> snapshot = dt->Snapshot()) {
   4313        // Calculate a matrix that will map the gradient ramp texture onto the
   4314        // actual direction of the gradient.
   4315        Point gradDir = (gradEnd - gradBegin) / len;
   4316        Point tangent = Point(-gradDir.y, gradDir.x) / gradDir.Length();
   4317        SurfacePattern surfacePattern(
   4318            snapshot, ExtendMode::CLAMP,
   4319            Matrix(gradDir.x, gradDir.y, tangent.x, tangent.y, gradBegin.x,
   4320                   gradBegin.y)
   4321                .PreTranslate(minDist - 1, 0));
   4322        if (SupportsPattern(surfacePattern)) {
   4323          return Some(surfacePattern);
   4324        }
   4325      }
   4326    }
   4327  }
   4328  return Nothing();
   4329 }
   4330 
   4331 void DrawTargetWebgl::FillRect(const Rect& aRect, const Pattern& aPattern,
   4332                               const DrawOptions& aOptions) {
   4333  RectDouble xformRect = TransformDouble(aRect);
   4334  if (aPattern.GetType() == PatternType::COLOR) {
   4335    if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
   4336      // If the pattern is transform-invariant and the rect clips to the
   4337      // viewport, just clip drawing to the viewport to avoid transform
   4338      // issues.
   4339      DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false);
   4340      return;
   4341    }
   4342  }
   4343  if (RectInsidePrecisionLimits(xformRect)) {
   4344    if (SupportsPattern(aPattern)) {
   4345      DrawRect(aRect, aPattern, aOptions);
   4346      return;
   4347    }
   4348    if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
   4349      if (Maybe<SurfacePattern> surface =
   4350              LinearGradientToSurface(xformRect, aPattern)) {
   4351        if (DrawRect(aRect, *surface, aOptions, Nothing(), nullptr, true, true,
   4352                     true)) {
   4353          return;
   4354        }
   4355      }
   4356    }
   4357  }
   4358 
   4359  if (!mWebglValid) {
   4360    MarkSkiaChanged(aOptions);
   4361    mSkia->FillRect(aRect, aPattern, aOptions);
   4362  } else {
   4363    // If the pattern is unsupported, then transform the rect to a path so it
   4364    // can be cached.
   4365    SkPath skiaPath;
   4366    skiaPath.addRect(RectToSkRect(aRect));
   4367    RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
   4368    DrawPath(path, aPattern, aOptions);
   4369  }
   4370 }
   4371 
   4372 void CacheEntry::Link(const RefPtr<TextureHandle>& aHandle) {
   4373  mHandle = aHandle;
   4374  mHandle->SetCacheEntry(this);
   4375 }
   4376 
   4377 // When the CacheEntry becomes unused, it marks the corresponding
   4378 // TextureHandle as unused and unlinks it from the CacheEntry. The
   4379 // entry is removed from its containing Cache, if applicable.
   4380 void CacheEntry::Unlink() {
   4381  // The entry may not have a valid handle if rasterization failed.
   4382  if (mHandle) {
   4383    mHandle->SetCacheEntry(nullptr);
   4384    mHandle = nullptr;
   4385  }
   4386 
   4387  RemoveFromList();
   4388 }
   4389 
   4390 // Hashes a path and pattern to a single hash value that can be used for quick
   4391 // comparisons. This currently avoids to expensive hashing of internal path
   4392 // and pattern data for speed, relying instead on later exact comparisons for
   4393 // disambiguation.
   4394 HashNumber PathCacheEntry::HashPath(const QuantizedPath& aPath,
   4395                                    const Pattern* aPattern,
   4396                                    const Matrix& aTransform,
   4397                                    const IntRect& aBounds,
   4398                                    const Point& aOrigin) {
   4399  HashNumber hash = 0;
   4400  hash = AddToHash(hash, aPath.mPath.num_types);
   4401  hash = AddToHash(hash, aPath.mPath.num_points);
   4402  if (aPath.mPath.num_points > 0) {
   4403    hash = AddToHash(hash, aPath.mPath.points[0].x);
   4404    hash = AddToHash(hash, aPath.mPath.points[0].y);
   4405    if (aPath.mPath.num_points > 1) {
   4406      hash = AddToHash(hash, aPath.mPath.points[1].x);
   4407      hash = AddToHash(hash, aPath.mPath.points[1].y);
   4408    }
   4409  }
   4410  // Quantize the relative offset of the path to its bounds.
   4411  IntPoint offset = RoundedToInt((aOrigin - Point(aBounds.TopLeft())) * 16.0f);
   4412  hash = AddToHash(hash, offset.x);
   4413  hash = AddToHash(hash, offset.y);
   4414  hash = AddToHash(hash, aBounds.width);
   4415  hash = AddToHash(hash, aBounds.height);
   4416  if (aPattern) {
   4417    hash = AddToHash(hash, (int)aPattern->GetType());
   4418  }
   4419  return hash;
   4420 }
   4421 
   4422 // When caching rendered geometry, we need to ensure the scale and orientation
   4423 // is approximately the same. The offset will be considered separately.
   4424 static inline bool HasMatchingScale(const Matrix& aTransform1,
   4425                                    const Matrix& aTransform2) {
   4426  return FuzzyEqual(aTransform1._11, aTransform2._11) &&
   4427         FuzzyEqual(aTransform1._22, aTransform2._22) &&
   4428         FuzzyEqual(aTransform1._12, aTransform2._12) &&
   4429         FuzzyEqual(aTransform1._21, aTransform2._21);
   4430 }
   4431 
   4432 static const float kIgnoreSigma = 1e6f;
   4433 
   4434 // Determines if an existing path cache entry matches an incoming path and
   4435 // pattern.
   4436 inline bool PathCacheEntry::MatchesPath(
   4437    const QuantizedPath& aPath, const Pattern* aPattern,
   4438    const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
   4439    const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
   4440    HashNumber aHash, float aSigma) {
   4441  return aHash == mHash && HasMatchingScale(aTransform, mTransform) &&
   4442         // Ensure the clipped relative bounds fit inside those of the entry
   4443         aBounds.x - aOrigin.x >= mBounds.x - mOrigin.x &&
   4444         (aBounds.x - aOrigin.x) + aBounds.width <=
   4445             (mBounds.x - mOrigin.x) + mBounds.width &&
   4446         aBounds.y - aOrigin.y >= mBounds.y - mOrigin.y &&
   4447         (aBounds.y - aOrigin.y) + aBounds.height <=
   4448             (mBounds.y - mOrigin.y) + mBounds.height &&
   4449         aPath == mPath &&
   4450         (!aPattern ? !mPattern : mPattern && *aPattern == *mPattern) &&
   4451         (!aStrokeOptions
   4452              ? !mStrokeOptions
   4453              : mStrokeOptions && *aStrokeOptions == *mStrokeOptions &&
   4454                    mAAStrokeMode == aStrokeMode) &&
   4455         (aSigma == kIgnoreSigma || aSigma == mSigma);
   4456 }
   4457 
   4458 PathCacheEntry::PathCacheEntry(QuantizedPath&& aPath, Pattern* aPattern,
   4459                               StoredStrokeOptions* aStrokeOptions,
   4460                               AAStrokeMode aStrokeMode,
   4461                               const Matrix& aTransform, const IntRect& aBounds,
   4462                               const Point& aOrigin, HashNumber aHash,
   4463                               float aSigma)
   4464    : CacheEntryImpl<PathCacheEntry>(aTransform, aBounds, aHash),
   4465      mPath(std::move(aPath)),
   4466      mOrigin(aOrigin),
   4467      mPattern(aPattern),
   4468      mStrokeOptions(aStrokeOptions),
   4469      mAAStrokeMode(aStrokeMode),
   4470      mSigma(aSigma) {}
   4471 
   4472 // Attempt to find a matching entry in the path cache. If one isn't found,
   4473 // a new entry will be created. The caller should check whether the contained
   4474 // texture handle is valid to determine if it will need to render the text run
   4475 // or just reuse the cached texture.
   4476 already_AddRefed<PathCacheEntry> PathCache::FindOrInsertEntry(
   4477    QuantizedPath aPath, const Pattern* aPattern,
   4478    const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
   4479    const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
   4480    float aSigma) {
   4481  HashNumber hash =
   4482      PathCacheEntry::HashPath(aPath, aPattern, aTransform, aBounds, aOrigin);
   4483  for (const RefPtr<PathCacheEntry>& entry : GetChain(hash)) {
   4484    if (entry->MatchesPath(aPath, aPattern, aStrokeOptions, aStrokeMode,
   4485                           aTransform, aBounds, aOrigin, hash, aSigma)) {
   4486      return do_AddRef(entry);
   4487    }
   4488  }
   4489  Pattern* pattern = nullptr;
   4490  if (aPattern) {
   4491    pattern = aPattern->CloneWeak();
   4492    if (!pattern) {
   4493      return nullptr;
   4494    }
   4495  }
   4496  StoredStrokeOptions* strokeOptions = nullptr;
   4497  if (aStrokeOptions) {
   4498    strokeOptions = aStrokeOptions->Clone();
   4499    if (!strokeOptions) {
   4500      return nullptr;
   4501    }
   4502  }
   4503  RefPtr<PathCacheEntry> entry =
   4504      new PathCacheEntry(std::move(aPath), pattern, strokeOptions, aStrokeMode,
   4505                         aTransform, aBounds, aOrigin, hash, aSigma);
   4506  Insert(entry);
   4507  return entry.forget();
   4508 }
   4509 
   4510 // Attempt to find a matching entry in the path cache. If one isn't found,
   4511 // just return failure and don't actually create an entry.
   4512 already_AddRefed<PathCacheEntry> PathCache::FindEntry(
   4513    const QuantizedPath& aPath, const Pattern* aPattern,
   4514    const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
   4515    const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
   4516    float aSigma, bool aHasSecondaryHandle) {
   4517  HashNumber hash =
   4518      PathCacheEntry::HashPath(aPath, aPattern, aTransform, aBounds, aOrigin);
   4519  for (const RefPtr<PathCacheEntry>& entry : GetChain(hash)) {
   4520    if (entry->MatchesPath(aPath, aPattern, aStrokeOptions, aStrokeMode,
   4521                           aTransform, aBounds, aOrigin, hash, aSigma) &&
   4522        (!aHasSecondaryHandle || (entry->GetSecondaryHandle() &&
   4523                                  entry->GetSecondaryHandle()->IsValid()))) {
   4524      return do_AddRef(entry);
   4525    }
   4526  }
   4527  return nullptr;
   4528 }
   4529 
   4530 void DrawTargetWebgl::Fill(const Path* aPath, const Pattern& aPattern,
   4531                           const DrawOptions& aOptions) {
   4532  if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
   4533    return;
   4534  }
   4535 
   4536  const SkPath& skiaPath = static_cast<const PathSkia*>(aPath)->GetPath();
   4537  SkRect skiaRect = SkRect::MakeEmpty();
   4538  // Draw the path as a simple rectangle with a supported pattern when possible.
   4539  if (skiaPath.isRect(&skiaRect)) {
   4540    RectDouble rect = SkRectToRectDouble(skiaRect);
   4541    RectDouble xformRect = TransformDouble(rect);
   4542    if (aPattern.GetType() == PatternType::COLOR) {
   4543      if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
   4544        // If the pattern is transform-invariant and the rect clips to the
   4545        // viewport, just clip drawing to the viewport to avoid transform
   4546        // issues.
   4547        DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false);
   4548        return;
   4549      }
   4550    }
   4551 
   4552    if (RectInsidePrecisionLimits(xformRect)) {
   4553      if (SupportsPattern(aPattern)) {
   4554        DrawRect(NarrowToFloat(rect), aPattern, aOptions);
   4555        return;
   4556      }
   4557      if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
   4558        if (Maybe<SurfacePattern> surface =
   4559                LinearGradientToSurface(xformRect, aPattern)) {
   4560          if (DrawRect(NarrowToFloat(rect), *surface, aOptions, Nothing(),
   4561                       nullptr, true, true, true)) {
   4562            return;
   4563          }
   4564        }
   4565      }
   4566    }
   4567  }
   4568 
   4569  DrawPath(aPath, aPattern, aOptions);
   4570 }
   4571 
   4572 void DrawTargetWebgl::FillCircle(const Point& aOrigin, float aRadius,
   4573                                 const Pattern& aPattern,
   4574                                 const DrawOptions& aOptions) {
   4575  DrawCircle(aOrigin, aRadius, aPattern, aOptions);
   4576 }
   4577 
   4578 QuantizedPath::QuantizedPath(const WGR::Path& aPath) : mPath(aPath) {}
   4579 
   4580 QuantizedPath::QuantizedPath(QuantizedPath&& aPath) noexcept
   4581    : mPath(aPath.mPath) {
   4582  aPath.mPath.points = nullptr;
   4583  aPath.mPath.num_points = 0;
   4584  aPath.mPath.types = nullptr;
   4585  aPath.mPath.num_types = 0;
   4586 }
   4587 
   4588 QuantizedPath::~QuantizedPath() {
   4589  if (mPath.points || mPath.types) {
   4590    WGR::wgr_path_release(mPath);
   4591  }
   4592 }
   4593 
   4594 bool QuantizedPath::operator==(const QuantizedPath& aOther) const {
   4595  return mPath.num_types == aOther.mPath.num_types &&
   4596         mPath.num_points == aOther.mPath.num_points &&
   4597         mPath.fill_mode == aOther.mPath.fill_mode &&
   4598         !memcmp(mPath.types, aOther.mPath.types,
   4599                 mPath.num_types * sizeof(uint8_t)) &&
   4600         !memcmp(mPath.points, aOther.mPath.points,
   4601                 mPath.num_points * sizeof(WGR::Point));
   4602 }
   4603 
   4604 // Generate a quantized path from the Skia path using WGR. The supplied
   4605 // transform will be applied to the path. The path is stored relative to its
   4606 // bounds origin to support translation later.
   4607 static Maybe<QuantizedPath> GenerateQuantizedPath(
   4608    WGR::PathBuilder* aPathBuilder, const SkPath& aPath, const Rect& aBounds,
   4609    const Matrix& aTransform) {
   4610  if (!aPathBuilder) {
   4611    return Nothing();
   4612  }
   4613 
   4614  WGR::wgr_builder_reset(aPathBuilder);
   4615  WGR::wgr_builder_set_fill_mode(aPathBuilder,
   4616                                 aPath.getFillType() == SkPathFillType::kWinding
   4617                                     ? WGR::FillMode::Winding
   4618                                     : WGR::FillMode::EvenOdd);
   4619 
   4620  SkPath::RawIter iter(aPath);
   4621  SkPoint params[4];
   4622  SkPath::Verb currentVerb;
   4623 
   4624  // printf_stderr("bounds: (%d, %d) %d x %d\n", aBounds.x, aBounds.y,
   4625  // aBounds.width, aBounds.height);
   4626  Matrix transform = aTransform;
   4627  transform.PostTranslate(-aBounds.TopLeft());
   4628  while ((currentVerb = iter.next(params)) != SkPath::kDone_Verb) {
   4629    switch (currentVerb) {
   4630      case SkPath::kMove_Verb: {
   4631        Point p0 = transform.TransformPoint(SkPointToPoint(params[0]));
   4632        WGR::wgr_builder_move_to(aPathBuilder, p0.x, p0.y);
   4633        break;
   4634      }
   4635      case SkPath::kLine_Verb: {
   4636        Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
   4637        WGR::wgr_builder_line_to(aPathBuilder, p1.x, p1.y);
   4638        break;
   4639      }
   4640      case SkPath::kCubic_Verb: {
   4641        Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
   4642        Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
   4643        Point p3 = transform.TransformPoint(SkPointToPoint(params[3]));
   4644        // printf_stderr("cubic (%f, %f), (%f, %f), (%f, %f)\n", p1.x, p1.y,
   4645        // p2.x, p2.y, p3.x, p3.y);
   4646        WGR::wgr_builder_curve_to(aPathBuilder, p1.x, p1.y, p2.x, p2.y, p3.x,
   4647                                  p3.y);
   4648        break;
   4649      }
   4650      case SkPath::kQuad_Verb: {
   4651        Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
   4652        Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
   4653        // printf_stderr("quad (%f, %f), (%f, %f)\n", p1.x, p1.y, p2.x, p2.y);
   4654        WGR::wgr_builder_quad_to(aPathBuilder, p1.x, p1.y, p2.x, p2.y);
   4655        break;
   4656      }
   4657      case SkPath::kConic_Verb: {
   4658        Point p0 = transform.TransformPoint(SkPointToPoint(params[0]));
   4659        Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
   4660        Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
   4661        float w = iter.conicWeight();
   4662        std::vector<Point> quads;
   4663        int numQuads = ConvertConicToQuads(p0, p1, p2, w, quads);
   4664        for (int i = 0; i < numQuads; i++) {
   4665          Point q1 = quads[2 * i + 1];
   4666          Point q2 = quads[2 * i + 2];
   4667          // printf_stderr("conic quad (%f, %f), (%f, %f)\n", q1.x, q1.y, q2.x,
   4668          // q2.y);
   4669          WGR::wgr_builder_quad_to(aPathBuilder, q1.x, q1.y, q2.x, q2.y);
   4670        }
   4671        break;
   4672      }
   4673      case SkPath::kClose_Verb:
   4674        // printf_stderr("close\n");
   4675        WGR::wgr_builder_close(aPathBuilder);
   4676        break;
   4677      default:
   4678        MOZ_ASSERT(false);
   4679        // Unexpected verb found in path!
   4680        return Nothing();
   4681    }
   4682  }
   4683 
   4684  WGR::Path p = WGR::wgr_builder_get_path(aPathBuilder);
   4685  if (!p.num_points || !p.num_types) {
   4686    WGR::wgr_path_release(p);
   4687    return Nothing();
   4688  }
   4689  return Some(QuantizedPath(p));
   4690 }
   4691 
   4692 // Get the output vertex buffer using WGR from an input quantized path.
   4693 static Maybe<WGR::VertexBuffer> GeneratePathVertexBuffer(
   4694    const QuantizedPath& aPath, const IntRect& aClipRect,
   4695    bool aRasterizationTruncates, WGR::OutputVertex* aBuffer,
   4696    size_t aBufferCapacity) {
   4697  WGR::VertexBuffer vb = WGR::wgr_path_rasterize_to_tri_list(
   4698      &aPath.mPath, aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height,
   4699      true, false, aRasterizationTruncates, aBuffer, aBufferCapacity);
   4700  if (!vb.len || (aBuffer && vb.len > aBufferCapacity)) {
   4701    WGR::wgr_vertex_buffer_release(vb);
   4702    return Nothing();
   4703  }
   4704  return Some(vb);
   4705 }
   4706 
   4707 static inline AAStroke::LineJoin ToAAStrokeLineJoin(JoinStyle aJoin) {
   4708  switch (aJoin) {
   4709    case JoinStyle::BEVEL:
   4710      return AAStroke::LineJoin::Bevel;
   4711    case JoinStyle::ROUND:
   4712      return AAStroke::LineJoin::Round;
   4713    case JoinStyle::MITER:
   4714    case JoinStyle::MITER_OR_BEVEL:
   4715      return AAStroke::LineJoin::Miter;
   4716  }
   4717  return AAStroke::LineJoin::Miter;
   4718 }
   4719 
   4720 static inline AAStroke::LineCap ToAAStrokeLineCap(CapStyle aCap) {
   4721  switch (aCap) {
   4722    case CapStyle::BUTT:
   4723      return AAStroke::LineCap::Butt;
   4724    case CapStyle::ROUND:
   4725      return AAStroke::LineCap::Round;
   4726    case CapStyle::SQUARE:
   4727      return AAStroke::LineCap::Square;
   4728  }
   4729  return AAStroke::LineCap::Butt;
   4730 }
   4731 
   4732 static inline Point WGRPointToPoint(const WGR::Point& aPoint) {
   4733  // WGR points are 28.4 fixed-point where (0.0, 0.0) is assumed to be a pixel
   4734  // center, as opposed to (0.5, 0.5) in canvas device space. WGR thus shifts
   4735  // each point by (-0.5, -0.5). To undo this, transform from fixed-point back
   4736  // to floating-point, and reverse the pixel shift by adding back (0.5, 0.5).
   4737  return Point(IntPoint(aPoint.x, aPoint.y)) * (1.0f / 16.0f) +
   4738         Point(0.5f, 0.5f);
   4739 }
   4740 
   4741 // Generates a vertex buffer for a stroked path using aa-stroke.
   4742 static Maybe<AAStroke::VertexBuffer> GenerateStrokeVertexBuffer(
   4743    const QuantizedPath& aPath, const StrokeOptions* aStrokeOptions,
   4744    float aScale, WGR::OutputVertex* aBuffer, size_t aBufferCapacity) {
   4745  AAStroke::StrokeStyle style = {aStrokeOptions->mLineWidth * aScale,
   4746                                 ToAAStrokeLineCap(aStrokeOptions->mLineCap),
   4747                                 ToAAStrokeLineJoin(aStrokeOptions->mLineJoin),
   4748                                 aStrokeOptions->mMiterLimit};
   4749  if (style.width <= 0.0f || !std::isfinite(style.width) ||
   4750      style.miter_limit <= 0.0f || !std::isfinite(style.miter_limit)) {
   4751    return Nothing();
   4752  }
   4753  AAStroke::Stroker* s = AAStroke::aa_stroke_new(
   4754      &style, (AAStroke::OutputVertex*)aBuffer, aBufferCapacity);
   4755  bool valid = true;
   4756  size_t curPoint = 0;
   4757  for (size_t curType = 0; valid && curType < aPath.mPath.num_types;) {
   4758    // Verify that we are at the start of a sub-path.
   4759    if ((aPath.mPath.types[curType] & WGR::PathPointTypePathTypeMask) !=
   4760        WGR::PathPointTypeStart) {
   4761      valid = false;
   4762      break;
   4763    }
   4764    // Find where the next sub-path starts so we can locate the end.
   4765    size_t endType = curType + 1;
   4766    for (; endType < aPath.mPath.num_types; endType++) {
   4767      if ((aPath.mPath.types[endType] & WGR::PathPointTypePathTypeMask) ==
   4768          WGR::PathPointTypeStart) {
   4769        break;
   4770      }
   4771    }
   4772    // Check if the path is closed. This is a flag modifying the last type.
   4773    bool closed =
   4774        (aPath.mPath.types[endType - 1] & WGR::PathPointTypeCloseSubpath) != 0;
   4775    for (; curType < endType; curType++) {
   4776      // If this is the last type and the sub-path is not closed, determine if
   4777      // this segment should be capped.
   4778      bool end = curType + 1 == endType && !closed;
   4779      switch (aPath.mPath.types[curType] & WGR::PathPointTypePathTypeMask) {
   4780        case WGR::PathPointTypeStart: {
   4781          if (curPoint + 1 > aPath.mPath.num_points) {
   4782            valid = false;
   4783            break;
   4784          }
   4785          Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
   4786          AAStroke::aa_stroke_move_to(s, p1.x, p1.y, closed);
   4787          if (end) {
   4788            AAStroke::aa_stroke_line_to(s, p1.x, p1.y, true);
   4789          }
   4790          curPoint++;
   4791          break;
   4792        }
   4793        case WGR::PathPointTypeLine: {
   4794          if (curPoint + 1 > aPath.mPath.num_points) {
   4795            valid = false;
   4796            break;
   4797          }
   4798          Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
   4799          AAStroke::aa_stroke_line_to(s, p1.x, p1.y, end);
   4800          curPoint++;
   4801          break;
   4802        }
   4803        case WGR::PathPointTypeBezier: {
   4804          if (curPoint + 3 > aPath.mPath.num_points) {
   4805            valid = false;
   4806            break;
   4807          }
   4808          Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
   4809          Point p2 = WGRPointToPoint(aPath.mPath.points[curPoint + 1]);
   4810          Point p3 = WGRPointToPoint(aPath.mPath.points[curPoint + 2]);
   4811          AAStroke::aa_stroke_curve_to(s, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y,
   4812                                       end);
   4813          curPoint += 3;
   4814          break;
   4815        }
   4816        default:
   4817          MOZ_ASSERT(false, "Unknown WGR path point type");
   4818          valid = false;
   4819          break;
   4820      }
   4821    }
   4822    // Close the sub-path if necessary.
   4823    if (valid && closed) {
   4824      AAStroke::aa_stroke_close(s);
   4825    }
   4826  }
   4827  Maybe<AAStroke::VertexBuffer> result;
   4828  if (valid) {
   4829    AAStroke::VertexBuffer vb = AAStroke::aa_stroke_finish(s);
   4830    if (!vb.len || (aBuffer && vb.len > aBufferCapacity)) {
   4831      AAStroke::aa_stroke_vertex_buffer_release(vb);
   4832    } else {
   4833      result = Some(vb);
   4834    }
   4835  }
   4836  AAStroke::aa_stroke_release(s);
   4837  return result;
   4838 }
   4839 
   4840 // Search the path cache for any entries stored in the path vertex buffer and
   4841 // remove them.
   4842 void PathCache::ClearVertexRanges() {
   4843  for (auto& chain : mChains) {
   4844    PathCacheEntry* entry = chain.getFirst();
   4845    while (entry) {
   4846      PathCacheEntry* next = entry->getNext();
   4847      if (entry->GetVertexRange().IsValid()) {
   4848        entry->Unlink();
   4849      }
   4850      entry = next;
   4851    }
   4852  }
   4853 }
   4854 
   4855 inline bool DrawTargetWebgl::ShouldAccelPath(
   4856    const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions,
   4857    const Rect& aRect) {
   4858  return mWebglValid && SupportsDrawOptions(aOptions, aRect) &&
   4859         PrepareContext();
   4860 }
   4861 
   4862 // For now, we only directly support stroking solid color patterns to limit
   4863 // artifacts from blending of overlapping geometry generated by AAStroke. Other
   4864 // types of patterns may be partially supported by rendering to a temporary
   4865 // mask.
   4866 static inline AAStrokeMode SupportsAAStroke(const Pattern& aPattern,
   4867                                            const DrawOptions& aOptions,
   4868                                            const StrokeOptions& aStrokeOptions,
   4869                                            bool aAllowStrokeAlpha) {
   4870  if (aStrokeOptions.mDashPattern) {
   4871    return AAStrokeMode::Unsupported;
   4872  }
   4873  switch (aOptions.mCompositionOp) {
   4874    case CompositionOp::OP_SOURCE:
   4875      return AAStrokeMode::Geometry;
   4876    case CompositionOp::OP_OVER:
   4877      if (aPattern.GetType() == PatternType::COLOR) {
   4878        return static_cast<const ColorPattern&>(aPattern).mColor.a *
   4879                               aOptions.mAlpha <
   4880                           1.0f &&
   4881                       !aAllowStrokeAlpha
   4882                   ? AAStrokeMode::Mask
   4883                   : AAStrokeMode::Geometry;
   4884      }
   4885      return AAStrokeMode::Unsupported;
   4886    default:
   4887      return AAStrokeMode::Unsupported;
   4888  }
   4889 }
   4890 
   4891 // Render an AA-Stroke'd vertex range into an R8 mask texture for subsequent
   4892 // drawing.
   4893 already_AddRefed<TextureHandle> SharedContextWebgl::DrawStrokeMask(
   4894    const PathVertexRange& aVertexRange, const IntSize& aSize) {
   4895  // Allocate a new texture handle to store the rendered mask.
   4896  RefPtr<TextureHandle> handle =
   4897      AllocateTextureHandle(SurfaceFormat::A8, aSize, true, true);
   4898  if (!handle) {
   4899    return nullptr;
   4900  }
   4901 
   4902  IntRect texBounds = handle->GetBounds();
   4903  BindScratchFramebuffer(handle, true);
   4904 
   4905  // Reset any blending when drawing the mask.
   4906  SetBlendState(CompositionOp::OP_OVER);
   4907 
   4908  // Set up the solid color shader to draw a simple opaque mask.
   4909  if (mLastProgram != mSolidProgram) {
   4910    mWebgl->UseProgram(mSolidProgram);
   4911    mLastProgram = mSolidProgram;
   4912  }
   4913  Array<float, 2> viewportData = {float(texBounds.width),
   4914                                  float(texBounds.height)};
   4915  MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, viewportData,
   4916                   mSolidProgramUniformState.mViewport);
   4917  Array<float, 1> aaData = {0.0f};
   4918  MaybeUniformData(LOCAL_GL_FLOAT, mSolidProgramAA, aaData,
   4919                   mSolidProgramUniformState.mAA);
   4920  Array<float, 4> clipData = {-0.5f, -0.5f, float(texBounds.width) + 0.5f,
   4921                              float(texBounds.height) + 0.5f};
   4922  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, clipData,
   4923                   mSolidProgramUniformState.mClipBounds);
   4924  Array<float, 4> colorData = {1.0f, 1.0f, 1.0f, 1.0f};
   4925  MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, colorData,
   4926                   mSolidProgramUniformState.mColor);
   4927  Array<float, 6> xformData = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
   4928  MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, xformData,
   4929                   mSolidProgramUniformState.mTransform);
   4930 
   4931  // Ensure the current clip mask is ignored.
   4932  RefPtr<WebGLTexture> prevClipMask = mLastClipMask;
   4933  SetNoClipMask();
   4934 
   4935  // Draw the mask using the supplied path vertex range.
   4936  DrawTriangles(aVertexRange);
   4937 
   4938  // Restore the previous framebuffer state.
   4939  RestoreCurrentTarget(prevClipMask);
   4940 
   4941  return handle.forget();
   4942 }
   4943 
   4944 // Attempts to draw a path using WGR (or AAStroke), when possible.
   4945 bool SharedContextWebgl::DrawWGRPath(
   4946    const Path* aPath, const IntRect& aIntBounds, const Rect& aQuantBounds,
   4947    const Matrix& aPathXform, RefPtr<PathCacheEntry>& aEntry,
   4948    const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions,
   4949    AAStrokeMode aAAStrokeMode, const Pattern& aPattern,
   4950    const Maybe<DeviceColor>& aColor) {
   4951  const PathSkia* pathSkia = static_cast<const PathSkia*>(aPath);
   4952  const Matrix& currentTransform = mCurrentTarget->GetTransform();
   4953  if (aEntry->GetVertexRange().IsValid()) {
   4954    // If there is a valid cached vertex data in the path vertex buffer, then
   4955    // just draw that. We must draw at integer pixel boundaries (using
   4956    // intBounds instead of quantBounds) due to WGR's reliance on pixel center
   4957    // location.
   4958    mCurrentTarget->mProfile.OnCacheHit();
   4959    return DrawRectAccel(Rect(aIntBounds.TopLeft(), Size(1, 1)), aPattern,
   4960                         aOptions, Nothing(), nullptr, false, true, true, false,
   4961                         nullptr, &aEntry->GetVertexRange());
   4962  }
   4963 
   4964  // printf_stderr("Generating... verbs %d, points %d\n",
   4965  //     int(pathSkia->GetPath().countVerbs()),
   4966  //     int(pathSkia->GetPath().countPoints()));
   4967  WGR::OutputVertex* outputBuffer = nullptr;
   4968  size_t outputBufferCapacity = 0;
   4969  if (mWGROutputBuffer) {
   4970    outputBuffer = mWGROutputBuffer.get();
   4971    outputBufferCapacity = mPathVertexCapacity / sizeof(WGR::OutputVertex);
   4972  }
   4973  Maybe<WGR::VertexBuffer> wgrVB;
   4974  Maybe<AAStroke::VertexBuffer> strokeVB;
   4975  if (!aStrokeOptions) {
   4976    if (aPath == mUnitCirclePath) {
   4977      auto scaleFactors = aPathXform.ScaleFactors();
   4978      if (scaleFactors.AreScalesSame()) {
   4979        Point center = aPathXform.GetTranslation() - aQuantBounds.TopLeft();
   4980        float radius = scaleFactors.xScale;
   4981        AAStroke::VertexBuffer vb = AAStroke::aa_stroke_filled_circle(
   4982            center.x, center.y, radius, (AAStroke::OutputVertex*)outputBuffer,
   4983            outputBufferCapacity);
   4984        if (!vb.len || (outputBuffer && vb.len > outputBufferCapacity)) {
   4985          AAStroke::aa_stroke_vertex_buffer_release(vb);
   4986        } else {
   4987          strokeVB = Some(vb);
   4988        }
   4989      }
   4990    }
   4991    if (!strokeVB) {
   4992      wgrVB = GeneratePathVertexBuffer(
   4993          aEntry->GetPath(), IntRect(-aIntBounds.TopLeft(), mViewportSize),
   4994          mRasterizationTruncates, outputBuffer, outputBufferCapacity);
   4995    }
   4996  } else {
   4997    if (aAAStrokeMode != AAStrokeMode::Unsupported) {
   4998      auto scaleFactors = currentTransform.ScaleFactors();
   4999      if (scaleFactors.AreScalesSame()) {
   5000        strokeVB = GenerateStrokeVertexBuffer(aEntry->GetPath(), aStrokeOptions,
   5001                                              scaleFactors.xScale, outputBuffer,
   5002                                              outputBufferCapacity);
   5003      }
   5004    }
   5005    if (!strokeVB && mPathWGRStroke) {
   5006      //  If stroking, then generate a path to fill the stroked region. This
   5007      //  path will need to be quantized again because it differs from the
   5008      //  path used for the cache entry, but this allows us to avoid
   5009      //  generating a fill path on a cache hit.
   5010      Maybe<Rect> cullRect;
   5011      Matrix invTransform = currentTransform;
   5012      if (invTransform.Invert()) {
   5013        // Transform the stroking clip rect from device space to local
   5014        // space.
   5015        Rect invRect = invTransform.TransformBounds(Rect(mClipRect));
   5016        invRect.RoundOut();
   5017        cullRect = Some(invRect);
   5018      }
   5019      SkPath fillPath;
   5020      if (pathSkia->GetFillPath(*aStrokeOptions, aPathXform, fillPath,
   5021                                cullRect)) {
   5022        // printf_stderr("    stroke fill... verbs %d, points %d\n",
   5023        //     int(fillPath.countVerbs()),
   5024        //     int(fillPath.countPoints()));
   5025        if (Maybe<QuantizedPath> qp = GenerateQuantizedPath(
   5026                mWGRPathBuilder, fillPath, aQuantBounds, aPathXform)) {
   5027          wgrVB = GeneratePathVertexBuffer(
   5028              *qp, IntRect(-aIntBounds.TopLeft(), mViewportSize),
   5029              mRasterizationTruncates, outputBuffer, outputBufferCapacity);
   5030        }
   5031      }
   5032    }
   5033  }
   5034  if (!wgrVB && !strokeVB) {
   5035    // Failed to generate any vertex data.
   5036    return false;
   5037  }
   5038  const uint8_t* vbData =
   5039      wgrVB ? (const uint8_t*)wgrVB->data : (const uint8_t*)strokeVB->data;
   5040  if (outputBuffer && !vbData) {
   5041    vbData = (const uint8_t*)outputBuffer;
   5042  }
   5043  size_t vbLen = wgrVB ? wgrVB->len : strokeVB->len;
   5044  uint32_t vertexBytes =
   5045      uint32_t(std::min(vbLen * sizeof(WGR::OutputVertex), size_t(UINT32_MAX)));
   5046  // printf_stderr("  ... %d verts, %d bytes\n", int(vbLen),
   5047  //     int(vertexBytes));
   5048  if (vertexBytes > mPathVertexCapacity - mPathVertexOffset &&
   5049      vertexBytes <= mPathVertexCapacity - sizeof(kRectVertexData)) {
   5050    // If the vertex data is too large to fit in the remaining path vertex
   5051    // buffer, then orphan the contents of the vertex buffer to make room
   5052    // for it.
   5053    if (mPathCache) {
   5054      mPathCache->ClearVertexRanges();
   5055    }
   5056    ResetPathVertexBuffer();
   5057  }
   5058  if (vertexBytes > mPathVertexCapacity - mPathVertexOffset) {
   5059    // There is insufficient space in the path buffer to fit vertex data.
   5060    if (wgrVB) {
   5061      WGR::wgr_vertex_buffer_release(wgrVB.ref());
   5062    } else {
   5063      AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
   5064    }
   5065    return false;
   5066  }
   5067  // If there is actually room to fit the vertex data in the vertex buffer
   5068  // after orphaning as necessary, then upload the data to the next
   5069  // available offset in the buffer.
   5070  PathVertexRange vertexRange(
   5071      uint32_t(mPathVertexOffset / sizeof(WGR::OutputVertex)), uint32_t(vbLen));
   5072  // printf_stderr("      ... offset %d\n", mPathVertexOffset);
   5073  // Normal glBufferSubData interleaved with draw calls causes performance
   5074  // issues on Mali, so use our special unsynchronized version. This is
   5075  // safe as we never update regions referenced by pending draw calls.
   5076  mWebgl->BufferSubData(LOCAL_GL_ARRAY_BUFFER, mPathVertexOffset, vertexBytes,
   5077                        vbData,
   5078                        /* unsynchronized */ true);
   5079  mPathVertexOffset += vertexBytes;
   5080  if (wgrVB) {
   5081    WGR::wgr_vertex_buffer_release(wgrVB.ref());
   5082  } else {
   5083    AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
   5084  }
   5085  if (strokeVB && aAAStrokeMode == AAStrokeMode::Mask) {
   5086    // Attempt to generate a stroke mask for path.
   5087    if (RefPtr<TextureHandle> handle =
   5088            DrawStrokeMask(vertexRange, aIntBounds.Size())) {
   5089      // Finally, draw the rendered stroke mask.
   5090      if (aEntry) {
   5091        aEntry->Link(handle);
   5092      }
   5093      mCurrentTarget->mProfile.OnCacheMiss();
   5094      SurfacePattern maskPattern(nullptr, ExtendMode::CLAMP,
   5095                                 Matrix::Translation(aQuantBounds.TopLeft()),
   5096                                 SamplingFilter::GOOD);
   5097      return DrawRectAccel(aQuantBounds, maskPattern, aOptions, aColor, &handle,
   5098                           false, true, true);
   5099    }
   5100  } else {
   5101    // Remember the vertex range in the cache entry so that it can be
   5102    // reused later.
   5103    if (aEntry) {
   5104      aEntry->SetVertexRange(vertexRange);
   5105    }
   5106 
   5107    // Finally, draw the uploaded vertex data.
   5108    mCurrentTarget->mProfile.OnCacheMiss();
   5109    return DrawRectAccel(Rect(aIntBounds.TopLeft(), Size(1, 1)), aPattern,
   5110                         aOptions, Nothing(), nullptr, false, true, true, false,
   5111                         nullptr, &vertexRange);
   5112  }
   5113  // If we failed to draw the vertex data for some reason, then fall back
   5114  // to the texture rasterization path.
   5115  return false;
   5116 }
   5117 
   5118 bool SharedContextWebgl::DrawPathAccel(
   5119    const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions,
   5120    const StrokeOptions* aStrokeOptions, bool aAllowStrokeAlpha,
   5121    const ShadowOptions* aShadow, bool aCacheable, const Matrix* aPathXform) {
   5122  // Get the transformed bounds for the path and conservatively check if the
   5123  // bounds overlap the canvas.
   5124  const PathSkia* pathSkia = static_cast<const PathSkia*>(aPath);
   5125  const Matrix& currentTransform = mCurrentTarget->GetTransform();
   5126  Matrix pathXform = currentTransform;
   5127  // If there is a path-specific transform that shouldn't be applied to the
   5128  // pattern, then generate a matrix that should only be used with the Skia
   5129  // path.
   5130  if (aPathXform) {
   5131    pathXform.PreMultiply(*aPathXform);
   5132  }
   5133  Rect bounds = pathSkia->GetFastBounds(pathXform, aStrokeOptions);
   5134  // If the path is empty, then there is nothing to draw.
   5135  if (bounds.IsEmpty()) {
   5136    return true;
   5137  }
   5138  // Avoid integer conversion errors with abnormally large paths.
   5139  if (!RectInsidePrecisionLimits(bounds)) {
   5140    return false;
   5141  }
   5142  IntRect viewport(IntPoint(), mViewportSize);
   5143  bool accelShadow = false;
   5144  if (aShadow) {
   5145    // Inflate the bounds to account for the blur radius.
   5146    bounds += aShadow->mOffset;
   5147    if (aShadow->mSigma > 0.0f && aShadow->mSigma <= BLUR_ACCEL_SIGMA_MAX &&
   5148        !RequiresMultiStageBlend(aOptions)) {
   5149      // Allow the input texture to be reused regardless of sigma since it
   5150      // doesn't actually differ.
   5151      viewport.Inflate(2 * BLUR_ACCEL_RADIUS_MAX);
   5152      accelShadow = true;
   5153    } else {
   5154      // Software blurs require inflated inputs dependent on blur radius.
   5155      int32_t blurRadius = aShadow->BlurRadius();
   5156      bounds.Inflate(blurRadius);
   5157      viewport.Inflate(blurRadius);
   5158    }
   5159  }
   5160  Point realOrigin = bounds.TopLeft();
   5161  if (aCacheable) {
   5162    // Quantize the path origin to increase the reuse of cache entries.
   5163    bounds.Scale(4.0f);
   5164    bounds.Round();
   5165    bounds.Scale(0.25f);
   5166  }
   5167  Point quantizedOrigin = bounds.TopLeft();
   5168  // If the path doesn't intersect the viewport, then there is nothing to draw.
   5169  IntRect intBounds = RoundedOut(bounds).Intersect(viewport);
   5170  if (intBounds.IsEmpty()) {
   5171    return true;
   5172  }
   5173  // Nudge the bounds to account for the quantization rounding.
   5174  Rect quantBounds = Rect(intBounds) + (realOrigin - quantizedOrigin);
   5175  // If the pattern is a solid color, then this will be used along with a path
   5176  // mask to render the path, as opposed to baking the pattern into the cached
   5177  // path texture.
   5178  Maybe<DeviceColor> color =
   5179      aOptions.mCompositionOp == CompositionOp::OP_CLEAR
   5180          ? Some(DeviceColor(1, 1, 1, 1))
   5181          : (aPattern.GetType() == PatternType::COLOR
   5182                 ? Some(static_cast<const ColorPattern&>(aPattern).mColor)
   5183                 : Nothing());
   5184  AAStrokeMode aaStrokeMode =
   5185      aStrokeOptions && mPathAAStroke
   5186          ? SupportsAAStroke(aPattern, aOptions, *aStrokeOptions,
   5187                             aAllowStrokeAlpha)
   5188          : AAStrokeMode::Unsupported;
   5189  // Look for an existing path cache entry, if possible, or otherwise create
   5190  // one. If the draw request is not cacheable, then don't create an entry.
   5191  RefPtr<PathCacheEntry> entry;
   5192  RefPtr<TextureHandle> handle;
   5193  if (aCacheable) {
   5194    if (!mPathCache) {
   5195      mPathCache = MakeUnique<PathCache>();
   5196    }
   5197    // Use a quantized, relative (to its bounds origin) version of the path as
   5198    // a cache key to help limit cache bloat.
   5199    Maybe<QuantizedPath> qp = GenerateQuantizedPath(
   5200        mWGRPathBuilder, pathSkia->GetPath(), quantBounds, pathXform);
   5201    if (!qp) {
   5202      return false;
   5203    }
   5204    entry = mPathCache->FindOrInsertEntry(
   5205        std::move(*qp), color ? nullptr : &aPattern, aStrokeOptions,
   5206        aaStrokeMode, currentTransform, intBounds, quantizedOrigin,
   5207        aShadow ? aShadow->mSigma : -1.0f);
   5208    if (!entry) {
   5209      return false;
   5210    }
   5211    handle = entry->GetHandle();
   5212  }
   5213 
   5214  // If there is a shadow, it needs to draw with the shadow color rather than
   5215  // the path color.
   5216  Maybe<DeviceColor> shadowColor = color;
   5217  if (aShadow && aOptions.mCompositionOp != CompositionOp::OP_CLEAR) {
   5218    shadowColor = Some(aShadow->mColor);
   5219    if (color) {
   5220      shadowColor->a *= color->a;
   5221    }
   5222  }
   5223  SamplingFilter filter =
   5224      aShadow ? SamplingFilter::GOOD : GetSamplingFilter(aPattern);
   5225  if (handle && handle->IsValid()) {
   5226    if (accelShadow) {
   5227      return BlurRectAccel(quantBounds, Point(aShadow->mSigma, aShadow->mSigma),
   5228                           nullptr, IntRect(), aOptions, shadowColor, nullptr,
   5229                           &handle);
   5230    }
   5231 
   5232    // If the entry has a valid texture handle still, use it. However, the
   5233    // entry texture is assumed to be located relative to its previous bounds.
   5234    // We need to offset the pattern by the difference between its new unclipped
   5235    // origin and its previous previous unclipped origin. Then when we finally
   5236    // draw a rectangle at the expected new bounds, it will overlap the portion
   5237    // of the old entry texture we actually need to sample from.
   5238    Point offset =
   5239        (realOrigin - entry->GetOrigin()) + entry->GetBounds().TopLeft();
   5240    SurfacePattern pathPattern(nullptr, ExtendMode::CLAMP,
   5241                               Matrix::Translation(offset), filter);
   5242    return DrawRectAccel(quantBounds, pathPattern, aOptions, shadowColor,
   5243                         &handle, false, true, true);
   5244  }
   5245 
   5246  if (mPathVertexCapacity > 0 && !handle && entry && !aShadow &&
   5247      aOptions.mAntialiasMode != AntialiasMode::NONE &&
   5248      entry->GetPath().mPath.num_types <= mPathMaxComplexity) {
   5249    if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
   5250      if (Maybe<SurfacePattern> gradient =
   5251              mCurrentTarget->LinearGradientToSurface(WidenToDouble(bounds),
   5252                                                      aPattern)) {
   5253        if (DrawWGRPath(aPath, intBounds, quantBounds, pathXform, entry,
   5254                        aOptions, aStrokeOptions, aaStrokeMode, gradient.ref(),
   5255                        color)) {
   5256          return true;
   5257        }
   5258      }
   5259    } else if (SupportsPattern(aPattern) &&
   5260               DrawWGRPath(aPath, intBounds, quantBounds, pathXform, entry,
   5261                           aOptions, aStrokeOptions, aaStrokeMode, aPattern,
   5262                           color)) {
   5263      return true;
   5264    }
   5265  }
   5266 
   5267  // If a stroke path covers too much screen area, it is likely that most is
   5268  // empty space in the interior. This usually imposes too high a cost versus
   5269  // just rasterizing without acceleration. Note that AA-Stroke generally
   5270  // produces more acceptable amounts of geometry for larger paths, so we do
   5271  // this heuristic after we attempt AA-Stroke.
   5272  if (aStrokeOptions &&
   5273      intBounds.width * intBounds.height >
   5274          (mViewportSize.width / 2) * (mViewportSize.height / 2)) {
   5275    // Avoid filling cache with empty entries.
   5276    if (entry) {
   5277      entry->Unlink();
   5278    }
   5279    return false;
   5280  }
   5281 
   5282  // If there is a similar shadow entry with a different blur radius that still
   5283  // has a valid input texture cached. The blurred texture can be generated from
   5284  // the previously cached input texture without incurring an upload cost.
   5285  if (accelShadow && entry) {
   5286    if (RefPtr<PathCacheEntry> similarEntry = mPathCache->FindEntry(
   5287            entry->GetPath(), color ? nullptr : &aPattern, aStrokeOptions,
   5288            aaStrokeMode, currentTransform, intBounds, quantizedOrigin,
   5289            kIgnoreSigma, true)) {
   5290      if (RefPtr<TextureHandle> inputHandle =
   5291              similarEntry->GetSecondaryHandle().get()) {
   5292        if (inputHandle->IsValid() &&
   5293            BlurRectAccel(quantBounds, Point(aShadow->mSigma, aShadow->mSigma),
   5294                          nullptr, IntRect(), aOptions, shadowColor,
   5295                          &inputHandle, &handle)) {
   5296          if (entry) {
   5297            entry->Link(handle);
   5298            entry->SetSecondaryHandle(WeakPtr(inputHandle));
   5299          }
   5300          return true;
   5301        }
   5302      }
   5303    }
   5304  }
   5305 
   5306  // If there isn't a valid texture handle, then we need to rasterize the
   5307  // path in a software canvas and upload this to a texture. Solid color
   5308  // patterns will be rendered as a path mask that can then be modulated
   5309  // with any color. Other pattern types have to rasterize the pattern
   5310  // directly into the cached texture.
   5311  handle = nullptr;
   5312  RefPtr<DrawTargetSkia> pathDT = new DrawTargetSkia;
   5313  if (pathDT->Init(intBounds.Size(), color || aShadow
   5314                                         ? SurfaceFormat::A8
   5315                                         : SurfaceFormat::B8G8R8A8)) {
   5316    Point offset = -quantBounds.TopLeft();
   5317    if (aShadow) {
   5318      // Ensure the the shadow is drawn at the requested offset
   5319      offset += aShadow->mOffset;
   5320    }
   5321    DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER,
   5322                            aOptions.mAntialiasMode);
   5323    static const ColorPattern maskPattern(DeviceColor(1.0f, 1.0f, 1.0f, 1.0f));
   5324    const Pattern& cachePattern = color ? maskPattern : aPattern;
   5325    // If the source pattern is a DrawTargetWebgl snapshot, we may shift
   5326    // targets when drawing the path, so back up the old target.
   5327    DrawTargetWebgl* oldTarget = mCurrentTarget;
   5328    RefPtr<TextureHandle> oldHandle = mTargetHandle;
   5329    IntSize oldViewport = mViewportSize;
   5330    {
   5331      RefPtr<const Path> path;
   5332      if (!aPathXform || (color && !aStrokeOptions)) {
   5333        // If the pattern is transform invariant or there is no pathXform, then
   5334        // it is safe to use the path directly. Solid colors are transform
   5335        // invariant, except when there are stroke options such as line width or
   5336        // dashes that should not be scaled by pathXform.
   5337        path = aPath;
   5338        pathDT->SetTransform(pathXform * Matrix::Translation(offset));
   5339      } else {
   5340        // If there is a pathXform, then pre-apply that to the path to avoid
   5341        // altering the pattern.
   5342        RefPtr<PathBuilder> builder =
   5343            aPath->TransformedCopyToBuilder(*aPathXform);
   5344        path = builder->Finish();
   5345        pathDT->SetTransform(currentTransform * Matrix::Translation(offset));
   5346      }
   5347      if (aStrokeOptions) {
   5348        pathDT->Stroke(path, cachePattern, *aStrokeOptions, drawOptions);
   5349      } else {
   5350        pathDT->Fill(path, cachePattern, drawOptions);
   5351      }
   5352    }
   5353    if (aShadow && aShadow->mSigma > 0.0f) {
   5354      if (accelShadow) {
   5355        RefPtr<SourceSurface> pathSurface = pathDT->Snapshot();
   5356        // If the target changed, try to restore it.
   5357        if ((mCurrentTarget == oldTarget && mTargetHandle == oldHandle &&
   5358             mViewportSize == oldViewport) ||
   5359            oldTarget->PrepareContext(!oldHandle, oldHandle, oldViewport)) {
   5360          RefPtr<TextureHandle> inputHandle;
   5361          // Generate the accelerated shadow from the software surface.
   5362          if (BlurRectAccel(quantBounds,
   5363                            Point(aShadow->mSigma, aShadow->mSigma),
   5364                            pathSurface, IntRect(), aOptions, shadowColor,
   5365                            &inputHandle, &handle)) {
   5366            if (entry) {
   5367              entry->Link(handle);
   5368              entry->SetSecondaryHandle(WeakPtr(inputHandle));
   5369            }
   5370          } else if (entry) {
   5371            entry->Unlink();
   5372          }
   5373          return true;
   5374        }
   5375        return false;
   5376      }
   5377      // Blur the shadow if required.
   5378      GaussianBlur blur(Point(aShadow->mSigma, aShadow->mSigma));
   5379      pathDT->Blur(blur);
   5380    }
   5381    RefPtr<SourceSurface> pathSurface = pathDT->Snapshot();
   5382    // If the target changed, try to restore it.
   5383    if (pathSurface &&
   5384        ((mCurrentTarget == oldTarget && mTargetHandle == oldHandle &&
   5385          mViewportSize == oldViewport) ||
   5386         oldTarget->PrepareContext(!oldHandle, oldHandle, oldViewport))) {
   5387      SurfacePattern pathPattern(pathSurface, ExtendMode::CLAMP,
   5388                                 Matrix::Translation(quantBounds.TopLeft()),
   5389                                 filter);
   5390      // Try and upload the rasterized path to a texture. If there is a
   5391      // valid texture handle after this, then link it to the entry.
   5392      // Otherwise, we might have to fall back to software drawing the
   5393      // path, so unlink it from the entry.
   5394      if (DrawRectAccel(quantBounds, pathPattern, aOptions, shadowColor,
   5395                        &handle, false, true) &&
   5396          handle) {
   5397        if (entry) {
   5398          entry->Link(handle);
   5399        }
   5400      } else if (entry) {
   5401        entry->Unlink();
   5402      }
   5403      return true;
   5404    }
   5405  }
   5406 
   5407  // Avoid filling cache with empty entries.
   5408  if (entry) {
   5409    entry->Unlink();
   5410  }
   5411  return false;
   5412 }
   5413 
   5414 void DrawTargetWebgl::DrawPath(const Path* aPath, const Pattern& aPattern,
   5415                               const DrawOptions& aOptions,
   5416                               const StrokeOptions* aStrokeOptions,
   5417                               bool aAllowStrokeAlpha) {
   5418  // If there is a WebGL context, then try to cache the path to avoid slow
   5419  // fallbacks.
   5420  if (ShouldAccelPath(aOptions, aStrokeOptions) &&
   5421      mSharedContext->DrawPathAccel(aPath, aPattern, aOptions, aStrokeOptions,
   5422                                    aAllowStrokeAlpha)) {
   5423    return;
   5424  }
   5425 
   5426  // There was no path cache entry available to use, so fall back to drawing the
   5427  // path with Skia.
   5428  MarkSkiaChanged(aOptions);
   5429  if (aStrokeOptions) {
   5430    mSkia->Stroke(aPath, aPattern, *aStrokeOptions, aOptions);
   5431  } else {
   5432    mSkia->Fill(aPath, aPattern, aOptions);
   5433  }
   5434 }
   5435 
   5436 // DrawCircleAccel is a more specialized version of DrawPathAccel that attempts
   5437 // to cache a unit circle.
   5438 bool SharedContextWebgl::DrawCircleAccel(const Point& aCenter, float aRadius,
   5439                                         const Pattern& aPattern,
   5440                                         const DrawOptions& aOptions,
   5441                                         const StrokeOptions* aStrokeOptions) {
   5442  // Cache a unit circle and transform it to avoid creating a path repeatedly.
   5443  if (!mUnitCirclePath) {
   5444    mUnitCirclePath = MakePathForCircle(*mCurrentTarget, Point(0, 0), 1);
   5445  }
   5446  // Scale and translate the circle to the desired shape.
   5447  Matrix circleXform(aRadius, 0, 0, aRadius, aCenter.x, aCenter.y);
   5448  return DrawPathAccel(mUnitCirclePath, aPattern, aOptions, aStrokeOptions,
   5449                       true, nullptr, true, &circleXform);
   5450 }
   5451 
   5452 void DrawTargetWebgl::DrawCircle(const Point& aOrigin, float aRadius,
   5453                                 const Pattern& aPattern,
   5454                                 const DrawOptions& aOptions,
   5455                                 const StrokeOptions* aStrokeOptions) {
   5456  if (ShouldAccelPath(aOptions, aStrokeOptions) &&
   5457      mSharedContext->DrawCircleAccel(aOrigin, aRadius, aPattern, aOptions,
   5458                                      aStrokeOptions)) {
   5459    return;
   5460  }
   5461 
   5462  MarkSkiaChanged(aOptions);
   5463  if (aStrokeOptions) {
   5464    mSkia->StrokeCircle(aOrigin, aRadius, aPattern, *aStrokeOptions, aOptions);
   5465  } else {
   5466    mSkia->FillCircle(aOrigin, aRadius, aPattern, aOptions);
   5467  }
   5468 }
   5469 
   5470 void DrawTargetWebgl::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
   5471                                  const Rect& aSource,
   5472                                  const DrawSurfaceOptions& aSurfOptions,
   5473                                  const DrawOptions& aOptions) {
   5474  Matrix matrix = Matrix::Scaling(aDest.width / aSource.width,
   5475                                  aDest.height / aSource.height);
   5476  matrix.PreTranslate(-aSource.TopLeft());
   5477  matrix.PostTranslate(aDest.TopLeft());
   5478 
   5479  // Ensure the source rect is clipped to the surface bounds.
   5480  Rect src = aSource.Intersect(Rect(aSurface->GetRect()));
   5481  // Ensure the destination rect does not sample outside the surface bounds.
   5482  Rect dest = matrix.TransformBounds(src).Intersect(aDest);
   5483  SurfacePattern pattern(aSurface, ExtendMode::CLAMP, matrix,
   5484                         aSurfOptions.mSamplingFilter);
   5485  DrawRect(dest, pattern, aOptions);
   5486 }
   5487 
   5488 void DrawTargetWebgl::Mask(const Pattern& aSource, const Pattern& aMask,
   5489                           const DrawOptions& aOptions) {
   5490  if (!SupportsDrawOptions(aOptions) ||
   5491      aMask.GetType() != PatternType::SURFACE ||
   5492      aSource.GetType() != PatternType::COLOR) {
   5493    MarkSkiaChanged(aOptions);
   5494    mSkia->Mask(aSource, aMask, aOptions);
   5495    return;
   5496  }
   5497  auto sourceColor = static_cast<const ColorPattern&>(aSource).mColor;
   5498  auto maskPattern = static_cast<const SurfacePattern&>(aMask);
   5499  if (!maskPattern.mSurface) {
   5500    return;
   5501  }
   5502 
   5503  IntRect samplingRect = !maskPattern.mSamplingRect.IsEmpty()
   5504                             ? maskPattern.mSamplingRect
   5505                             : maskPattern.mSurface->GetRect();
   5506  DrawRect(maskPattern.mMatrix.TransformBounds(Rect(samplingRect)), maskPattern,
   5507           aOptions, Some(sourceColor));
   5508 }
   5509 
   5510 void DrawTargetWebgl::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
   5511                                  Point aOffset, const DrawOptions& aOptions) {
   5512  if (!SupportsDrawOptions(aOptions) ||
   5513      aSource.GetType() != PatternType::COLOR) {
   5514    MarkSkiaChanged(aOptions);
   5515    mSkia->MaskSurface(aSource, aMask, aOffset, aOptions);
   5516  } else {
   5517    auto sourceColor = static_cast<const ColorPattern&>(aSource).mColor;
   5518    SurfacePattern pattern(
   5519        aMask, ExtendMode::CLAMP,
   5520        Matrix::Translation(aOffset + aMask->GetRect().TopLeft()));
   5521    DrawRect(Rect(aOffset, Size(aMask->GetSize())), pattern, aOptions,
   5522             Some(sourceColor));
   5523  }
   5524 }
   5525 
   5526 // Extract the surface's alpha values into an A8 surface.
   5527 static already_AddRefed<DataSourceSurface> ExtractAlpha(SourceSurface* aSurface,
   5528                                                        bool aAllowSubpixelAA) {
   5529  RefPtr<DataSourceSurface> surfaceData = aSurface->GetDataSurface();
   5530  if (!surfaceData) {
   5531    return nullptr;
   5532  }
   5533  DataSourceSurface::ScopedMap srcMap(surfaceData, DataSourceSurface::READ);
   5534  if (!srcMap.IsMapped()) {
   5535    return nullptr;
   5536  }
   5537  IntSize size = surfaceData->GetSize();
   5538  RefPtr<DataSourceSurface> alpha =
   5539      Factory::CreateDataSourceSurface(size, SurfaceFormat::A8, false);
   5540  if (!alpha) {
   5541    return nullptr;
   5542  }
   5543  DataSourceSurface::ScopedMap dstMap(alpha, DataSourceSurface::WRITE);
   5544  if (!dstMap.IsMapped()) {
   5545    return nullptr;
   5546  }
   5547  // For subpixel masks, ignore the alpha and instead sample one of the color
   5548  // channels as if they were alpha.
   5549  SwizzleData(
   5550      srcMap.GetData(), srcMap.GetStride(),
   5551      aAllowSubpixelAA ? SurfaceFormat::A8R8G8B8 : surfaceData->GetFormat(),
   5552      dstMap.GetData(), dstMap.GetStride(), SurfaceFormat::A8, size);
   5553  return alpha.forget();
   5554 }
   5555 
   5556 void DrawTargetWebgl::DrawShadow(const Path* aPath, const Pattern& aPattern,
   5557                                 const ShadowOptions& aShadow,
   5558                                 const DrawOptions& aOptions,
   5559                                 const StrokeOptions* aStrokeOptions) {
   5560  if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
   5561    return;
   5562  }
   5563 
   5564  // If there is a WebGL context, then try to cache the path to avoid slow
   5565  // fallbacks.
   5566  if (ShouldAccelPath(aOptions, aStrokeOptions) &&
   5567      mSharedContext->DrawPathAccel(aPath, aPattern, aOptions, aStrokeOptions,
   5568                                    false, &aShadow)) {
   5569    return;
   5570  }
   5571 
   5572  // There was no path cache entry available to use, so fall back to drawing the
   5573  // path with Skia.
   5574  MarkSkiaChanged(aOptions);
   5575  mSkia->DrawShadow(aPath, aPattern, aShadow, aOptions, aStrokeOptions);
   5576 }
   5577 
   5578 void DrawTargetWebgl::DrawSurfaceWithShadow(SourceSurface* aSurface,
   5579                                            const Point& aDest,
   5580                                            const ShadowOptions& aShadow,
   5581                                            CompositionOp aOperator) {
   5582  DrawOptions options(1.0f, aOperator);
   5583  if (ShouldAccelPath(options, nullptr)) {
   5584    SurfacePattern pattern(aSurface, ExtendMode::CLAMP,
   5585                           Matrix::Translation(aDest));
   5586    SkPath skiaPath;
   5587    skiaPath.addRect(RectToSkRect(Rect(aSurface->GetRect()) + aDest));
   5588    RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
   5589    AutoRestoreTransform restore(this);
   5590    SetTransform(Matrix());
   5591    if (mSharedContext->DrawPathAccel(path, pattern, options, nullptr, false,
   5592                                      &aShadow, false)) {
   5593      DrawRect(Rect(aSurface->GetRect()) + aDest, pattern, options);
   5594      return;
   5595    }
   5596  }
   5597 
   5598  MarkSkiaChanged(options);
   5599  mSkia->DrawSurfaceWithShadow(aSurface, aDest, aShadow, aOperator);
   5600 }
   5601 
   5602 already_AddRefed<PathBuilder> DrawTargetWebgl::CreatePathBuilder(
   5603    FillRule aFillRule) const {
   5604  return mSkia->CreatePathBuilder(aFillRule);
   5605 }
   5606 
   5607 void DrawTargetWebgl::SetTransform(const Matrix& aTransform) {
   5608  DrawTarget::SetTransform(aTransform);
   5609  mSkia->SetTransform(aTransform);
   5610 }
   5611 
   5612 void DrawTargetWebgl::StrokeRect(const Rect& aRect, const Pattern& aPattern,
   5613                                 const StrokeOptions& aStrokeOptions,
   5614                                 const DrawOptions& aOptions) {
   5615  if (!mWebglValid) {
   5616    MarkSkiaChanged(aOptions);
   5617    mSkia->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
   5618  } else {
   5619    // If the stroke options are unsupported, then transform the rect to a path
   5620    // so it can be cached.
   5621    SkPath skiaPath;
   5622    skiaPath.addRect(RectToSkRect(aRect));
   5623    RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
   5624    DrawPath(path, aPattern, aOptions, &aStrokeOptions, true);
   5625  }
   5626 }
   5627 
   5628 static inline bool IsThinLine(const Matrix& aTransform,
   5629                              const StrokeOptions& aStrokeOptions) {
   5630  auto scale = aTransform.ScaleFactors();
   5631  return std::max(scale.xScale, scale.yScale) * aStrokeOptions.mLineWidth <= 1;
   5632 }
   5633 
   5634 bool DrawTargetWebgl::StrokeLineAccel(const Point& aStart, const Point& aEnd,
   5635                                      const Pattern& aPattern,
   5636                                      const StrokeOptions& aStrokeOptions,
   5637                                      const DrawOptions& aOptions,
   5638                                      bool aClosed) {
   5639  // Approximating a wide line as a rectangle works only with certain cap styles
   5640  // in the general case (butt or square). However, if the line width is
   5641  // sufficiently thin, we can either ignore the round cap (or treat it like
   5642  // square for zero-length lines) without causing objectionable artifacts.
   5643  // Lines may sometimes be used in closed paths that immediately reverse back,
   5644  // in which case we need to use mLineJoin instead of mLineCap to determine the
   5645  // actual cap used.
   5646  CapStyle capStyle =
   5647      aClosed ? (aStrokeOptions.mLineJoin == JoinStyle::ROUND ? CapStyle::ROUND
   5648                                                              : CapStyle::BUTT)
   5649              : aStrokeOptions.mLineCap;
   5650  if (mWebglValid && SupportsPattern(aPattern) &&
   5651      (capStyle != CapStyle::ROUND ||
   5652       IsThinLine(GetTransform(), aStrokeOptions)) &&
   5653      aStrokeOptions.mDashPattern == nullptr && aStrokeOptions.mLineWidth > 0) {
   5654    // Treat the line as a rectangle whose center-line is the supplied line and
   5655    // for which the height is the supplied line width. Generate a matrix that
   5656    // maps the X axis to the orientation of the line and the Y axis to the
   5657    // normal vector to the line. This only works if the line caps are squared,
   5658    // as rounded rectangles are currently not supported for round line caps.
   5659    Point start = aStart;
   5660    Point dirX = aEnd - aStart;
   5661    Point dirY;
   5662    float dirLen = dirX.Length();
   5663    float scale = aStrokeOptions.mLineWidth;
   5664    if (dirLen == 0.0f) {
   5665      // If the line is zero-length, then only a cap is rendered.
   5666      switch (capStyle) {
   5667        case CapStyle::BUTT:
   5668          // The cap doesn't extend beyond the line so nothing is drawn.
   5669          return true;
   5670        case CapStyle::ROUND:
   5671        case CapStyle::SQUARE:
   5672          // Draw a unit square centered at the single point.
   5673          dirX = Point(scale, 0.0f);
   5674          dirY = Point(0.0f, scale);
   5675          // Offset the start by half a unit.
   5676          start.x -= 0.5f * scale;
   5677          break;
   5678      }
   5679    } else {
   5680      // Make the scale map to a single unit length.
   5681      scale /= dirLen;
   5682      dirY = Point(-dirX.y, dirX.x) * scale;
   5683      if (capStyle == CapStyle::SQUARE) {
   5684        // Offset the start by half a unit.
   5685        start -= (dirX * scale) * 0.5f;
   5686        // Ensure the extent also accounts for the start and end cap.
   5687        dirX += dirX * scale;
   5688      }
   5689    }
   5690    Matrix lineXform(dirX.x, dirX.y, dirY.x, dirY.y, start.x - 0.5f * dirY.x,
   5691                     start.y - 0.5f * dirY.y);
   5692    if (PrepareContext() &&
   5693        mSharedContext->DrawRectAccel(Rect(0, 0, 1, 1), aPattern, aOptions,
   5694                                      Nothing(), nullptr, true, true, true,
   5695                                      false, nullptr, nullptr, &lineXform)) {
   5696      return true;
   5697    }
   5698  }
   5699  return false;
   5700 }
   5701 
   5702 void DrawTargetWebgl::StrokeLine(const Point& aStart, const Point& aEnd,
   5703                                 const Pattern& aPattern,
   5704                                 const StrokeOptions& aStrokeOptions,
   5705                                 const DrawOptions& aOptions) {
   5706  if (!mWebglValid) {
   5707    MarkSkiaChanged(aOptions);
   5708    mSkia->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
   5709  } else if (!StrokeLineAccel(aStart, aEnd, aPattern, aStrokeOptions,
   5710                              aOptions)) {
   5711    // If the stroke options are unsupported, then transform the line to a path
   5712    // so it can be cached.
   5713    SkPath skiaPath;
   5714    skiaPath.moveTo(PointToSkPoint(aStart));
   5715    skiaPath.lineTo(PointToSkPoint(aEnd));
   5716    RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
   5717    DrawPath(path, aPattern, aOptions, &aStrokeOptions, true);
   5718  }
   5719 }
   5720 
   5721 void DrawTargetWebgl::Stroke(const Path* aPath, const Pattern& aPattern,
   5722                             const StrokeOptions& aStrokeOptions,
   5723                             const DrawOptions& aOptions) {
   5724  if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
   5725    return;
   5726  }
   5727  const auto& skiaPath = static_cast<const PathSkia*>(aPath)->GetPath();
   5728  if (!mWebglValid) {
   5729    MarkSkiaChanged(aOptions);
   5730    mSkia->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
   5731    return;
   5732  }
   5733 
   5734  // Avoid using Skia's isLine here because some paths erroneously include a
   5735  // closePath at the end, causing isLine to not detect the line. In that case
   5736  // we just draw a line in reverse right over the original line.
   5737  int numVerbs = skiaPath.countVerbs();
   5738  bool allowStrokeAlpha = false;
   5739  if (numVerbs >= 2 && numVerbs <= 3) {
   5740    uint8_t verbs[3];
   5741    skiaPath.getVerbs({verbs, numVerbs});
   5742    if (verbs[0] == SkPath::kMove_Verb && verbs[1] == SkPath::kLine_Verb &&
   5743        (numVerbs < 3 || verbs[2] == SkPath::kClose_Verb)) {
   5744      bool closed = numVerbs >= 3;
   5745      Point start = SkPointToPoint(skiaPath.getPoint(0));
   5746      Point end = SkPointToPoint(skiaPath.getPoint(1));
   5747      if (StrokeLineAccel(start, end, aPattern, aStrokeOptions, aOptions,
   5748                          closed)) {
   5749        if (closed) {
   5750          StrokeLineAccel(end, start, aPattern, aStrokeOptions, aOptions, true);
   5751        }
   5752        return;
   5753      }
   5754      // If accelerated line drawing failed, just treat it as a path.
   5755      allowStrokeAlpha = true;
   5756    }
   5757  }
   5758 
   5759  DrawPath(aPath, aPattern, aOptions, &aStrokeOptions, allowStrokeAlpha);
   5760 }
   5761 
   5762 void DrawTargetWebgl::StrokeCircle(const Point& aOrigin, float aRadius,
   5763                                   const Pattern& aPattern,
   5764                                   const StrokeOptions& aStrokeOptions,
   5765                                   const DrawOptions& aOptions) {
   5766  DrawCircle(aOrigin, aRadius, aPattern, aOptions, &aStrokeOptions);
   5767 }
   5768 
   5769 bool DrawTargetWebgl::ShouldUseSubpixelAA(ScaledFont* aFont,
   5770                                          const DrawOptions& aOptions) {
   5771  AntialiasMode aaMode = aFont->GetDefaultAAMode();
   5772  if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
   5773    aaMode = aOptions.mAntialiasMode;
   5774  }
   5775  return GetPermitSubpixelAA() &&
   5776         (aaMode == AntialiasMode::DEFAULT ||
   5777          aaMode == AntialiasMode::SUBPIXEL) &&
   5778         aOptions.mCompositionOp == CompositionOp::OP_OVER;
   5779 }
   5780 
   5781 void DrawTargetWebgl::StrokeGlyphs(ScaledFont* aFont,
   5782                                   const GlyphBuffer& aBuffer,
   5783                                   const Pattern& aPattern,
   5784                                   const StrokeOptions& aStrokeOptions,
   5785                                   const DrawOptions& aOptions) {
   5786  if (!aFont || !aBuffer.mNumGlyphs) {
   5787    return;
   5788  }
   5789 
   5790  bool useSubpixelAA = ShouldUseSubpixelAA(aFont, aOptions);
   5791 
   5792  if (mWebglValid && SupportsDrawOptions(aOptions) &&
   5793      aPattern.GetType() == PatternType::COLOR && PrepareContext() &&
   5794      mSharedContext->DrawGlyphsAccel(aFont, aBuffer, aPattern, aOptions,
   5795                                      &aStrokeOptions, useSubpixelAA)) {
   5796    return;
   5797  }
   5798 
   5799  if (useSubpixelAA) {
   5800    // Subpixel AA does not support layering because the subpixel masks can't
   5801    // blend with the over op.
   5802    MarkSkiaChanged();
   5803  } else {
   5804    MarkSkiaChanged(aOptions);
   5805  }
   5806  mSkia->StrokeGlyphs(aFont, aBuffer, aPattern, aStrokeOptions, aOptions);
   5807 }
   5808 
   5809 // Depending on whether we enable subpixel position for a given font, Skia may
   5810 // round transformed coordinates differently on each axis. By default, text is
   5811 // subpixel quantized horizontally and snapped to a whole integer vertical
   5812 // baseline. Axis-flip transforms instead snap to horizontal boundaries while
   5813 // subpixel quantizing along the vertical. For other types of transforms, Skia
   5814 // just applies subpixel quantization to both axes.
   5815 // We must duplicate the amount of quantization Skia applies carefully as a
   5816 // boundary value such as 0.49 may round to 0.5 with subpixel quantization,
   5817 // but if Skia actually snapped it to a whole integer instead, it would round
   5818 // down to 0. If a subsequent glyph with offset 0.51 came in, we might
   5819 // mistakenly round it down to 0.5, whereas Skia would round it up to 1. Thus
   5820 // we would alias 0.49 and 0.51 to the same cache entry, while Skia would
   5821 // actually snap the offset to 0 or 1, depending, resulting in mismatched
   5822 // hinting.
   5823 static inline IntPoint QuantizeScale(ScaledFont* aFont,
   5824                                     const Matrix& aTransform) {
   5825  if (!aFont->UseSubpixelPosition()) {
   5826    return {1, 1};
   5827  }
   5828  if (aTransform._12 == 0) {
   5829    // Glyphs are rendered subpixel horizontally, so snap vertically.
   5830    return {4, 1};
   5831  }
   5832  if (aTransform._11 == 0) {
   5833    // Glyphs are rendered subpixel vertically, so snap horizontally.
   5834    return {1, 4};
   5835  }
   5836  // The transform isn't aligned, so don't snap.
   5837  return {4, 4};
   5838 }
   5839 
   5840 // Skia only supports subpixel positioning to the nearest 1/4 fraction. It
   5841 // would be wasteful to attempt to cache text runs with positioning that is
   5842 // anymore precise than this. To prevent this cache bloat, we quantize the
   5843 // transformed glyph positions to the nearest 1/4. The scaling factor for
   5844 // the quantization is baked into the transform, so that if subpixel rounding
   5845 // is used on a given axis, then the axis will be multiplied by 4 before
   5846 // rounding. Since the quantized position is not used for rasterization, the
   5847 // transform is safe to modify as such.
   5848 static inline IntPoint QuantizePosition(const Matrix& aTransform,
   5849                                        const IntPoint& aOffset,
   5850                                        const Point& aPosition) {
   5851  return RoundedToInt(aTransform.TransformPoint(aPosition)) - aOffset;
   5852 }
   5853 
   5854 // Get a quantized starting offset for the glyph buffer. We want this offset
   5855 // to encapsulate the transform and buffer offset while still preserving the
   5856 // relative subpixel positions of the glyphs this offset is subtracted from.
   5857 static inline IntPoint QuantizeOffset(const Matrix& aTransform,
   5858                                      const IntPoint& aQuantizeScale,
   5859                                      const GlyphBuffer& aBuffer) {
   5860  IntPoint offset =
   5861      RoundedToInt(aTransform.TransformPoint(aBuffer.mGlyphs[0].mPosition));
   5862  offset.x.value &= ~(aQuantizeScale.x.value - 1);
   5863  offset.y.value &= ~(aQuantizeScale.y.value - 1);
   5864  return offset;
   5865 }
   5866 
   5867 // Hashes a glyph buffer to a single hash value that can be used for quick
   5868 // comparisons. Each glyph position is transformed and quantized before
   5869 // hashing.
   5870 HashNumber GlyphCacheEntry::HashGlyphs(const GlyphBuffer& aBuffer,
   5871                                       const Matrix& aTransform,
   5872                                       const IntPoint& aQuantizeScale) {
   5873  HashNumber hash = 0;
   5874  IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
   5875  for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
   5876    const Glyph& glyph = aBuffer.mGlyphs[i];
   5877    hash = AddToHash(hash, glyph.mIndex);
   5878    IntPoint pos = QuantizePosition(aTransform, offset, glyph.mPosition);
   5879    hash = AddToHash(hash, pos.x);
   5880    hash = AddToHash(hash, pos.y);
   5881  }
   5882  return hash;
   5883 }
   5884 
   5885 // Determines if an existing glyph cache entry matches an incoming text run.
   5886 inline bool GlyphCacheEntry::MatchesGlyphs(
   5887    const GlyphBuffer& aBuffer, const DeviceColor& aColor,
   5888    const Matrix& aTransform, const IntPoint& aQuantizeOffset,
   5889    const IntPoint& aBoundsOffset, const IntRect& aClipRect, HashNumber aHash,
   5890    const StrokeOptions* aStrokeOptions) {
   5891  // First check if the hash matches to quickly reject the text run before any
   5892  // more expensive checking. If it matches, then check if the color and
   5893  // transform are the same.
   5894  if (aHash != mHash || aBuffer.mNumGlyphs != mBuffer.mNumGlyphs ||
   5895      aColor != mColor || !HasMatchingScale(aTransform, mTransform)) {
   5896    return false;
   5897  }
   5898  // Finally check if all glyphs and their quantized positions match.
   5899  for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
   5900    const Glyph& dst = mBuffer.mGlyphs[i];
   5901    const Glyph& src = aBuffer.mGlyphs[i];
   5902    if (dst.mIndex != src.mIndex ||
   5903        dst.mPosition != Point(QuantizePosition(aTransform, aQuantizeOffset,
   5904                                                src.mPosition))) {
   5905      return false;
   5906    }
   5907  }
   5908  // Check that stroke options actually match.
   5909  if (aStrokeOptions) {
   5910    // If stroking, verify that the entry is also stroked with the same options.
   5911    if (!(mStrokeOptions && *aStrokeOptions == *mStrokeOptions)) {
   5912      return false;
   5913    }
   5914  } else if (mStrokeOptions) {
   5915    // If not stroking, check if the entry is stroked. If so, don't match.
   5916    return false;
   5917  }
   5918  // Verify that the full bounds, once translated and clipped, are equal to the
   5919  // clipped bounds.
   5920  return (mFullBounds + aBoundsOffset)
   5921      .Intersect(aClipRect)
   5922      .IsEqualEdges(GetBounds() + aBoundsOffset);
   5923 }
   5924 
   5925 GlyphCacheEntry::GlyphCacheEntry(const GlyphBuffer& aBuffer,
   5926                                 const DeviceColor& aColor,
   5927                                 const Matrix& aTransform,
   5928                                 const IntPoint& aQuantizeScale,
   5929                                 const IntRect& aBounds,
   5930                                 const IntRect& aFullBounds, HashNumber aHash,
   5931                                 StoredStrokeOptions* aStrokeOptions)
   5932    : CacheEntryImpl<GlyphCacheEntry>(aTransform, aBounds, aHash),
   5933      mColor(aColor),
   5934      mFullBounds(aFullBounds),
   5935      mStrokeOptions(aStrokeOptions) {
   5936  // Store a copy of the glyph buffer with positions already quantized for fast
   5937  // comparison later.
   5938  Glyph* glyphs = new Glyph[aBuffer.mNumGlyphs];
   5939  IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
   5940  // Make the bounds relative to the offset so we can add a new offset later.
   5941  IntPoint boundsOffset(offset.x / aQuantizeScale.x,
   5942                        offset.y / aQuantizeScale.y);
   5943  mBounds -= boundsOffset;
   5944  mFullBounds -= boundsOffset;
   5945  for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
   5946    Glyph& dst = glyphs[i];
   5947    const Glyph& src = aBuffer.mGlyphs[i];
   5948    dst.mIndex = src.mIndex;
   5949    dst.mPosition = Point(QuantizePosition(aTransform, offset, src.mPosition));
   5950  }
   5951  mBuffer.mGlyphs = glyphs;
   5952  mBuffer.mNumGlyphs = aBuffer.mNumGlyphs;
   5953 }
   5954 
   5955 GlyphCacheEntry::~GlyphCacheEntry() { delete[] mBuffer.mGlyphs; }
   5956 
   5957 // Attempt to find a matching entry in the glyph cache. The caller should check
   5958 // whether the contained texture handle is valid to determine if it will need to
   5959 // render the text run or just reuse the cached texture.
   5960 already_AddRefed<GlyphCacheEntry> GlyphCache::FindEntry(
   5961    const GlyphBuffer& aBuffer, const DeviceColor& aColor,
   5962    const Matrix& aTransform, const IntPoint& aQuantizeScale,
   5963    const IntRect& aClipRect, HashNumber aHash,
   5964    const StrokeOptions* aStrokeOptions) {
   5965  IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
   5966  IntPoint boundsOffset(offset.x / aQuantizeScale.x,
   5967                        offset.y / aQuantizeScale.y);
   5968  for (const RefPtr<GlyphCacheEntry>& entry : GetChain(aHash)) {
   5969    if (entry->MatchesGlyphs(aBuffer, aColor, aTransform, offset, boundsOffset,
   5970                             aClipRect, aHash, aStrokeOptions)) {
   5971      return do_AddRef(entry);
   5972    }
   5973  }
   5974  return nullptr;
   5975 }
   5976 
   5977 // Insert a new entry in the glyph cache.
   5978 already_AddRefed<GlyphCacheEntry> GlyphCache::InsertEntry(
   5979    const GlyphBuffer& aBuffer, const DeviceColor& aColor,
   5980    const Matrix& aTransform, const IntPoint& aQuantizeScale,
   5981    const IntRect& aBounds, const IntRect& aFullBounds, HashNumber aHash,
   5982    const StrokeOptions* aStrokeOptions) {
   5983  StoredStrokeOptions* strokeOptions = nullptr;
   5984  if (aStrokeOptions) {
   5985    strokeOptions = aStrokeOptions->Clone();
   5986    if (!strokeOptions) {
   5987      return nullptr;
   5988    }
   5989  }
   5990  RefPtr<GlyphCacheEntry> entry =
   5991      new GlyphCacheEntry(aBuffer, aColor, aTransform, aQuantizeScale, aBounds,
   5992                          aFullBounds, aHash, strokeOptions);
   5993  Insert(entry);
   5994  return entry.forget();
   5995 }
   5996 
   5997 GlyphCache::GlyphCache(ScaledFont* aFont) : mFont(aFont) {}
   5998 
   5999 static void ReleaseGlyphCache(void* aPtr) {
   6000  delete static_cast<GlyphCache*>(aPtr);
   6001 }
   6002 
   6003 // Whether all glyphs in the buffer match the last whitespace glyph queried.
   6004 bool GlyphCache::IsWhitespace(const GlyphBuffer& aBuffer) const {
   6005  if (!mLastWhitespace) {
   6006    return false;
   6007  }
   6008  uint32_t whitespace = *mLastWhitespace;
   6009  for (size_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
   6010    if (aBuffer.mGlyphs[i].mIndex != whitespace) {
   6011      return false;
   6012    }
   6013  }
   6014  return true;
   6015 }
   6016 
   6017 // Remember the last whitespace glyph seen.
   6018 void GlyphCache::SetLastWhitespace(const GlyphBuffer& aBuffer) {
   6019  mLastWhitespace = Some(aBuffer.mGlyphs[0].mIndex);
   6020 }
   6021 
   6022 void DrawTargetWebgl::SetPermitSubpixelAA(bool aPermitSubpixelAA) {
   6023  DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
   6024  mSkia->SetPermitSubpixelAA(aPermitSubpixelAA);
   6025 }
   6026 
   6027 // Check for any color glyphs contained within a rasterized BGRA8 text result.
   6028 static bool CheckForColorGlyphs(const RefPtr<SourceSurface>& aSurface) {
   6029  if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
   6030    return false;
   6031  }
   6032  RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
   6033  if (!dataSurf) {
   6034    return true;
   6035  }
   6036  DataSourceSurface::ScopedMap map(dataSurf, DataSourceSurface::READ);
   6037  if (!map.IsMapped()) {
   6038    return true;
   6039  }
   6040  IntSize size = dataSurf->GetSize();
   6041  const uint8_t* data = map.GetData();
   6042  int32_t stride = map.GetStride();
   6043  for (int y = 0; y < size.height; y++) {
   6044    const uint32_t* x = (const uint32_t*)data;
   6045    const uint32_t* end = x + size.width;
   6046    for (; x < end; x++) {
   6047      // Verify if all components are the same as for premultiplied grayscale.
   6048      uint32_t color = *x;
   6049      uint32_t gray = color & 0xFF;
   6050      gray |= gray << 8;
   6051      gray |= gray << 16;
   6052      if (color != gray) return true;
   6053    }
   6054    data += stride;
   6055  }
   6056  return false;
   6057 }
   6058 
   6059 // Quantize the preblend color used to key the cache, as only the high bits are
   6060 // used to determine the amount of preblending. This avoids excessive cache use.
   6061 // This roughly matches the quantization used in WebRender and Skia.
   6062 static DeviceColor QuantizePreblendColor(const DeviceColor& aColor,
   6063                                         bool aUseSubpixelAA) {
   6064  int32_t r = int32_t(aColor.r * 255.0f + 0.5f);
   6065  int32_t g = int32_t(aColor.g * 255.0f + 0.5f);
   6066  int32_t b = int32_t(aColor.b * 255.0f + 0.5f);
   6067  // Skia only uses the high 3 bits of each color component to cache preblend
   6068  // ramp tables.
   6069  constexpr int32_t lumBits = 3;
   6070  constexpr int32_t floorMask = ((1 << lumBits) - 1) << (8 - lumBits);
   6071  if (!aUseSubpixelAA) {
   6072    // If not using subpixel AA, then quantize only the luminance, stored in the
   6073    // G channel.
   6074    g = (r * 54 + g * 183 + b * 19) >> 8;
   6075    g &= floorMask;
   6076    r = g;
   6077    b = g;
   6078  } else {
   6079    r &= floorMask;
   6080    g &= floorMask;
   6081    b &= floorMask;
   6082  }
   6083  return DeviceColor{r / 255.0f, g / 255.0f, b / 255.0f, 1.0f};
   6084 }
   6085 
   6086 // Draws glyphs to the WebGL target by trying to generate a cached texture for
   6087 // the text run that can be subsequently reused to quickly render the text run
   6088 // without using any software surfaces.
   6089 bool SharedContextWebgl::DrawGlyphsAccel(ScaledFont* aFont,
   6090                                         const GlyphBuffer& aBuffer,
   6091                                         const Pattern& aPattern,
   6092                                         const DrawOptions& aOptions,
   6093                                         const StrokeOptions* aStrokeOptions,
   6094                                         bool aUseSubpixelAA) {
   6095  // Look for an existing glyph cache on the font. If not there, create it.
   6096  GlyphCache* cache =
   6097      static_cast<GlyphCache*>(aFont->GetUserData(&mGlyphCacheKey));
   6098  if (!cache) {
   6099    cache = new GlyphCache(aFont);
   6100    aFont->AddUserData(&mGlyphCacheKey, cache, ReleaseGlyphCache);
   6101    mGlyphCaches.insertFront(cache);
   6102  }
   6103 
   6104  // Check if the buffer contains non-renderable whitespace characters before
   6105  // any other expensive checks.
   6106  if (cache->IsWhitespace(aBuffer)) {
   6107    return true;
   6108  }
   6109 
   6110  // Whether the font may use bitmaps. If so, we need to render the glyphs with
   6111  // color as grayscale bitmaps will use the color while color emoji will not,
   6112  // with no easy way to know ahead of time. We currently have to check the
   6113  // rasterized result to see if there are any color glyphs. To render subpixel
   6114  // masks, we need to know that the rasterized result actually represents a
   6115  // subpixel mask rather than try to interpret it as a normal RGBA result such
   6116  // as for color emoji.
   6117  bool useBitmaps = !aStrokeOptions && aFont->MayUseBitmaps() &&
   6118                    aOptions.mCompositionOp != CompositionOp::OP_CLEAR;
   6119  // Hash the incoming text run and looking for a matching entry.
   6120  DeviceColor color = aOptions.mCompositionOp == CompositionOp::OP_CLEAR
   6121                          ? DeviceColor(1, 1, 1, 1)
   6122                          : static_cast<const ColorPattern&>(aPattern).mColor;
   6123 #if defined(XP_MACOSX)
   6124  // macOS uses gamma-aware blending with font smooth from subpixel AA.
   6125  // If font smoothing is requested, even if there is no subpixel AA, gamma-
   6126  // aware blending might be used and differing amounts of dilation might be
   6127  // applied.
   6128  bool usePreblend = aUseSubpixelAA ||
   6129                     (aFont->GetType() == FontType::MAC &&
   6130                      static_cast<ScaledFontMac*>(aFont)->UseFontSmoothing());
   6131 #elif defined(XP_WIN)
   6132  // Windows uses gamma-aware blending via ClearType with grayscale and subpixel
   6133  // AA.
   6134  bool usePreblend =
   6135      aUseSubpixelAA || aOptions.mAntialiasMode != AntialiasMode::NONE;
   6136 #else
   6137  // FreeType backends currently don't use any preblending.
   6138  bool usePreblend = false;
   6139 #endif
   6140 
   6141  // If the font has bitmaps, use the color directly. Otherwise, the texture
   6142  // holds a grayscale mask, so encode the key's subpixel state in the color.
   6143  const Matrix& currentTransform = mCurrentTarget->GetTransform();
   6144  IntPoint quantizeScale = QuantizeScale(aFont, currentTransform);
   6145  Matrix quantizeTransform = currentTransform;
   6146  quantizeTransform.PostScale(quantizeScale.x, quantizeScale.y);
   6147  HashNumber hash =
   6148      GlyphCacheEntry::HashGlyphs(aBuffer, quantizeTransform, quantizeScale);
   6149  DeviceColor colorOrMask =
   6150      useBitmaps ? color
   6151                 : (usePreblend ? QuantizePreblendColor(color, aUseSubpixelAA)
   6152                                : DeviceColor::Mask(aUseSubpixelAA ? 1 : 0, 1));
   6153  IntRect clipRect(IntPoint(), mViewportSize);
   6154  RefPtr<GlyphCacheEntry> entry =
   6155      cache->FindEntry(aBuffer, colorOrMask, quantizeTransform, quantizeScale,
   6156                       clipRect, hash, aStrokeOptions);
   6157  if (!entry) {
   6158    // For small text runs, bounds computations can be expensive relative to the
   6159    // cost of looking up a cache result. Avoid doing local bounds computations
   6160    // until actually inserting the entry into the cache.
   6161    Maybe<Rect> bounds = mCurrentTarget->mSkia->GetGlyphLocalBounds(
   6162        aFont, aBuffer, aPattern, aStrokeOptions, aOptions);
   6163    if (!bounds) {
   6164      // Assume the buffer is full of whitespace characters that should be
   6165      // remembered for subsequent lookups.
   6166      cache->SetLastWhitespace(aBuffer);
   6167      return true;
   6168    }
   6169    // Transform the local bounds into device space so that we know how big
   6170    // the cached texture will be.
   6171    Rect xformBounds = currentTransform.TransformBounds(*bounds);
   6172    // Check if the transform flattens out the bounds before rounding.
   6173    if (xformBounds.IsEmpty()) {
   6174      return true;
   6175    }
   6176    IntRect fullBounds = RoundedOut(xformBounds);
   6177    IntRect clipBounds = fullBounds.Intersect(clipRect);
   6178    // Check if the bounds are completely clipped out.
   6179    if (clipBounds.IsEmpty()) {
   6180      return true;
   6181    }
   6182    entry = cache->InsertEntry(aBuffer, colorOrMask, quantizeTransform,
   6183                               quantizeScale, clipBounds, fullBounds, hash,
   6184                               aStrokeOptions);
   6185    if (!entry) {
   6186      return false;
   6187    }
   6188  }
   6189 
   6190  // The bounds of the entry may have a different transform offset from the
   6191  // bounds of the currently drawn text run. The entry bounds are relative to
   6192  // the entry's quantized offset already, so just move the bounds to the new
   6193  // offset.
   6194  IntRect intBounds = entry->GetBounds();
   6195  IntPoint newOffset =
   6196      QuantizeOffset(quantizeTransform, quantizeScale, aBuffer);
   6197  intBounds +=
   6198      IntPoint(newOffset.x / quantizeScale.x, newOffset.y / quantizeScale.y);
   6199  // Ensure there is a clear border around the text. This must be applied only
   6200  // after clipping so that we always have some border texels for filtering.
   6201  intBounds.Inflate(2);
   6202 
   6203  RefPtr<TextureHandle> handle = entry->GetHandle();
   6204  if (handle && handle->IsValid()) {
   6205    // If there is an entry with a valid cached texture handle, then try
   6206    // to draw with that. If that for some reason failed, then fall back
   6207    // to using the Skia target as that means we were preventing from
   6208    // drawing to the WebGL context based on something other than the
   6209    // texture.
   6210    SurfacePattern pattern(nullptr, ExtendMode::CLAMP,
   6211                           Matrix::Translation(intBounds.TopLeft()));
   6212    if (DrawRectAccel(Rect(intBounds), pattern, aOptions,
   6213                      useBitmaps ? Nothing() : Some(color), &handle, false,
   6214                      true, true)) {
   6215      return true;
   6216    }
   6217  } else {
   6218    handle = nullptr;
   6219 
   6220    // If we get here, either there wasn't a cached texture handle or it
   6221    // wasn't valid. Render the text run into a temporary target.
   6222    RefPtr<DrawTargetSkia> textDT = new DrawTargetSkia;
   6223    if (textDT->Init(intBounds.Size(),
   6224                     useBitmaps || usePreblend || aUseSubpixelAA
   6225                         ? SurfaceFormat::B8G8R8A8
   6226                         : SurfaceFormat::A8)) {
   6227      textDT->SetTransform(currentTransform *
   6228                           Matrix::Translation(-intBounds.TopLeft()));
   6229      textDT->SetPermitSubpixelAA(aUseSubpixelAA);
   6230      DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER,
   6231                              aOptions.mAntialiasMode);
   6232      // If bitmaps might be used, then we have to supply the color, as color
   6233      // emoji may ignore it while grayscale bitmaps may use it, with no way to
   6234      // know ahead of time. If we are using preblending in some form, then the
   6235      // output also will depend on the supplied color. Otherwise, assume the
   6236      // output will be a mask and just render it white to determine intensity.
   6237      if (!useBitmaps && usePreblend) {
   6238        textDT->DrawGlyphMask(aFont, aBuffer, color, aStrokeOptions,
   6239                              drawOptions);
   6240      } else {
   6241        ColorPattern colorPattern(useBitmaps ? color : DeviceColor(1, 1, 1, 1));
   6242        if (aStrokeOptions) {
   6243          textDT->StrokeGlyphs(aFont, aBuffer, colorPattern, *aStrokeOptions,
   6244                               drawOptions);
   6245        } else {
   6246          textDT->FillGlyphs(aFont, aBuffer, colorPattern, drawOptions);
   6247        }
   6248      }
   6249      RefPtr<SourceSurface> textSurface = textDT->Snapshot();
   6250      if (textSurface) {
   6251        // If we don't expect the text surface to contain color glyphs
   6252        // such as from subpixel AA, then do one final check to see if
   6253        // any ended up in the result. If not, extract the alpha values
   6254        // from the surface so we can render it as a mask.
   6255        if (textSurface->GetFormat() != SurfaceFormat::A8 &&
   6256            !CheckForColorGlyphs(textSurface)) {
   6257          textSurface = ExtractAlpha(textSurface, !useBitmaps);
   6258          if (!textSurface) {
   6259            // Failed extracting alpha for the text surface...
   6260            return false;
   6261          }
   6262        }
   6263        // Attempt to upload the rendered text surface into a texture
   6264        // handle and draw it.
   6265        SurfacePattern pattern(textSurface, ExtendMode::CLAMP,
   6266                               Matrix::Translation(intBounds.TopLeft()));
   6267        if (DrawRectAccel(Rect(intBounds), pattern, aOptions,
   6268                          useBitmaps ? Nothing() : Some(color), &handle, false,
   6269                          true) &&
   6270            handle) {
   6271          // If drawing succeeded, then the text surface was uploaded to
   6272          // a texture handle. Assign it to the glyph cache entry.
   6273          entry->Link(handle);
   6274        } else {
   6275          // If drawing failed, remove the entry from the cache.
   6276          entry->Unlink();
   6277        }
   6278        return true;
   6279      }
   6280    }
   6281  }
   6282 
   6283  // Avoid filling cache with empty entries.
   6284  entry->Unlink();
   6285  return false;
   6286 }
   6287 
   6288 void DrawTargetWebgl::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
   6289                                 const Pattern& aPattern,
   6290                                 const DrawOptions& aOptions) {
   6291  if (!aFont || !aBuffer.mNumGlyphs) {
   6292    return;
   6293  }
   6294 
   6295  bool useSubpixelAA = ShouldUseSubpixelAA(aFont, aOptions);
   6296 
   6297  if (mWebglValid && SupportsDrawOptions(aOptions) &&
   6298      aPattern.GetType() == PatternType::COLOR && PrepareContext() &&
   6299      mSharedContext->DrawGlyphsAccel(aFont, aBuffer, aPattern, aOptions,
   6300                                      nullptr, useSubpixelAA)) {
   6301    return;
   6302  }
   6303 
   6304  // If not able to cache the text run to a texture, then just fall back to
   6305  // drawing with the Skia target.
   6306  if (useSubpixelAA) {
   6307    // Subpixel AA does not support layering because the subpixel masks can't
   6308    // blend with the over op.
   6309    MarkSkiaChanged();
   6310  } else {
   6311    MarkSkiaChanged(aOptions);
   6312  }
   6313  mSkia->FillGlyphs(aFont, aBuffer, aPattern, aOptions);
   6314 }
   6315 
   6316 // Attempts to read the contents of the WebGL context into the Skia target.
   6317 bool DrawTargetWebgl::ReadIntoSkia() {
   6318  if (mSkiaValid) {
   6319    return false;
   6320  }
   6321  bool didReadback = false;
   6322  if (mWebglValid) {
   6323    uint8_t* data = nullptr;
   6324    IntSize size;
   6325    int32_t stride;
   6326    SurfaceFormat format;
   6327    if (mIsClear) {
   6328      // If the WebGL target is still clear, then just clear the Skia target.
   6329      mSkia->DetachAllSnapshots();
   6330      mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
   6331                            DrawOptions(1.0f, CompositionOp::OP_SOURCE));
   6332    } else {
   6333      // If there's no existing snapshot and we can successfully map the Skia
   6334      // target for reading, then try to read into that.
   6335      if (!mSnapshot && mSkia->LockBits(&data, &size, &stride, &format)) {
   6336        (void)ReadInto(data, stride);
   6337        mSkia->ReleaseBits(data);
   6338      } else if (RefPtr<SourceSurface> snapshot = Snapshot()) {
   6339        // Otherwise, fall back to getting a snapshot from WebGL if available
   6340        // and then copying that to Skia.
   6341        mSkia->CopySurface(snapshot, GetRect(), IntPoint(0, 0));
   6342      }
   6343      didReadback = true;
   6344    }
   6345  }
   6346  mSkiaValid = true;
   6347  // The Skia data is flat after reading, so disable any layering.
   6348  mSkiaLayer = false;
   6349  return didReadback;
   6350 }
   6351 
   6352 // Reads data from the WebGL context and blends it with the current Skia layer.
   6353 void DrawTargetWebgl::FlattenSkia() {
   6354  if (!mSkiaValid || !mSkiaLayer) {
   6355    return;
   6356  }
   6357  mSkiaLayer = false;
   6358  if (mSkiaLayerClear) {
   6359    // If the WebGL target is clear, then there is nothing to blend.
   6360    return;
   6361  }
   6362  if (RefPtr<DataSourceSurface> base = ReadSnapshot()) {
   6363    mSkia->DetachAllSnapshots();
   6364    mSkiaNoClip->DrawSurface(base, Rect(GetRect()), Rect(GetRect()),
   6365                             DrawSurfaceOptions(SamplingFilter::POINT),
   6366                             DrawOptions(1.f, CompositionOp::OP_DEST_OVER));
   6367  }
   6368 }
   6369 
   6370 // Attempts to draw the contents of the Skia target into the WebGL context.
   6371 bool DrawTargetWebgl::FlushFromSkia() {
   6372  // If the WebGL context has been lost, then mark it as invalid and fail.
   6373  if (mSharedContext->IsContextLost()) {
   6374    mWebglValid = false;
   6375    return false;
   6376  }
   6377  // The WebGL target is already valid, so there is nothing to do.
   6378  if (mWebglValid) {
   6379    return true;
   6380  }
   6381  // Ensure that DrawRect doesn't recursively call into FlushFromSkia. If
   6382  // the Skia target isn't valid, then it doesn't matter what is in the the
   6383  // WebGL target either, so only try to blend if there is a valid Skia target.
   6384  mWebglValid = true;
   6385  if (mSkiaValid) {
   6386    AutoRestoreContext restore(this);
   6387 
   6388    // If the Skia target is clear, then there is no need to use a snapshot.
   6389    // Directly clear the WebGL target instead.
   6390    if (mIsClear) {
   6391      if (!DrawRect(Rect(GetRect()), GetClearPattern(),
   6392                    DrawOptions(1.0f, CompositionOp::OP_SOURCE), Nothing(),
   6393                    nullptr, false, false, true)) {
   6394        mWebglValid = false;
   6395        return false;
   6396      }
   6397      return true;
   6398    }
   6399 
   6400    RefPtr<SourceSurface> skiaSnapshot = mSkia->Snapshot();
   6401    if (!skiaSnapshot) {
   6402      // There's a valid Skia target to draw to, but for some reason there is
   6403      // no available snapshot, so just keep using the Skia target.
   6404      mWebglValid = false;
   6405      return false;
   6406    }
   6407 
   6408    // If there is no layer, then just upload it directly.
   6409    if (!mSkiaLayer) {
   6410      if (PrepareContext(false) && MarkChanged()) {
   6411        if (RefPtr<DataSourceSurface> data = skiaSnapshot->GetDataSurface()) {
   6412          mSharedContext->UploadSurface(data, mFormat, GetRect(), IntPoint(),
   6413                                        false, false, mTex);
   6414          return true;
   6415        }
   6416      }
   6417      // Failed to upload the Skia snapshot.
   6418      mWebglValid = false;
   6419      return false;
   6420    }
   6421 
   6422    SurfacePattern pattern(skiaSnapshot, ExtendMode::CLAMP);
   6423    // If there is a layer, blend the snapshot with the WebGL context.
   6424    if (!DrawRect(Rect(GetRect()), pattern,
   6425                  DrawOptions(1.0f, CompositionOp::OP_OVER), Nothing(),
   6426                  &mSnapshotTexture, false, false, true, true)) {
   6427      // If accelerated drawing failed for some reason, then leave the Skia
   6428      // target unchanged.
   6429      mWebglValid = false;
   6430      return false;
   6431    }
   6432  }
   6433  return true;
   6434 }
   6435 
   6436 void DrawTargetWebgl::UsageProfile::BeginFrame() {
   6437  // Reset the usage profile counters for the new frame.
   6438  mFallbacks = 0;
   6439  mLayers = 0;
   6440  mCacheMisses = 0;
   6441  mCacheHits = 0;
   6442  mUncachedDraws = 0;
   6443  mReadbacks = 0;
   6444 }
   6445 
   6446 void DrawTargetWebgl::UsageProfile::EndFrame() {
   6447  bool failed = false;
   6448  // If we hit a complete fallback to software rendering, or if cache misses
   6449  // were more than cutoff ratio of all requests, then we consider the frame as
   6450  // having failed performance profiling.
   6451  float cacheRatio =
   6452      StaticPrefs::gfx_canvas_accelerated_profile_cache_miss_ratio();
   6453  if (mFallbacks > 0 ||
   6454      float(mCacheMisses + mReadbacks + mLayers) >
   6455          cacheRatio * float(mCacheMisses + mCacheHits + mUncachedDraws +
   6456                             mReadbacks + mLayers)) {
   6457    failed = true;
   6458  }
   6459  if (failed) {
   6460    ++mFailedFrames;
   6461  }
   6462  ++mFrameCount;
   6463 }
   6464 
   6465 bool DrawTargetWebgl::UsageProfile::RequiresRefresh() const {
   6466  // If we've rendered at least the required number of frames for a profile and
   6467  // more than the cutoff ratio of frames did not meet performance criteria,
   6468  // then we should stop using an accelerated canvas.
   6469  uint32_t profileFrames = StaticPrefs::gfx_canvas_accelerated_profile_frames();
   6470  if (!profileFrames || mFrameCount < profileFrames) {
   6471    return false;
   6472  }
   6473  float failRatio =
   6474      StaticPrefs::gfx_canvas_accelerated_profile_fallback_ratio();
   6475  return mFailedFrames > failRatio * mFrameCount;
   6476 }
   6477 
   6478 void SharedContextWebgl::CachePrefs() {
   6479  uint32_t capacity = StaticPrefs::gfx_canvas_accelerated_gpu_path_size() << 20;
   6480  if (capacity != mPathVertexCapacity) {
   6481    mPathVertexCapacity = capacity;
   6482    if (mPathCache) {
   6483      mPathCache->ClearVertexRanges();
   6484    }
   6485    if (mPathVertexBuffer) {
   6486      ResetPathVertexBuffer();
   6487    }
   6488  }
   6489 
   6490  mPathMaxComplexity =
   6491      StaticPrefs::gfx_canvas_accelerated_gpu_path_complexity();
   6492 
   6493  mPathAAStroke = StaticPrefs::gfx_canvas_accelerated_aa_stroke_enabled();
   6494  mPathWGRStroke = StaticPrefs::gfx_canvas_accelerated_stroke_to_fill_path();
   6495 }
   6496 
   6497 // For use within CanvasRenderingContext2D, called on BorrowDrawTarget.
   6498 void DrawTargetWebgl::BeginFrame(bool aInvalidContents) {
   6499  // If still rendering into the Skia target, switch back to the WebGL
   6500  // context.
   6501  if (!mWebglValid) {
   6502    if (aInvalidContents) {
   6503      // If nothing needs to persist, just mark the WebGL context valid.
   6504      mWebglValid = true;
   6505      // Even if the Skia framebuffer is marked clear, since the WebGL
   6506      // context is not valid, its contents may be out-of-date and not
   6507      // necessarily clear.
   6508      mIsClear = false;
   6509    } else {
   6510      FlushFromSkia();
   6511    }
   6512  }
   6513  // Check if we need to clear out any cached because of memory pressure.
   6514  mSharedContext->ClearCachesIfNecessary();
   6515  // Cache any prefs for the frame.
   6516  mSharedContext->CachePrefs();
   6517  mProfile.BeginFrame();
   6518 }
   6519 
   6520 // For use within CanvasRenderingContext2D, called on ReturnDrawTarget.
   6521 void DrawTargetWebgl::EndFrame() {
   6522  if (StaticPrefs::gfx_canvas_accelerated_debug()) {
   6523    // Draw a green rectangle in the upper right corner to indicate
   6524    // acceleration.
   6525    IntRect corner = IntRect(mSize.width - 16, 0, 16, 16).Intersect(GetRect());
   6526    DrawRect(Rect(corner), ColorPattern(DeviceColor(0.0f, 1.0f, 0.0f, 1.0f)),
   6527             DrawOptions(), Nothing(), nullptr, false, false);
   6528  }
   6529  mProfile.EndFrame();
   6530  // Ensure we're not somehow using more than the allowed texture memory.
   6531  mSharedContext->PruneTextureMemory();
   6532  // Signal that we're done rendering the frame in case no present occurs.
   6533  mSharedContext->mWebgl->EndOfFrame();
   6534  // Check if we need to clear out any cached because of memory pressure.
   6535  mSharedContext->ClearCachesIfNecessary();
   6536 }
   6537 
   6538 bool DrawTargetWebgl::CopyToSwapChain(
   6539    layers::TextureType aTextureType, layers::RemoteTextureId aId,
   6540    layers::RemoteTextureOwnerId aOwnerId,
   6541    layers::RemoteTextureOwnerClient* aOwnerClient) {
   6542  if (!mWebglValid && !FlushFromSkia()) {
   6543    return false;
   6544  }
   6545 
   6546  // Copy and swizzle the WebGL framebuffer to the swap chain front buffer.
   6547  webgl::SwapChainOptions options;
   6548  options.bgra = true;
   6549  // Allow async present to be toggled on for accelerated Canvas2D
   6550  // independent of WebGL via pref.
   6551  options.forceAsyncPresent =
   6552      StaticPrefs::gfx_canvas_accelerated_async_present();
   6553  options.remoteTextureId = aId;
   6554  options.remoteTextureOwnerId = aOwnerId;
   6555  return mSharedContext->mWebgl->CopyToSwapChain(mFramebuffer, aTextureType,
   6556                                                 options, aOwnerClient);
   6557 }
   6558 
   6559 std::shared_ptr<gl::SharedSurface> SharedContextWebgl::ExportSharedSurface(
   6560    layers::TextureType aTextureType, SourceSurface* aSurface) {
   6561  RefPtr<WebGLTexture> tex = GetCompatibleSnapshot(aSurface, nullptr, false);
   6562  if (!tex) {
   6563    return nullptr;
   6564  }
   6565  if (!mExportFramebuffer) {
   6566    mExportFramebuffer = mWebgl->CreateFramebuffer();
   6567  }
   6568  mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mExportFramebuffer);
   6569  webgl::FbAttachInfo attachInfo;
   6570  attachInfo.tex = tex;
   6571  mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
   6572                            LOCAL_GL_TEXTURE_2D, attachInfo);
   6573  // Copy and swizzle the WebGL framebuffer to the swap chain front buffer.
   6574  webgl::SwapChainOptions options;
   6575  options.bgra = true;
   6576  std::shared_ptr<gl::SharedSurface> sharedSurface;
   6577  if (mWebgl->CopyToSwapChain(mExportFramebuffer, aTextureType, options)) {
   6578    if (gl::SwapChain* swapChain =
   6579            mWebgl->GetSwapChain(mExportFramebuffer, false)) {
   6580      sharedSurface = swapChain->FrontBuffer();
   6581    }
   6582  }
   6583  RestoreCurrentTarget();
   6584  return sharedSurface;
   6585 }
   6586 
   6587 already_AddRefed<SourceSurface> SharedContextWebgl::ImportSurfaceDescriptor(
   6588    const layers::SurfaceDescriptor& aDesc, const IntSize& aSize,
   6589    SurfaceFormat aFormat) {
   6590  if (IsContextLost()) {
   6591    return nullptr;
   6592  }
   6593 
   6594  RefPtr<TextureHandle> handle =
   6595      AllocateTextureHandle(aFormat, aSize, true, true);
   6596  if (!handle) {
   6597    return nullptr;
   6598  }
   6599  BackingTexture* backing = handle->GetBackingTexture();
   6600  RefPtr<WebGLTexture> tex = backing->GetWebGLTexture();
   6601  if (mLastTexture != tex) {
   6602    mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
   6603    mLastTexture = tex;
   6604  }
   6605  if (!backing->IsInitialized()) {
   6606    backing->MarkInitialized();
   6607    InitTexParameters(tex);
   6608    if (handle->GetSize() != backing->GetSize()) {
   6609      UploadSurface(nullptr, backing->GetFormat(),
   6610                    IntRect(IntPoint(), backing->GetSize()), IntPoint(), true,
   6611                    true);
   6612    }
   6613  }
   6614 
   6615  IntRect bounds = handle->GetBounds();
   6616  webgl::TexUnpackBlobDesc texDesc = {
   6617      LOCAL_GL_TEXTURE_2D, {uint32_t(aSize.width), uint32_t(aSize.height), 1}};
   6618  texDesc.sd = Some(aDesc);
   6619  texDesc.structuredSrcSize = uvec2::FromSize(aSize);
   6620  GLenum intFormat =
   6621      aFormat == SurfaceFormat::A8 ? LOCAL_GL_R8 : LOCAL_GL_RGBA8;
   6622  GLenum extFormat =
   6623      aFormat == SurfaceFormat::A8 ? LOCAL_GL_RED : LOCAL_GL_RGBA;
   6624  webgl::PackingInfo texPI = {extFormat, LOCAL_GL_UNSIGNED_BYTE};
   6625  mWebgl->TexImage(0, handle->GetSize() == backing->GetSize() ? intFormat : 0,
   6626                   {uint32_t(bounds.x), uint32_t(bounds.y), 0}, texPI, texDesc);
   6627 
   6628  RefPtr<SourceSurfaceWebgl> surface = new SourceSurfaceWebgl(this);
   6629  surface->SetHandle(handle);
   6630  return surface.forget();
   6631 }
   6632 
   6633 already_AddRefed<SourceSurface> DrawTargetWebgl::ImportSurfaceDescriptor(
   6634    const layers::SurfaceDescriptor& aDesc, const IntSize& aSize,
   6635    SurfaceFormat aFormat) {
   6636  return mSharedContext->ImportSurfaceDescriptor(aDesc, aSize, aFormat);
   6637 }
   6638 
   6639 already_AddRefed<DrawTarget> DrawTargetWebgl::CreateSimilarDrawTarget(
   6640    const IntSize& aSize, SurfaceFormat aFormat) const {
   6641  return mSkia->CreateSimilarDrawTarget(aSize, aFormat);
   6642 }
   6643 
   6644 bool DrawTargetWebgl::CanCreateSimilarDrawTarget(const IntSize& aSize,
   6645                                                 SurfaceFormat aFormat) const {
   6646  return mSkia->CanCreateSimilarDrawTarget(aSize, aFormat);
   6647 }
   6648 
   6649 RefPtr<DrawTarget> DrawTargetWebgl::CreateClippedDrawTarget(
   6650    const Rect& aBounds, SurfaceFormat aFormat) {
   6651  return mSkia->CreateClippedDrawTarget(aBounds, aFormat);
   6652 }
   6653 
   6654 already_AddRefed<SourceSurface> DrawTargetWebgl::CreateSourceSurfaceFromData(
   6655    unsigned char* aData, const IntSize& aSize, int32_t aStride,
   6656    SurfaceFormat aFormat) const {
   6657  return mSkia->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
   6658 }
   6659 
   6660 already_AddRefed<SourceSurface>
   6661 DrawTargetWebgl::CreateSourceSurfaceFromNativeSurface(
   6662    const NativeSurface& aSurface) const {
   6663  return mSkia->CreateSourceSurfaceFromNativeSurface(aSurface);
   6664 }
   6665 
   6666 already_AddRefed<SourceSurface> DrawTargetWebgl::OptimizeSourceSurface(
   6667    SourceSurface* aSurface) const {
   6668  if (aSurface->GetUnderlyingType() == SurfaceType::WEBGL) {
   6669    return do_AddRef(aSurface);
   6670  }
   6671  return mSkia->OptimizeSourceSurface(aSurface);
   6672 }
   6673 
   6674 already_AddRefed<SourceSurface>
   6675 DrawTargetWebgl::OptimizeSourceSurfaceForUnknownAlpha(
   6676    SourceSurface* aSurface) const {
   6677  return mSkia->OptimizeSourceSurfaceForUnknownAlpha(aSurface);
   6678 }
   6679 
   6680 already_AddRefed<GradientStops> DrawTargetWebgl::CreateGradientStops(
   6681    GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
   6682  return mSkia->CreateGradientStops(aStops, aNumStops, aExtendMode);
   6683 }
   6684 
   6685 already_AddRefed<FilterNode> DrawTargetWebgl::CreateFilter(FilterType aType) {
   6686  return FilterNodeWebgl::Create(aType);
   6687 }
   6688 
   6689 already_AddRefed<FilterNode> DrawTargetWebgl::DeferFilterInput(
   6690    const Path* aPath, const Pattern& aPattern, const IntRect& aSourceRect,
   6691    const IntPoint& aDestOffset, const DrawOptions& aOptions,
   6692    const StrokeOptions* aStrokeOptions) {
   6693  RefPtr<FilterNode> filter = new FilterNodeDeferInputWebgl(
   6694      do_AddRef((Path*)aPath), aPattern, aSourceRect,
   6695      GetTransform().PostTranslate(aDestOffset), aOptions, aStrokeOptions);
   6696  return filter.forget();
   6697 }
   6698 
   6699 void DrawTargetWebgl::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
   6700                                 const Point& aDestPoint,
   6701                                 const DrawOptions& aOptions) {
   6702  if (!aNode || aNode->GetBackendType() != FILTER_BACKEND_WEBGL) {
   6703    return;
   6704  }
   6705  auto* webglFilter = static_cast<FilterNodeWebgl*>(aNode);
   6706  webglFilter->Draw(this, aSourceRect, aDestPoint, aOptions);
   6707 }
   6708 
   6709 void DrawTargetWebgl::DrawFilterFallback(FilterNode* aNode,
   6710                                         const Rect& aSourceRect,
   6711                                         const Point& aDestPoint,
   6712                                         const DrawOptions& aOptions) {
   6713  MarkSkiaChanged(aOptions);
   6714  mSkia->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
   6715 }
   6716 
   6717 bool DrawTargetWebgl::Draw3DTransformedSurface(SourceSurface* aSurface,
   6718                                               const Matrix4x4& aMatrix) {
   6719  MarkSkiaChanged();
   6720  return mSkia->Draw3DTransformedSurface(aSurface, aMatrix);
   6721 }
   6722 
   6723 void DrawTargetWebgl::PushLayer(bool aOpaque, Float aOpacity,
   6724                                SourceSurface* aMask,
   6725                                const Matrix& aMaskTransform,
   6726                                const IntRect& aBounds, bool aCopyBackground) {
   6727  PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
   6728                     aCopyBackground, CompositionOp::OP_OVER);
   6729 }
   6730 
   6731 void DrawTargetWebgl::PushLayerWithBlend(bool aOpaque, Float aOpacity,
   6732                                         SourceSurface* aMask,
   6733                                         const Matrix& aMaskTransform,
   6734                                         const IntRect& aBounds,
   6735                                         bool aCopyBackground,
   6736                                         CompositionOp aCompositionOp) {
   6737  MarkSkiaChanged(DrawOptions(aOpacity, aCompositionOp));
   6738  mSkia->PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
   6739                            aCopyBackground, aCompositionOp);
   6740  ++mLayerDepth;
   6741  SetPermitSubpixelAA(mSkia->GetPermitSubpixelAA());
   6742 }
   6743 
   6744 void DrawTargetWebgl::PopLayer() {
   6745  MOZ_ASSERT(mSkiaValid);
   6746  MOZ_ASSERT(mLayerDepth > 0);
   6747  --mLayerDepth;
   6748  mSkia->PopLayer();
   6749  SetPermitSubpixelAA(mSkia->GetPermitSubpixelAA());
   6750 }
   6751 
   6752 }  // namespace mozilla::gfx