Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/gtk/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 16 kB image not shown  

Quelle  WaylandBuffer.cpp   Sprache: C

 

/*
 * 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 "WaylandBuffer.h"
#include "WaylandSurface.h"
#include "WaylandSurfaceLock.h"

#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>

#include "gfx2DGlue.h"
#include "gfxPlatform.h"
#include "mozilla/WidgetUtilsGtk.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/ipc/SharedMemoryHandle.h"
#include "nsGtkUtils.h"
#include "nsPrintfCString.h"
#include "prenv.h"  // For PR_GetEnv

#ifdef MOZ_LOGGING
#  include "mozilla/Logging.h"
#  include "mozilla/ScopeExit.h"
#  include "Units.h"
extern mozilla::LazyLogModule gWidgetWaylandLog;
#  define LOGWAYLAND(...) \
    MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#else
#  define LOGWAYLAND(...)
#endif /* MOZ_LOGGING */

using namespace mozilla::gl;

namespace mozilla::widget {

#define BUFFER_BPP 4

#ifdef MOZ_LOGGING
MOZ_RUNINIT int WaylandBuffer::mDumpSerial =
    PR_GetEnv("MOZ_WAYLAND_DUMP_WL_BUFFERS") ? 1 : 0;
MOZ_RUNINIT char* WaylandBuffer::mDumpDir = PR_GetEnv("MOZ_WAYLAND_DUMP_DIR");
#endif

/* static */
RefPtr<WaylandShmPool> WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay,
                                              int aSize) {
  if (!aWaylandDisplay->GetShm()) {
    NS_WARNING("WaylandShmPool: Missing Wayland shm interface!");
    return nullptr;
  }

  RefPtr<WaylandShmPool> shmPool = new WaylandShmPool();

  auto handle = ipc::shared_memory::Create(aSize);
  if (!handle) {
    NS_WARNING("WaylandShmPool: Unable to allocate shared memory!");
    return nullptr;
  }

  shmPool->mShmHandle = handle.Clone();
  shmPool->mShmPool =
      wl_shm_create_pool(aWaylandDisplay->GetShm(),
                         handle.Clone().TakePlatformHandle().get(), aSize);
  if (!shmPool->mShmPool) {
    NS_WARNING("WaylandShmPool: Unable to allocate shared memory pool!");
    return nullptr;
  }

  return shmPool;
}

void* WaylandShmPool::GetImageData() {
  if (!mShm) {
    mShm = mShmHandle.Map();
    if (!mShm) {
      NS_WARNING("WaylandShmPool: Failed to map Shm!");
      return nullptr;
    }
  }
  return mShm.Address();
}

WaylandShmPool::~WaylandShmPool() {
  MozClearPointer(mShmPool, wl_shm_pool_destroy);
}

WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize& aSize) : mSize(aSize) {}

bool WaylandBuffer::IsAttached(const WaylandSurfaceLock& aSurfaceLock) const {
  for (const auto& transaction : mBufferTransactions) {
    if (transaction->IsAttached()) {
      return true;
    }
  }
  return false;
}

BufferTransaction* WaylandBuffer::GetTransaction(
    const WaylandSurfaceLock& aSurfaceLock) {
  for (const auto& transaction : mBufferTransactions) {
    if (transaction->CanRecycle(aSurfaceLock.GetWaylandSurface())) {
      LOGWAYLAND("WaylandBuffer::GetTransaction() [%p] reuse transaction [%d]",
                 (void*)this, (int)mBufferTransactions.Length());
      return transaction;
    }
  }

  wl_buffer* buffer = mExternalWlBuffer;
  if (!buffer) {
    buffer = CreateWlBuffer();
  }
  if (!buffer) {
    gfxCriticalError()
        << "WaylandBuffer::GetTransaction() failed to create wl_buffer!";
    return nullptr;
  }

  LOGWAYLAND(
      "WaylandBuffer::GetTransaction() create new [%p] wl_buffer [%p] "
      "transactions [%d] external buffer [%d] WaylandSurface [%p]",
      (void*)this, buffer, (int)mBufferTransactions.Length(),
      !!mExternalWlBuffer, aSurfaceLock.GetWaylandSurface());

  auto* transaction = new BufferTransaction(this, buffer, !!mExternalWlBuffer);
  mBufferTransactions.AppendElement(transaction);
  return transaction;
}

