DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (2b8364cfdb04)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
/* -*- 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/. */

#ifndef MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
#define MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_

#include "mozilla/gfx/2D.h"
#include "mozilla/Mutex.h"
#include "mozilla/ipc/SharedMemoryBasic.h"

namespace mozilla {
namespace gfx {

class SourceSurfaceSharedData;

/**
 * This class is used to wrap shared (as in process) data buffers allocated by
 * a SourceSurfaceSharedData object. It may live in the same process or a
 * different process from the actual SourceSurfaceSharedData object.
 *
 * If it is in the same process, mBuf is the same object as that in the surface.
 * It is a useful abstraction over just using the surface directly, because it
 * can have a different lifetime from the surface; if the surface gets freed,
 * consumers may continue accessing the data in the buffer. Releasing the
 * original surface is a signal which feeds into SharedSurfacesParent to decide
 * to release the SourceSurfaceSharedDataWrapper.
 *
 * If it is in a different process, mBuf is a new SharedMemoryBasic object which
 * mapped in the given shared memory handle as read only memory.
 */
class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
  typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;

 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper,
                                          override)

  SourceSurfaceSharedDataWrapper()
      : mStride(0),
        mConsumers(0),
        mFormat(SurfaceFormat::UNKNOWN),
        mCreatorPid(0),
        mCreatorRef(true) {}

  bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
            const SharedMemoryBasic::Handle& aHandle,
            base::ProcessId aCreatorPid);

  void Init(SourceSurfaceSharedData* aSurface);

  base::ProcessId GetCreatorPid() const { return mCreatorPid; }

  int32_t Stride() override { return mStride; }

  SurfaceType GetType() const override { return SurfaceType::DATA; }
  IntSize GetSize() const override { return mSize; }
  SurfaceFormat GetFormat() const override { return mFormat; }

  uint8_t* GetData() override { return static_cast<uint8_t*>(mBuf->memory()); }

  bool OnHeap() const override { return false; }

  bool AddConsumer() { return ++mConsumers == 1; }

  bool RemoveConsumer(bool aForCreator) {
    MOZ_ASSERT(mConsumers > 0);
    if (aForCreator) {
      if (!mCreatorRef) {
        MOZ_ASSERT_UNREACHABLE("Already released creator reference!");
        return false;
      }
      mCreatorRef = false;
    }
    return --mConsumers == 0;
  }

  uint32_t GetConsumers() const {
    MOZ_ASSERT(mConsumers > 0);
    return mConsumers;
  }

  bool HasCreatorRef() const { return mCreatorRef; }

 private:
  size_t GetDataLength() const {
    return static_cast<size_t>(mStride) * mSize.height;
  }

  size_t GetAlignedDataLength() const {
    return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
  }

  int32_t mStride;
  uint32_t mConsumers;
  IntSize mSize;
  RefPtr<SharedMemoryBasic> mBuf;
  SurfaceFormat mFormat;
  base::ProcessId mCreatorPid;
  bool mCreatorRef;
};

/**
 * This class is used to wrap shared (as in process) data buffers used by a
 * source surface.
 */
