/* -*- 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 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 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& forwarder = GetForwarder(); // - auto flags = TextureFlags::IMMUTABLE; if (!YIsDown()) { flags |= TextureFlags::ORIGIN_BOTTOM_LEFT; } if (IsOpaque()) { flags |= TextureFlags::IS_OPAQUE; } // With remote texture push callback, a new pushed remote texture is notifiled // from RemoteTextureMap to WebRenderImageHost. if (mData.mRemoteTextureOwnerIdOfPushCallback) { GetForwarder()->EnableRemoteTexturePushCallback( mCanvasClient, *mData.mRemoteTextureOwnerIdOfPushCallback, mData.mSize, flags); EnsurePipeline(); return; } // - const auto fnGetExistingTc = [&](const Maybe& aDesc) -> RefPtr { if (aDesc) { return GetFrontBufferFromDesc(*aDesc, flags); } if (provider) { if (!provider->SetKnowsCompositor(forwarder)) { gfxCriticalNote << "BufferProvider::SetForwarder failed"; return nullptr; } return provider->GetTextureClient(); } return nullptr; }; // - const auto fnMakeTcFromSnapshot = [&]() -> RefPtr { 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 dt = tc->BorrowDrawTarget(); const bool requireAlphaPremult = false; auto borrowed = BorrowSnapshot(requireAlphaPremult); if (!borrowed) { return nullptr; } dt->CopySurface(borrowed->mSurf, borrowed->mSurf->GetRect(), {0, 0}); } return tc; }; // - { FirePreTransactionCallback(); const auto desc = context->GetFrontBuffer(nullptr); if (desc && desc->type() == SurfaceDescriptor::TSurfaceDescriptorRemoteTexture) { const auto& forwarder = GetForwarder(); const auto& textureDesc = desc->get_SurfaceDescriptorRemoteTexture(); if (!mData.mIsAlphaPremult) { flags |= TextureFlags::NON_PREMULTIPLIED; } EnsurePipeline(); forwarder->UseRemoteTexture(mCanvasClient, textureDesc.textureId(), textureDesc.ownerId(), mData.mSize, flags); FireDidTransactionCallback(); return; } EnsurePipeline(); // Let's see if we can get a no-copy TextureClient from the canvas. auto tc = fnGetExistingTc(desc); 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