Files
sousa-gecko/dom/webgpu/SharedTextureDMABuf.cpp
Teodor Tanasoaia b14e80842d Bug 2037013 - Don't hold Vulkan objects in SharedTextureDMABuf. r=webgpu-reviewers,aleiserson
`SharedTextureDMABuf` is handed to the compositor and can be destroyed
on any thread long after the content process is done with the device,
yet it held a `VkImage`, a `VkDeviceMemory`, `VkSemaphore`s and a raw
`mDeviceId`. Teardown can attempt wgpu-core registry lookups on
already-dropped IDs, panicking with `Queue[Id(2,3)] does not exist`. The
`IsDeviceActive()` guard "fixed" that by skipping destruction and
leaking instead (`VUID-vkDestroyDevice-device-05137`). Holding onto the
Vulkan objects was not needed since the compositor only ever imports the
FDs, which already hold the underlying resources alive. The WebGPU
parent actor can do the same.

Differential Revision: https://phabricator.services.mozilla.com/D320959
2026-08-27 13:15:21 +00:00

175 lines
5.5 KiB
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 "SharedTextureDMABuf.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/webgpu/WebGPUParent.h"
#include "mozilla/widget/DMABufDevice.h"
#include "mozilla/widget/DMABufSurface.h"
namespace mozilla::webgpu {
// static
UniquePtr<SharedTextureDMABuf> SharedTextureDMABuf::Create(
WebGPUParent* aParent, const ffi::WGPUDeviceId aDeviceId,
const uint32_t aWidth, const uint32_t aHeight,
const struct ffi::WGPUTextureFormat aFormat,
const ffi::WGPUTextureUsages aUsage) {
if (aFormat.tag != ffi::WGPUTextureFormat_Bgra8Unorm) {
gfxCriticalNoteOnce << "Non supported format: " << aFormat.tag;
return nullptr;
}
auto* context = aParent->GetContext();
int32_t rawFd = -1;
ffi::WGPUDMABufInfo dmaBufInfo = ffi::wgpu_vkimage_create_with_dma_buf(
context, aDeviceId, aWidth, aHeight, &rawFd);
if (!dmaBufInfo.is_valid || rawFd < 0) {
gfxCriticalNoteOnce << "Failed to create dma-buf backed VkImage";
return nullptr;
}
RefPtr<gfx::FileHandleWrapper> fd =
new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
MOZ_ASSERT(dmaBufInfo.plane_count <= 3);
if (dmaBufInfo.plane_count > 3) {
gfxCriticalNoteOnce << "Invalid plane count";
return nullptr;
}
RefPtr<DMABufSurface> surface = DMABufSurfaceRGBA::CreateDMABufSurface(
std::move(fd), dmaBufInfo, aWidth, aHeight);
if (!surface) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return nullptr;
}
layers::SurfaceDescriptor desc;
if (!surface->Serialize(desc)) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return nullptr;
}
const auto sdType = desc.type();
if (sdType != layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return nullptr;
}
return MakeUnique<SharedTextureDMABuf>(
aWidth, aHeight, aFormat, aUsage, std::move(surface),
desc.get_SurfaceDescriptorDMABuf(), dmaBufInfo);
}
SharedTextureDMABuf::SharedTextureDMABuf(
const uint32_t aWidth, const uint32_t aHeight,
const struct ffi::WGPUTextureFormat aFormat,
const ffi::WGPUTextureUsages aUsage, RefPtr<DMABufSurface>&& aSurface,
const layers::SurfaceDescriptorDMABuf& aSurfaceDescriptor,
const ffi::WGPUDMABufInfo& aDMABufInfo)
: SharedTexture(aWidth, aHeight, aFormat, aUsage),
mSurface(std::move(aSurface)),
mSurfaceDescriptor(aSurfaceDescriptor),
mDMABufInfo(aDMABufInfo) {}
SharedTextureDMABuf::~SharedTextureDMABuf() = default;
void SharedTextureDMABuf::CleanForRecycling() {
SharedTexture::CleanForRecycling();
mSemaphoreFd = nullptr;
}
Maybe<layers::SurfaceDescriptor> SharedTextureDMABuf::ToSurfaceDescriptor() {
MOZ_ASSERT(mSubmissionIndex > 0);
layers::SurfaceDescriptor sd;
if (!mSurface->Serialize(sd)) {
return Nothing();
}
if (sd.type() != layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf) {
return Nothing();
}
auto& sdDMABuf = sd.get_SurfaceDescriptorDMABuf();
sdDMABuf.semaphoreFd() = mSemaphoreFd;
return Some(sd);
}
void SharedTextureDMABuf::GetSnapshot(const ipc::Shmem& aDestShmem,
size_t aDestStride) {
const RefPtr<gfx::SourceSurface> surface = mSurface->GetAsSourceSurface();
if (!surface) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
gfxCriticalNoteOnce << "Failed to get SourceSurface from DMABufSurface";
return;
}
const RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
if (!dataSurface) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return;
}
gfx::DataSourceSurface::ScopedMap map(dataSurface,
gfx::DataSourceSurface::READ);
if (!map.IsMapped()) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return;
}
uint8_t* src = static_cast<uint8_t*>(map.GetData());
uint8_t* dst = aDestShmem.get<uint8_t>();
const size_t src_stride = static_cast<size_t>(map.GetStride());
const size_t bytesPerRow = static_cast<size_t>(mWidth) * 4;
MOZ_RELEASE_ASSERT(src_stride >= bytesPerRow);
MOZ_RELEASE_ASSERT(aDestStride >= bytesPerRow);
for (uint32_t y = 0; y < mHeight; y++) {
memcpy(dst, src, bytesPerRow);
if (bytesPerRow < aDestStride) {
memset(dst + bytesPerRow, 0, aDestStride - bytesPerRow);
}
src += src_stride;
dst += aDestStride;
}
}
UniqueFileHandle SharedTextureDMABuf::CloneDmaBufFd() {
return mSurfaceDescriptor.fds()[0]->ClonePlatformHandle();
}
void SharedTextureDMABuf::onBeforeQueueSubmit(
const ffi::WGPUGlobal* aContext, RawId aDeviceId, RawId aQueueId,
nsTArray<ffi::WGPUVkSemaphoreHandle>& aSignalSemaphores) {
SharedTexture::onBeforeQueueSubmit(aContext, aDeviceId, aQueueId,
aSignalSemaphores);
int32_t rawFd = -1;
auto semaphore = ffi::wgpu_vksemaphore_create_signal_semaphore(
aContext, aDeviceId, aQueueId, &rawFd);
if (!semaphore) {
gfxCriticalNoteOnce << "Failed to create VkSemaphore";
return;
}
// Ownership transfers to wgpu_server_queue_submit(), which destroys the
// semaphore once the submission that signals it has completed.
aSignalSemaphores.AppendElement(semaphore);
if (rawFd < 0) {
gfxCriticalNoteOnce << "Failed to get fd from VkSemaphore";
return;
}
mSemaphoreFd = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
}
} // namespace mozilla::webgpu