class SourceSurfaceSharedData final : public DataSourceSurface {
  typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;

 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)

  SourceSurfaceSharedData()
      : mMutex("SourceSurfaceSharedData"),
        mStride(0),
        mHandleCount(0),
        mFormat(SurfaceFormat::UNKNOWN),
        mClosed(false),
        mFinalized(false),
        mShared(false) {}

  /**
   * Initialize the surface by creating a shared memory buffer with a size
   * determined by aSize, aStride and aFormat. If aShare is true, it will also
   * immediately attempt to share the surface with the GPU process via
   * SharedSurfacesChild.
   */
  bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
            bool aShare = true);

  uint8_t* GetData() override {
    MutexAutoLock lock(mMutex);
    return GetDataInternal();
  }

  int32_t Stride() override { return mStride; }

  SurfaceType GetType() const override { return SurfaceType::DATA_SHARED; }
  IntSize GetSize() const override { return mSize; }
  SurfaceFormat GetFormat() const override { return mFormat; }

  void GuaranteePersistance() override;

  void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
                              size_t& aNonHeapSizeOut, size_t& aExtHandlesOut,
                              uint64_t& aExtIdOut) const override;

  bool OnHeap() const override { return false; }

  /**
   * Although Map (and Moz2D in general) isn't normally threadsafe,
   * we want to allow it for SourceSurfaceSharedData since it should
   * always be fine (for reading at least).
   *
   * This is the same as the base class implementation except using
   * mMapCount instead of mIsMapped since that breaks for multithread.
   *
   * Additionally if a reallocation happened while there were active
   * mappings, then we guarantee that GetData will continue to return
   * the same data pointer by retaining the old shared buffer until
   * the last mapping is freed via Unmap.
   */
  bool Map(MapType, MappedSurface* aMappedSurface) override {
    MutexAutoLock lock(mMutex);
    ++mMapCount;
    aMappedSurface->mData = GetDataInternal();
    aMappedSurface->mStride = mStride;
    return true;
  }

  void Unmap() override {
    MutexAutoLock lock(mMutex);
    MOZ_ASSERT(mMapCount > 0);
    if (--mMapCount == 0) {
      mOldBuf = nullptr;
    }
  }

  /**
   * Get a handle to share to another process for this buffer. Returns:
   *   NS_OK -- success, aHandle is valid.
   *   NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate.
   *   NS_ERROR_FAILURE -- failed to create a handle to share.
   */
  nsresult ShareToProcess(base::ProcessId aPid,
                          SharedMemoryBasic::Handle& aHandle);

  /**
   * Indicates the buffer is not expected to be shared with any more processes.
   * May release the handle if possible (see CloseHandleInternal).
   */
  void FinishedSharing() {
    MutexAutoLock lock(mMutex);
    mShared = true;
    CloseHandleInternal();
  }

  /**
   * Indicates whether or not the buffer can be shared with another process
   * without reallocating. Note that this is racy and should only be used for
   * informational/reporting purposes.
   */
  bool CanShare() const {
    MutexAutoLock lock(mMutex);
    return !mClosed;
  }

  /**
   * Allocate a new shared memory buffer so that we can get a new handle for
   * sharing to new processes. ShareToProcess must have failed with
   * NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
   * if the operation succeeds. If it fails, there is no state change.
   */
  bool ReallocHandle();

  /**
   * Signals we have finished writing to the buffer and it may be marked as
   * read only.
   */
  void Finalize();

  /**
   * Indicates whether or not the buffer can change. If this returns true, it is
   * guaranteed to continue to do so for the remainder of the surface's life.
   */
  bool IsFinalized() const {
    MutexAutoLock lock(mMutex);
    return mFinalized;
  }

  /**
   * Yields a dirty rect of what has changed since it was last called.
   */
  Maybe<IntRect> TakeDirtyRect() override {
    MutexAutoLock lock(mMutex);
    if (mDirtyRect) {
      Maybe<IntRect> ret = std::move(mDirtyRect);
      return ret;
    }
    return Nothing();
  }

  /**
   * Increment the invalidation counter.
   */
  void Invalidate(const IntRect& aDirtyRect) override {
    MutexAutoLock lock(mMutex);
    if (!aDirtyRect.IsEmpty()) {
      if (mDirtyRect) {
        mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect);
      } else {
        mDirtyRect = Some(aDirtyRect);
      }
    } else {
      mDirtyRect = Some(IntRect(IntPoint(0, 0), mSize));
    }
    MOZ_ASSERT_IF(mDirtyRect, !mDirtyRect->IsEmpty());
  }

  /**
   * While a HandleLock exists for the given surface, the shared memory handle
   * cannot be released.
   */
  class MOZ_STACK_CLASS HandleLock final {
   public:
    explicit HandleLock(SourceSurfaceSharedData* aSurface)
        : mSurface(aSurface) {
      mSurface->LockHandle();
    }

    ~HandleLock() { mSurface->UnlockHandle(); }

   private:
    RefPtr<SourceSurfaceSharedData> mSurface;
  };

 private:
  friend class SourceSurfaceSharedDataWrapper;

  virtual ~SourceSurfaceSharedData() = default;

  void LockHandle() {
    MutexAutoLock lock(mMutex);
    ++mHandleCount;
  }

  void UnlockHandle() {
    MutexAutoLock lock(mMutex);
    MOZ_ASSERT(mHandleCount > 0);
    --mHandleCount;
    mShared = true;
    CloseHandleInternal();
  }

  uint8_t* GetDataInternal() const;

  size_t GetDataLength() const {
    return static_cast<size_t>(mStride) * mSize.height;
  }

  size_t GetAlignedDataLength() const {
    return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
  }

  /**
   * Attempt to close the handle. Only if the buffer has been both finalized
   * and we have completed sharing will it be released.
   */
  void CloseHandleInternal();

  mutable Mutex mMutex;
  int32_t mStride;
  int32_t mHandleCount;
  Maybe<IntRect> mDirtyRect;
  IntSize mSize;
  RefPtr<SharedMemoryBasic> mBuf;
  RefPtr<SharedMemoryBasic> mOldBuf;
  SurfaceFormat mFormat;
  bool mClosed : 1;
  bool mFinalized : 1;
  bool mShared : 1;
};

}  // namespace gfx
}  // namespace mozilla

#endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */