зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1755704 - Part 1. Add support for reading back the front buffer for WebGPU. r=gfx-reviewers,lsalzman
This reworks the CanvasManagerChild::GetSnapshot method to work with WebGPU. This will allow it to be called for a WebGPU context from any thread, which is useful for screenshots. Differential Revision: https://phabricator.services.mozilla.com/D144308
This commit is contained in:
Родитель
49e2102b48
Коммит
720de7a009
|
@ -252,12 +252,14 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
|
|||
Maybe<int32_t> childId;
|
||||
HTMLCanvasElement* canvasElement;
|
||||
RefPtr<gfx::SourceSurface> surface;
|
||||
layers::CompositableHandle handle;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
hasAlpha = !mData.mIsOpaque;
|
||||
isAlphaPremult = mData.mIsAlphaPremult;
|
||||
originPos = mData.mOriginPos;
|
||||
handle = mData.mHandle;
|
||||
managerId = mContextManagerId;
|
||||
childId = mContextChildId;
|
||||
canvasElement = mCanvasElement;
|
||||
|
@ -311,7 +313,9 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
|
|||
// We don't have a usable surface, and the context lives in the compositor
|
||||
// process.
|
||||
return gfx::CanvasManagerChild::Get()->GetSnapshot(
|
||||
managerId.value(), childId.value(), hasAlpha);
|
||||
managerId.value(), childId.value(), handle,
|
||||
hasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8,
|
||||
hasAlpha && !isAlphaPremult, originPos == gl::OriginPos::BottomLeft);
|
||||
}
|
||||
|
||||
// If we don't have any protocol IDs, or an existing surface, it is possible
|
||||
|
|
|
@ -78,6 +78,7 @@ void CanvasContext::Configure(const dom::GPUCanvasConfiguration& aDesc) {
|
|||
} else if (mOffscreenCanvas) {
|
||||
dom::OffscreenCanvasDisplayData data;
|
||||
data.mSize = {mWidth, mHeight};
|
||||
data.mIsOpaque = false;
|
||||
data.mHandle = mHandle;
|
||||
mOffscreenCanvas->UpdateDisplayData(data);
|
||||
}
|
||||
|
|
|
@ -640,6 +640,36 @@ static void PresentCallback(ffi::WGPUBufferMapAsyncStatus status,
|
|||
delete req;
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::GetFrontBufferSnapshot(
|
||||
IProtocol* aProtocol, const CompositableHandle& aHandle,
|
||||
Maybe<Shmem>& aShmem, gfx::IntSize& aSize) {
|
||||
const auto& lookup = mCanvasMap.find(aHandle.Value());
|
||||
if (lookup == mCanvasMap.end()) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<PresentationData> data = lookup->second.get();
|
||||
aSize = data->mTextureHost->GetSize();
|
||||
uint32_t stride =
|
||||
aSize.width * BytesPerPixel(data->mTextureHost->GetFormat());
|
||||
uint32_t len = data->mRowCount * stride;
|
||||
Shmem shmem;
|
||||
if (!AllocShmem(len, ipc::Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
uint8_t* dst = shmem.get<uint8_t>();
|
||||
uint8_t* src = data->mTextureHost->GetBuffer();
|
||||
for (uint32_t row = 0; row < data->mRowCount; ++row) {
|
||||
memcpy(dst, src, stride);
|
||||
src += data->mTargetPitch;
|
||||
dst += stride;
|
||||
}
|
||||
|
||||
aShmem.emplace(std::move(shmem));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvSwapChainPresent(
|
||||
const CompositableHandle& aHandle, RawId aTextureId,
|
||||
RawId aCommandEncoderId) {
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include "WebGPUTypes.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
namespace mozilla::webgpu {
|
||||
class ErrorBuffer;
|
||||
class PresentationData;
|
||||
|
||||
|
@ -90,6 +89,11 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
ipc::IPCResult RecvDevicePopErrorScope(
|
||||
RawId aSelfId, DevicePopErrorScopeResolver&& aResolver);
|
||||
|
||||
ipc::IPCResult GetFrontBufferSnapshot(IProtocol* aProtocol,
|
||||
const CompositableHandle& aHandle,
|
||||
Maybe<Shmem>& aShmem,
|
||||
gfx::IntSize& aSize);
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
|
@ -109,7 +113,6 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
std::unordered_map<uint64_t, ErrorScopeStack> mErrorScopeMap;
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::webgpu
|
||||
|
||||
#endif // WEBGPU_PARENT_H_
|
||||
|
|
|
@ -144,13 +144,15 @@ RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() {
|
|||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
||||
uint32_t aManagerId, int32_t aProtocolId, bool aHasAlpha) {
|
||||
uint32_t aManagerId, int32_t aProtocolId,
|
||||
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
|
||||
bool aPremultiply, bool aYFlip) {
|
||||
if (!CanSend()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
webgl::FrontBufferSnapshotIpc res;
|
||||
if (!SendGetSnapshot(aManagerId, aProtocolId, &res)) {
|
||||
if (!SendGetSnapshot(aManagerId, aProtocolId, aHandle, &res)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -178,7 +180,7 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
|||
}
|
||||
|
||||
SurfaceFormat format =
|
||||
aHasAlpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
|
||||
IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
|
||||
RefPtr<DataSourceSurface> surface =
|
||||
Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(),
|
||||
/* aZero */ false);
|
||||
|
@ -192,21 +194,32 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// The buffer we read back from WebGL is R8G8B8A8, not premultiplied and has
|
||||
// its rows inverted. For the general case, we want surfaces represented as
|
||||
// premultiplied B8G8R8A8, with its rows ordered top to bottom. Given this
|
||||
// path is used for screenshots/SurfaceFromElement, that's the representation
|
||||
// we need.
|
||||
if (aHasAlpha) {
|
||||
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
SurfaceFormat::R8G8B8A8, map.GetData(),
|
||||
map.GetStride(), format, size)) {
|
||||
// The buffer we may readback from the canvas could be R8G8B8A8, not
|
||||
// premultiplied, and/or has its rows iverted. For the general case, we want
|
||||
// surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
|
||||
// to bottom. Given this path is used for screenshots/SurfaceFromElement,
|
||||
// that's the representation we need.
|
||||
if (aYFlip) {
|
||||
if (aPremultiply) {
|
||||
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
aFormat, map.GetData(), map.GetStride(), format,
|
||||
size)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else if (aPremultiply) {
|
||||
if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
SurfaceFormat::R8G8B8X8, map.GetData(),
|
||||
map.GetStride(), format, size)) {
|
||||
if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/gfx/PCanvasManagerChild.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -29,9 +30,10 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
|||
|
||||
explicit CanvasManagerChild(uint32_t aId);
|
||||
uint32_t Id() const { return mId; }
|
||||
already_AddRefed<DataSourceSurface> GetSnapshot(uint32_t aManagerId,
|
||||
int32_t aProtocolId,
|
||||
bool aHasAlpha);
|
||||
already_AddRefed<DataSourceSurface> GetSnapshot(
|
||||
uint32_t aManagerId, int32_t aProtocolId,
|
||||
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
|
||||
bool aPremultiply, bool aYFlip);
|
||||
void ActorDestroy(ActorDestroyReason aReason) override;
|
||||
|
||||
static CanvasManagerChild* Get();
|
||||
|
|
|
@ -111,7 +111,7 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvInitialize(
|
|||
|
||||
mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
|
||||
const uint32_t& aManagerId, const int32_t& aProtocolId,
|
||||
webgl::FrontBufferSnapshotIpc* aResult) {
|
||||
const CompositableHandle& aHandle, webgl::FrontBufferSnapshotIpc* aResult) {
|
||||
if (!aManagerId) {
|
||||
return IPC_FAIL(this, "invalid id");
|
||||
}
|
||||
|
@ -129,16 +129,33 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
|
|||
return IPC_FAIL(this, "invalid actor");
|
||||
}
|
||||
|
||||
if (actor->GetProtocolId() != ProtocolId::PWebGLMsgStart ||
|
||||
actor->GetSide() != mozilla::ipc::Side::ParentSide) {
|
||||
if (actor->GetSide() != mozilla::ipc::Side::ParentSide) {
|
||||
return IPC_FAIL(this, "unsupported actor");
|
||||
}
|
||||
|
||||
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
|
||||
webgl::FrontBufferSnapshotIpc buffer;
|
||||
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
switch (actor->GetProtocolId()) {
|
||||
case ProtocolId::PWebGLMsgStart: {
|
||||
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
|
||||
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
}
|
||||
} break;
|
||||
case ProtocolId::PWebGPUMsgStart: {
|
||||
RefPtr<webgpu::WebGPUParent> webgpu =
|
||||
static_cast<webgpu::WebGPUParent*>(actor);
|
||||
IntSize size;
|
||||
mozilla::ipc::IPCResult rv =
|
||||
webgpu->GetFrontBufferSnapshot(this, aHandle, buffer.shmem, size);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
}
|
||||
buffer.surfSize.x = static_cast<uint32_t>(size.width);
|
||||
buffer.surfSize.y = static_cast<uint32_t>(size.height);
|
||||
} break;
|
||||
default:
|
||||
return IPC_FAIL(this, "unsupported protocol");
|
||||
}
|
||||
|
||||
*aResult = std::move(buffer);
|
||||
|
|
|
@ -30,6 +30,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
|
|||
mozilla::ipc::IPCResult RecvInitialize(const uint32_t& aId);
|
||||
mozilla::ipc::IPCResult RecvGetSnapshot(
|
||||
const uint32_t& aManagerId, const int32_t& aProtocolId,
|
||||
const CompositableHandle& aHandle,
|
||||
webgl::FrontBufferSnapshotIpc* aResult);
|
||||
|
||||
private:
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
include protocol PWebGL;
|
||||
include protocol PWebGPU;
|
||||
|
||||
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
|
||||
using mozilla::webgl::FrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -39,7 +40,7 @@ parent:
|
|||
// intended to be used by the main thread in the content process to block
|
||||
// reading without having to block on the worker thread that owns the context
|
||||
// instance.
|
||||
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId) returns (FrontBufferSnapshotIpc ret);
|
||||
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId, CompositableHandle aHandle) returns (FrontBufferSnapshotIpc ret);
|
||||
};
|
||||
|
||||
} // gfx
|
||||
|
|
Загрузка…
Ссылка в новой задаче