void WaylandBuffer::RemoveTransaction(const WaylandSurfaceLock& aSurfaceLock,
                                      RefPtr<BufferTransaction> aTransaction) {
  LOGWAYLAND("WaylandBuffer::RemoveTransaction() [%p]", (void*)aTransaction);
  [[maybe_unused]] bool removed =
      mBufferTransactions.RemoveElement(aTransaction);
  MOZ_DIAGNOSTIC_ASSERT(removed);
  MOZ_DIAGNOSTIC_ASSERT(!mBufferTransactions.Contains(aTransaction));
}

void WaylandBuffer::SetExternalWLBuffer(wl_buffer* aWLBuffer) {
  LOGWAYLAND("WaylandBuffer::SetExternalWLBuffer() [%p] wl_buffer %p",
             (void*)this, aWLBuffer);
  MOZ_DIAGNOSTIC_ASSERT(!mExternalWlBuffer);
  mExternalWlBuffer = aWLBuffer;
}

/* static */
RefPtr<WaylandBufferSHM> WaylandBufferSHM::Create(
    const LayoutDeviceIntSize& aSize) {
  RefPtr<WaylandBufferSHM> buffer = new WaylandBufferSHM(aSize);
  nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();

  LOGWAYLAND("WaylandBufferSHM::Create() [%p] [%d x %d]", (void*)buffer,
             aSize.width, aSize.height);

  int size = aSize.width * aSize.height * BUFFER_BPP;
  buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size);
  if (!buffer->mShmPool) {
    LOGWAYLAND("  failed to create shmPool");
    return nullptr;
  }

  LOGWAYLAND("  created [%p] WaylandDisplay [%p]\n", buffer.get(),
             waylandDisplay);

  return buffer;
}

wl_buffer* WaylandBufferSHM::CreateWlBuffer() {
  MOZ_DIAGNOSTIC_ASSERT(!mExternalWlBuffer);

  auto* buffer = wl_shm_pool_create_buffer(
      mShmPool->GetShmPool(), 0, mSize.width, mSize.height,
      mSize.width * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);

  LOGWAYLAND("WaylandBufferSHM::CreateWlBuffer() [%p] wl_buffer [%p]",
             (void*)this, buffer);

  return buffer;
}

WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize& aSize)
    : WaylandBuffer(aSize) {
  LOGWAYLAND("WaylandBufferSHM::WaylandBufferSHM() [%p]\n", (void*)this);
}

WaylandBufferSHM::~WaylandBufferSHM() {
  LOGWAYLAND("WaylandBufferSHM::~WaylandBufferSHM() [%p]\n", (void*)this);
  MOZ_RELEASE_ASSERT(mBufferTransactions.IsEmpty());
}

already_AddRefed<gfx::DrawTarget> WaylandBufferSHM::Lock() {
  LOGWAYLAND("WaylandBufferSHM::lock() [%p]\n", (void*)this);
  return gfxPlatform::CreateDrawTargetForData(
      static_cast<unsigned char*>(mShmPool->GetImageData()),
      mSize.ToUnknownSize(), BUFFER_BPP * mSize.width, GetSurfaceFormat());
}

void WaylandBufferSHM::Clear() {
  LOGWAYLAND("WaylandBufferSHM::Clear() [%p]\n", (void*)this);
  memset(mShmPool->GetImageData(), 0xff,
         mSize.height * mSize.width * BUFFER_BPP);
}

