mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-18 06:45:33 +00:00
![Lee Salzman](/assets/img/avatar_default.png)
This mainly provides DrawTargetWebgl, which implements the subset of the DrawTarget API necessary for integration with CanvasRenderingContext2D. It translates them to suitable commands for its internal ClientWebGLContext, which then manages remoting WebGL requests to the parent/GPU process. Currently two shaders are used for drawing Canvas2D primitives, but can be expanded in the future. These are an image shader and a solid color shader. The core of this implementation revolves around TexturePacker and TextureHandle, which cope with the necessity of frequently uploading SourceSurfaces for use with WebGL. TexturePacker implements a bin-packing algorithm for packing these uploads into texture pages, which can either be SharedTextures if they are reasonably small, or StandaloneTextures if they are too big to pack in a SharedTexture. Each upload is assigned a TextureHandle which is used to manage it in a move-to-front cache, so that we can easily eject TextureHandles from the back of the cache if we have too many. These TextureHandles are associated with the SourceSurface that spawned them to more easily manage their lifetimes. There are further dependent caches for dealing with blurred shadows and with text. Shadows are cached in an uploaded texture bound to the SourceSurface that generated them. Text is handled by caching entire runs in the GlyphCache (keyed by both their rendering parameters and their glyphs). The text is first rasterized to a surface and then uploaded to a texture in the GlyphCache which can be reused should the text be encountered again. To deal with commands we can't accelerate, a separate internal DrawTargetSkia is also maintained. The content of the WebGL framebuffer is copied into it so that drawing can then proceed in software from there. It remains in this fallover state until the next frame, when it resets back to using the WebGL framebuffer again. This acceleration is disabled by default. To enable it, you must toggle the pref "gfx.canvas.accelerated" to true. This should be suitably different from the naming of the previous SkiaGL prefs to not alias with them. There are a few dependent prefs that follow from the previous SkiaGL prefs for setting the size limitations for acceleration and also limitations for the internal texture cache. Differential Revision: https://phabricator.services.mozilla.com/D130388
217 lines
5.6 KiB
C++
217 lines
5.6 KiB
C++
/* -*- 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 "ShareableCanvasRenderer.h"
|
|
|
|
#include "mozilla/dom/WebGLTypes.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/layers/TextureClientSharedSurface.h"
|
|
#include "mozilla/layers/CompositableForwarder.h"
|
|
#include "mozilla/layers/TextureForwarder.h"
|
|
|
|
#include "ClientWebGLContext.h"
|
|
#include "gfxUtils.h"
|
|
#include "GLScreenBuffer.h"
|
|
#include "nsICanvasRenderingContextInternal.h"
|
|
#include "SharedSurfaceGL.h"
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "mozilla/layers/AndroidHardwareBuffer.h"
|
|
#endif
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
ShareableCanvasRenderer::ShareableCanvasRenderer() {
|
|
MOZ_COUNT_CTOR(ShareableCanvasRenderer);
|
|
}
|
|
|
|
ShareableCanvasRenderer::~ShareableCanvasRenderer() {
|
|
MOZ_COUNT_DTOR(ShareableCanvasRenderer);
|
|
|
|
mFrontBufferFromDesc = nullptr;
|
|
DisconnectClient();
|
|
}
|
|
|
|
void ShareableCanvasRenderer::Initialize(const CanvasRendererData& aData) {
|
|
CanvasRenderer::Initialize(aData);
|
|
mCanvasClient = nullptr;
|
|
}
|
|
|
|
void ShareableCanvasRenderer::ClearCachedResources() {
|
|
CanvasRenderer::ClearCachedResources();
|
|
|
|
if (mCanvasClient) {
|
|
mCanvasClient->Clear();
|
|
}
|
|
}
|
|
|
|
void ShareableCanvasRenderer::DisconnectClient() {
|
|
if (mCanvasClient) {
|
|
mCanvasClient->OnDetach();
|
|
mCanvasClient = nullptr;
|
|
}
|
|
}
|
|
|
|
RefPtr<layers::TextureClient> ShareableCanvasRenderer::GetFrontBufferFromDesc(
|
|
const layers::SurfaceDescriptor& desc, TextureFlags flags) {
|
|
if (mFrontBufferFromDesc && mFrontBufferDesc == desc)
|
|
return mFrontBufferFromDesc;
|
|
mFrontBufferFromDesc = nullptr;
|
|
|
|
// Test the validity of aAllocator
|
|
const auto& compositableForwarder = GetForwarder();
|
|
if (!compositableForwarder) {
|
|
return nullptr;
|
|
}
|
|
const auto& textureForwarder = compositableForwarder->GetTextureForwarder();
|
|
|
|
auto format = gfx::SurfaceFormat::R8G8B8X8;
|
|
if (!mData.mIsOpaque) {
|
|
format = gfx::SurfaceFormat::R8G8B8A8;
|
|
|
|
if (!mData.mIsAlphaPremult) {
|
|
flags |= TextureFlags::NON_PREMULTIPLIED;
|
|
}
|
|
}
|
|
|
|
if (desc.type() !=
|
|
SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer) {
|
|
mFrontBufferFromDesc = SharedSurfaceTextureData::CreateTextureClient(
|
|
desc, format, mData.mSize, flags, textureForwarder);
|
|
} else {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
const SurfaceDescriptorAndroidHardwareBuffer& bufferDesc =
|
|
desc.get_SurfaceDescriptorAndroidHardwareBuffer();
|
|
RefPtr<AndroidHardwareBuffer> buffer =
|
|
AndroidHardwareBufferManager::Get()->GetBuffer(bufferDesc.bufferId());
|
|
if (!buffer) {
|
|
return nullptr;
|
|
}
|
|
// TextureClient is created only when AndroidHardwareBuffer does not own it.
|
|
mFrontBufferFromDesc = buffer->GetTextureClientOfSharedSurfaceTextureData(
|
|
desc, format, mData.mSize, flags, textureForwarder);
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
#endif
|
|
}
|
|
mFrontBufferDesc = desc;
|
|
return mFrontBufferFromDesc;
|
|
}
|
|
|
|
void ShareableCanvasRenderer::UpdateCompositableClient() {
|
|
if (!CreateCompositable()) {
|
|
return;
|
|
}
|
|
|
|
if (!IsDirty()) {
|
|
return;
|
|
}
|
|
ResetDirty();
|
|
|
|
const auto context = mData.GetContext();
|
|
if (!context) return;
|
|
const auto& provider = context->GetBufferProvider();
|
|
const auto webgl = context->AsWebgl();
|
|
|
|
const auto& forwarder = GetForwarder();
|
|
|
|
// -
|
|
|
|
auto flags = TextureFlags::IMMUTABLE;
|
|
if (!YIsDown()) {
|
|
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
|
|
}
|
|
if (IsOpaque()) {
|
|
flags |= TextureFlags::IS_OPAQUE;
|
|
}
|
|
|
|
// -
|
|
|
|
const auto fnGetExistingTc = [&]() -> RefPtr<TextureClient> {
|
|
if (webgl) {
|
|
const auto desc = webgl->GetFrontBuffer(nullptr);
|
|
if (!desc) return nullptr;
|
|
return GetFrontBufferFromDesc(*desc, flags);
|
|
}
|
|
if (provider) {
|
|
if (!provider->SetKnowsCompositor(forwarder)) {
|
|
gfxCriticalNote << "BufferProvider::SetForwarder failed";
|
|
return nullptr;
|
|
}
|
|
|
|
return provider->GetTextureClient();
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
// -
|
|
|
|
const auto fnMakeTcFromSnapshot = [&]() -> RefPtr<TextureClient> {
|
|
const auto& size = mData.mSize;
|
|
|
|
auto contentType = gfxContentType::COLOR;
|
|
if (!mData.mIsOpaque) {
|
|
contentType = gfxContentType::COLOR_ALPHA;
|
|
}
|
|
const auto surfaceFormat =
|
|
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
|
|
|
|
const auto tc =
|
|
mCanvasClient->CreateTextureClientForCanvas(surfaceFormat, size, flags);
|
|
if (!tc) {
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
TextureClientAutoLock tcLock(tc, OpenMode::OPEN_WRITE_ONLY);
|
|
if (!tcLock.Succeeded()) {
|
|
return nullptr;
|
|
}
|
|
|
|
const RefPtr<DrawTarget> dt = tc->BorrowDrawTarget();
|
|
|
|
const bool requireAlphaPremult = false;
|
|
const auto borrowed = BorrowSnapshot(requireAlphaPremult);
|
|
if (!borrowed) return nullptr;
|
|
|
|
dt->CopySurface(borrowed->mSurf, {{0, 0}, size}, {0, 0});
|
|
}
|
|
|
|
return tc;
|
|
};
|
|
|
|
// -
|
|
|
|
{
|
|
FirePreTransactionCallback();
|
|
|
|
// First, let's see if we can get a no-copy TextureClient from the canvas.
|
|
auto tc = fnGetExistingTc();
|
|
if (!tc) {
|
|
// Otherwise, snapshot the surface and copy into a TexClient.
|
|
tc = fnMakeTcFromSnapshot();
|
|
}
|
|
if (tc != mFrontBufferFromDesc) {
|
|
mFrontBufferFromDesc = nullptr;
|
|
}
|
|
|
|
if (!tc) {
|
|
NS_WARNING("Couldn't make TextureClient for CanvasRenderer.");
|
|
return;
|
|
}
|
|
|
|
mCanvasClient->UseTexture(tc);
|
|
|
|
FireDidTransactionCallback();
|
|
}
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|