gfx/2d/SourceSurfaceCapture.cpp
author Bas Schouten <bschouten@mozilla.com>
Fri, 18 Jan 2019 06:45:17 +0100
changeset 511723 bd3e08ada630ea0051c778a5cbdb4af33e8471ae
parent 505383 6f3709b3878117466168c40affa7bca0b60cf75b
child 512783 a4da2c3de38fd19c313dd1ab8f8f10e847560012
permissions -rw-r--r--
Bug 1521008: Defer OptimizeSourceSurface to the paint thread if possible. r=rhunt Differential Revision: https://phabricator.services.mozilla.com/D16943

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "SourceSurfaceCapture.h"
#include "DrawCommand.h"
#include "DrawTargetCapture.h"
#include "MainThreadUtils.h"
#include "mozilla/gfx/Logging.h"

namespace mozilla {
namespace gfx {

SourceSurfaceCapture::SourceSurfaceCapture(DrawTargetCaptureImpl* aOwner)
    : mOwner(aOwner),
      mHasCommandList(false),
      mShouldResolveToLuminance{false},
      mLuminanceType{LuminanceType::LUMINANCE},
      mOpacity{1.0f},
      mLock("SourceSurfaceCapture.mLock") {
  mSize = mOwner->GetSize();
  mFormat = mOwner->GetFormat();
  mRefDT = mOwner->mRefDT;
  mStride = mOwner->mStride;
  mSurfaceAllocationSize = mOwner->mSurfaceAllocationSize;
}

SourceSurfaceCapture::SourceSurfaceCapture(
    DrawTargetCaptureImpl* aOwner,
    LuminanceType aLuminanceType /* = LuminanceType::LINEARRGB */,
    Float aOpacity /* = 1.0f */)
    : mOwner{aOwner},
      mHasCommandList{false},
      mShouldResolveToLuminance{true},
      mLuminanceType{aLuminanceType},
      mOpacity{aOpacity},
      mLock{"SourceSurfaceCapture.mLock"} {
  mSize = mOwner->GetSize();
  mFormat = mOwner->GetFormat();
  mRefDT = mOwner->mRefDT;
  mStride = mOwner->mStride;
  mSurfaceAllocationSize = mOwner->mSurfaceAllocationSize;

  // In this case our DrawTarget will not track us, so copy its drawing
  // commands.
  DrawTargetWillChange();
}

SourceSurfaceCapture::SourceSurfaceCapture(DrawTargetCaptureImpl* aOwner,
                                           SourceSurface* aSurfToOptimize)
    : mOwner{aOwner},
      mHasCommandList{false},
      mShouldResolveToLuminance{false},
      mLuminanceType{LuminanceType::LUMINANCE},
      mOpacity{1.0f},
      mLock{"SourceSurfaceCapture.mLock"},
      mSurfToOptimize(aSurfToOptimize) {
  mSize = aSurfToOptimize->GetSize();
  mFormat = aSurfToOptimize->GetFormat();
  mRefDT = mOwner->mRefDT;
}

SourceSurfaceCapture::~SourceSurfaceCapture() {}

bool SourceSurfaceCapture::IsValid() const {
  // We must either be able to source a command list, or we must have a cached
  // and rasterized surface.
  MutexAutoLock lock(mLock);
  return (mOwner || mHasCommandList || mSurfToOptimize) || mResolved;
}

RefPtr<SourceSurface> SourceSurfaceCapture::Resolve(BackendType aBackendType) {
  MutexAutoLock lock(mLock);

  if (mSurfToOptimize) {
    mResolved = mRefDT->OptimizeSourceSurface(mSurfToOptimize);
    mSurfToOptimize = nullptr;
  }

  if (mResolved || (!mOwner && !mHasCommandList)) {
    // We are already resolved, or there is no way we can rasterize
    // anything, we don't have a source DrawTarget and we don't have
    // a command list. Return whatever our cached surface is.
    return mResolved;
  }

  BackendType backendType = aBackendType;
  if (backendType == BackendType::NONE) {
    backendType = mRefDT->GetBackendType();
  }

  // If on the paint thread, we require that the owning DrawTarget be detached
  // from this snapshot. This roughly approximates an assert that nothing can
  // mutate the snapshot.
  MOZ_RELEASE_ASSERT(NS_IsMainThread() || !mOwner);

  // Note: SurfaceType is not 1:1 with BackendType, so we can't easily decide
  // that they match. Instead we just cache the first thing to be requested.
  // We ensured no mResolved existed before.
  mResolved = ResolveImpl(backendType);

  return mResolved;
}

RefPtr<SourceSurface> SourceSurfaceCapture::ResolveImpl(
    BackendType aBackendType) {
  RefPtr<DrawTarget> dt;
  uint8_t* data = nullptr;
  if (!mSurfaceAllocationSize) {
    if (aBackendType == mRefDT->GetBackendType()) {
      dt = mRefDT->CreateSimilarDrawTarget(mSize, mFormat);
    } else {
      dt = Factory::CreateDrawTarget(aBackendType, mSize, mFormat);
    }
  } else {
    data = static_cast<uint8_t*>(calloc(1, mSurfaceAllocationSize));
    if (!data) {
      return nullptr;
    }
    BackendType type = Factory::DoesBackendSupportDataDrawtarget(aBackendType)
                           ? aBackendType
                           : BackendType::SKIA;
    dt = Factory::CreateDrawTargetForData(type, data, mSize, mStride, mFormat);
    if (!dt) {
      free(data);
      return nullptr;
    }
  }

  if (!dt) {
    // Make sure we haven't allocated and aren't leaking something, the code
    // right anove here should have guaranteed that.
    MOZ_ASSERT(!data);
    return nullptr;
  }

  // If we're still attached to a DrawTarget, use its command list rather than
  // our own (which will be empty).
  CaptureCommandList& commands =
      mHasCommandList ? mCommands : mOwner->mCommands;
  for (CaptureCommandList::iterator iter(commands); !iter.Done(); iter.Next()) {
    DrawingCommand* cmd = iter.Get();
    cmd->ExecuteOnDT(dt, nullptr);
  }

  RefPtr<SourceSurface> surf;
  if (!mShouldResolveToLuminance) {
    surf = dt->Snapshot();
  } else {
    surf = dt->IntoLuminanceSource(mLuminanceType, mOpacity);
  }

  if (data) {
    surf->AddUserData(reinterpret_cast<UserDataKey*>(dt.get()), data, free);
  }

  return surf.forget();
}

already_AddRefed<DataSourceSurface> SourceSurfaceCapture::GetDataSurface() {
  RefPtr<SourceSurface> surface = Resolve();
  if (!surface) {
    return nullptr;
  }
  return surface->GetDataSurface();
}

void SourceSurfaceCapture::DrawTargetWillDestroy() {
  MutexAutoLock lock(mLock);

  // The source DrawTarget is going away, so we can just steal its commands.
  mCommands = std::move(mOwner->mCommands);
  mHasCommandList = true;
  mOwner = nullptr;
}

void SourceSurfaceCapture::DrawTargetWillChange() {
  MutexAutoLock lock(mLock);

  for (CaptureCommandList::iterator iter(mOwner->mCommands); !iter.Done();
       iter.Next()) {
    DrawingCommand* cmd = iter.Get();
    cmd->CloneInto(&mCommands);
  }

  mHasCommandList = true;
  mOwner = nullptr;
}

}  // namespace gfx
}  // namespace mozilla