#ifdef MOZ_LOGGING
void WaylandBufferSHM::DumpToFile(const char* aHint) {
  if (!mDumpSerial) {
    NS_WARNING("mDumpSerial is not set!");
    return;
  }

  cairo_surface_t* surface = nullptr;
  auto unmap = MakeScopeExit([&] {
    if (surface) {
      cairo_surface_destroy(surface);
    }
  });
  surface = cairo_image_surface_create_for_data(
      (unsigned char*)mShmPool->GetImageData(), CAIRO_FORMAT_ARGB32,
      mSize.width, mSize.height, BUFFER_BPP * mSize.width);
  if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
    nsCString filename;
    if (mDumpDir) {
      filename.Append(mDumpDir);
      filename.Append('/');
    }
    filename.Append(nsPrintfCString("firefox-wl-sw-buffer-%.5d-%s.png",
                                    mDumpSerial++, aHint));
    cairo_surface_write_to_png(surface, filename.get());
    LOGWAYLAND("Dumped wl_buffer to %s\n", filename.get());
  }
}
#endif

/* static */
already_AddRefed<WaylandBufferDMABUF> WaylandBufferDMABUF::CreateRGBA(
    const LayoutDeviceIntSize& aSize, GLContext* aGL,
    RefPtr<DRMFormat> aFormat) {
  RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(aSize);

  buffer->mDMABufSurface = DMABufSurfaceRGBA::CreateDMABufSurface(
      aGL, aSize.width, aSize.height, DMABUF_SCANOUT | DMABUF_USE_MODIFIERS,
      aFormat);
  if (!buffer->mDMABufSurface || !buffer->mDMABufSurface->CreateTexture(aGL)) {
    LOGWAYLAND("  failed to create texture");
    return nullptr;
  }

  LOGWAYLAND("WaylandBufferDMABUF::CreateRGBA() [%p] UID %d [%d x %d]",
             (void*)buffer, buffer->mDMABufSurface->GetUID(), aSize.width,
             aSize.height);
  return buffer.forget();
}

/* static */
already_AddRefed<WaylandBufferDMABUF> WaylandBufferDMABUF::CreateExternal(
    RefPtr<DMABufSurface> aSurface) {
  const auto size =
      LayoutDeviceIntSize(aSurface->GetWidth(), aSurface->GetHeight());
  RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(size);

  LOGWAYLAND("WaylandBufferDMABUF::CreateExternal() [%p] UID %d [%d x %d]",
             (void*)buffer, aSurface->GetUID(), size.width, size.height);

  buffer->mDMABufSurface = aSurface;
  return buffer.forget();
}

wl_buffer* WaylandBufferDMABUF::CreateWlBuffer() {
  MOZ_DIAGNOSTIC_ASSERT(mDMABufSurface);
  MOZ_DIAGNOSTIC_ASSERT(!mExternalWlBuffer);

  auto* buffer = mDMABufSurface->CreateWlBuffer();

  LOGWAYLAND("WaylandBufferDMABUF::CreateWlBuffer() [%p] UID %d wl_buffer [%p]",
             (void*)this, mDMABufSurface->GetUID(), buffer);

  return buffer;
}

WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize)
    : WaylandBuffer(aSize) {
  LOGWAYLAND("WaylandBufferDMABUF::WaylandBufferDMABUF [%p]\n", (void*)this);
}

WaylandBufferDMABUF::~WaylandBufferDMABUF() {
  LOGWAYLAND("WaylandBufferDMABUF::~WaylandBufferDMABUF [%p] UID %d\n",
             (void*)this, mDMABufSurface ? mDMABufSurface->GetUID() : -1);
  MOZ_RELEASE_ASSERT(mBufferTransactions.IsEmpty());
}

#ifdef MOZ_LOGGING
void WaylandBufferDMABUF::DumpToFile(const char* aHint) {
  if (!mDumpSerial) {
    NS_WARNING("mDumpSerial is not set!");
    return;
  }
  nsCString filename;
  if (mDumpDir) {
    filename.Append(mDumpDir);
    filename.Append('/');
  }
  filename.AppendPrintf("firefox-wl-buffer-dmabuf-%.5d-%s.png", mDumpSerial++,
                        aHint);
  mDMABufSurface->DumpToFile(filename.get());
  LOGWAYLAND("Dumped wl_buffer to %s\n", filename.get());
}
#endif

WaylandBufferDMABUFHolder::WaylandBufferDMABUFHolder(DMABufSurface* aSurface,
                                                     wl_buffer* aWLBuffer)
    : mWLBuffer(aWLBuffer) {
  mUID = aSurface->GetUID();
  mPID = aSurface->GetPID();
  LOGWAYLAND(
      "WaylandBufferDMABUFHolder::WaylandBufferDMABUFHolder wl_buffer [%p] UID "
      "%d PID %d",
      mWLBuffer, mUID, mPID);
}

WaylandBufferDMABUFHolder::~WaylandBufferDMABUFHolder() {
  LOGWAYLAND(
      "WaylandBufferDMABUFHolder::~WaylandBufferDMABUFHolder wl_buffer [%p] "
      "UID %d PID %d",
      mWLBuffer, mUID, mPID);
  MozClearPointer(mWLBuffer, wl_buffer_destroy);
}

bool WaylandBufferDMABUFHolder::Matches(DMABufSurface* aSurface) const {
  return mUID == aSurface->GetUID() && mPID == aSurface->GetPID();
}

wl_buffer* BufferTransaction::BufferBorrowLocked(
    const WaylandSurfaceLock& aSurfaceLock) {
  LOGWAYLAND(
      "BufferTransaction::BufferBorrow() [%p] widget [%p] WaylandSurface [%p] "
      "WaylandBuffer [%p]",
      this, aSurfaceLock.GetWaylandSurface()->GetLoggingWidget(),
      aSurfaceLock.GetWaylandSurface(), mBuffer.get());

  MOZ_DIAGNOSTIC_ASSERT(
      !mSurface || mSurface == aSurfaceLock.GetWaylandSurface(),
      "Can't transfer transaction between WaylandSurfaces!");
  MOZ_DIAGNOSTIC_ASSERT(mBufferState == BufferState::Detached);

  mSurface = aSurfaceLock.GetWaylandSurface();

  // We don't take reference to this. Some compositors doesn't send
  // buffer release callback and we may leak BufferTransaction then.
  // Rather we destroy wl_buffer at end which makes sure no release callback
  // comes after BufferTransaction release.
  if (wl_proxy_get_listener((wl_proxy*)mWLBuffer)) {
    wl_proxy_set_user_data((wl_proxy*)mWLBuffer, this);
  } else {
    static const struct wl_buffer_listener listener{
        [](void* aData, wl_buffer* aBuffer) {
          auto transaction = static_cast<BufferTransaction*>(aData);
          if (transaction) {
            // BufferDetachCallback can delete transaction
            RefPtr grip{transaction};
            transaction->BufferDetachCallback();
          }
        }};
    if (wl_buffer_add_listener(mWLBuffer, &listener, this) < 0) {
      gfxCriticalError() << "wl_buffer_add_listener() failed";
    }
  }

  mBufferState = BufferState::WaitingForDetach;
  return mWLBuffer;
}

void BufferTransaction::BufferDetachCallback() {
  WaylandSurfaceLock lock(mSurface);
  LOGWAYLAND(
      "BufferTransaction::BufferDetach() [%p] WaylandBuffer [%p] attached to "
      "WaylandSurface %d",
      this, (void*)mBuffer, mSurface->IsBufferAttached(mBuffer));

  if (mBufferState != BufferState::WaitingForDelete) {
    mBufferState = BufferState::Detached;

    // Delete this transaction if WaylandSurface uses different WaylandBuffer
    // already, we don't need to keep it for recycling.
    if (!mSurface->IsBufferAttached(mBuffer)) {
      DeleteTransactionLocked(lock);
    }
  }
}

void BufferTransaction::BufferDeleteCallback() {
  LOGWAYLAND("BufferTransaction::DeleteCallback() [%p] WaylandBuffer [%p] ",
             this, (void*)mBuffer);
  WaylandSurfaceLock lock(mSurface);
  mBufferState = BufferState::Deleted;
  DeleteLocked(lock);
}

void BufferTransaction::WlBufferDeleteLocked(
    const WaylandSurfaceLock& aSurfaceLock) {
  LOGWAYLAND(
      "BufferTransaction::WlBufferDeleteLocked() [%p] WaylandBuffer [%p] ",
      this, (void*)mBuffer);
  MOZ_DIAGNOSTIC_ASSERT(mWLBuffer);
  if (mIsExternalBuffer) {
    wl_proxy_set_user_data((wl_proxy*)mWLBuffer, nullptr);
    mWLBuffer = nullptr;
  } else {
    MozClearPointer(mWLBuffer, wl_buffer_destroy);
  }
}

void BufferTransaction::DeleteTransactionLocked(
    const WaylandSurfaceLock& aSurfaceLock) {
  // It's possible that the transaction is already deleted. It happens
  // if one WaylandBuffer is attached to WaylandSurface,
  // then detached/deleted from WaylandSurface::UnmapLocked() where all buffers
  // are removed and then attached to another WaylandSurface.
  if (mBufferState == BufferState::WaitingForDelete ||
      mBufferState == BufferState::Deleted) {
    return;
  }

  LOGWAYLAND(
      "BufferTransaction::BufferDelete() [%p] WaylandBuffer [%p] wl_buffer "
      "[%p] "
      "external %d state %d",
      this, (void*)mBuffer, mWLBuffer, mIsExternalBuffer, (int)mBufferState);

  WlBufferDeleteLocked(aSurfaceLock);

  // wl_buffer is detached so we can't get any release event so
  // delete the transaction now.
  if (mBufferState == BufferState::Detached) {
    mBufferState = BufferState::Deleted;
    DeleteLocked(aSurfaceLock);
    return;
  }

  mBufferState = BufferState::WaitingForDelete;

  // There are various Wayland queues processed for every thread.
  // It's possible that wl_buffer release event is pending in any
  // queue while we already asked for wl_buffer delete.
  // We need to finish wl_buffer removal when all events from this
  // point are processed so we use sync callback.
  //
  // When wl_display_sync comes back to us (from main thread)
  // we know all events are processed and there isn't any
  // wl_buffer operation pending so we can safely release WaylandSurface
  // and WaylandBuffer objects.
  // We need to wait with WaylandMonitor
  AddRef();
  static const struct wl_callback_listener listener{
      [](void* aData, struct wl_callback* callback, uint32_t time) {
        RefPtr t = dont_AddRef(static_cast<BufferTransaction*>(aData));
        t->BufferDeleteCallback();
      }};
  wl_callback_add_listener(wl_display_sync(WaylandDisplayGetWLDisplay()),
                           &listener, this);
}

void BufferTransaction::DeleteLocked(const WaylandSurfaceLock& aSurfaceLock) {
  LOGWAYLAND("BufferTransaction::DeleteLocked() [%p] WaylandBuffer [%p]", this,
             (void*)mBuffer);
  MOZ_DIAGNOSTIC_ASSERT(mBufferState == BufferState::Deleted);
  MOZ_DIAGNOSTIC_ASSERT(mSurface);

  // Unlink from Surface
  mSurface->RemoveTransactionLocked(aSurfaceLock, this);
  mSurface = nullptr;

  // This can destroy us
  RefPtr grip{this};
  mBuffer->RemoveTransaction(aSurfaceLock, this);
  mBuffer = nullptr;
}

BufferTransaction::BufferTransaction(WaylandBuffer* aBuffer,
                                     wl_buffer* aWLBuffer,
                                     bool aIsExternalBuffer)
    : mBuffer(aBuffer),
      mWLBuffer(aWLBuffer),
      mIsExternalBuffer(aIsExternalBuffer) {
  MOZ_COUNT_CTOR(BufferTransaction);
  LOGWAYLAND(
      "BufferTransaction::BufferTransaction() [%p] WaylandBuffer [%p] "
      "wl_buffer [%p] external [%d]",
      this, aBuffer, mWLBuffer, mIsExternalBuffer);
}

BufferTransaction::~BufferTransaction() {
  MOZ_COUNT_DTOR(BufferTransaction);
  LOGWAYLAND("BufferTransaction::~BufferTransaction() [%p] ", this);
  MOZ_DIAGNOSTIC_ASSERT(mBufferState == BufferState::Deleted);
  MOZ_DIAGNOSTIC_ASSERT(!mWLBuffer);
}

}  // namespace mozilla::widget

Messung V0.5 in Prozent
C=94 H=98 G=95

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-09-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.