diff options
-rw-r--r-- | src/core/compositor.cpp | 66 | ||||
-rw-r--r-- | src/core/compositor.h | 29 | ||||
-rw-r--r-- | src/core/compositor_resource.h | 122 | ||||
-rw-r--r-- | src/core/compositor_resource_fence.cpp | 154 | ||||
-rw-r--r-- | src/core/compositor_resource_fence.h | 71 | ||||
-rw-r--r-- | src/core/compositor_resource_tracker.cpp | 251 | ||||
-rw-r--r-- | src/core/compositor_resource_tracker.h | 126 | ||||
-rw-r--r-- | src/core/core_chromium.pri | 5 | ||||
-rw-r--r-- | src/core/delegated_frame_node.cpp | 522 | ||||
-rw-r--r-- | src/core/delegated_frame_node.h | 61 | ||||
-rw-r--r-- | src/core/render_widget_host_view_qt.cpp | 7 |
11 files changed, 924 insertions, 490 deletions
diff --git a/src/core/compositor.cpp b/src/core/compositor.cpp index 5dd053718..2d955b917 100644 --- a/src/core/compositor.cpp +++ b/src/core/compositor.cpp @@ -39,6 +39,7 @@ #include "compositor.h" +#include "compositor_resource_tracker.h" #include "delegated_frame_node.h" #include <components/viz/common/resources/returned_resource.h> @@ -48,7 +49,7 @@ namespace QtWebEngineCore { Compositor::Compositor() - : m_chromiumCompositorData(new ChromiumCompositorData) + : m_resourceTracker(new CompositorResourceTracker) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -65,13 +66,6 @@ Compositor::~Compositor() DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void Compositor::setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - m_viewDelegate = viewDelegate; -} - void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -83,7 +77,7 @@ void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frame // should not be returned. // // TODO(juvaldma): Can there be a pending frame from the old client? - m_resourcesToRelease.clear(); + m_resourceTracker->returnResources(); m_frameSinkClient = frameSinkClient; } @@ -102,21 +96,19 @@ void Compositor::setNeedsBeginFrames(bool needsBeginFrames) m_needsBeginFrames = needsBeginFrames; } -void Compositor::submitFrame(viz::CompositorFrame frame) +void Compositor::submitFrame(viz::CompositorFrame frame, base::OnceClosure callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!m_havePendingFrame); + DCHECK(!m_submitCallback); - m_chromiumCompositorData->frameDevicePixelRatio = frame.metadata.device_scale_factor; - m_chromiumCompositorData->previousFrameData = std::move(m_chromiumCompositorData->frameData); - m_chromiumCompositorData->frameData = std::move(frame); - m_havePendingFrame = true; - - // Tell viewDelegate to call updatePaintNode() soon. - m_viewDelegate->update(); + m_pendingFrame = std::move(frame); + m_submitCallback = std::move(callback); + m_resourceTracker->submitResources( + m_pendingFrame, + base::BindOnce(&Compositor::runSubmitCallback, base::Unretained(this))); } -QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) +QSGNode *Compositor::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate) { // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // @@ -127,30 +119,44 @@ QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) if (!frameNode) frameNode = new DelegatedFrameNode; - frameNode->commit(m_chromiumCompositorData.data(), &m_resourcesToRelease, m_viewDelegate); - - if (m_havePendingFrame) { - m_havePendingFrame = false; - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); + if (!m_updatePaintNodeShouldCommit) { + frameNode->commit(m_committedFrame, viz::CompositorFrame(), m_resourceTracker.get(), viewDelegate); + return frameNode; } - if (m_chromiumCompositorData->frameData.metadata.request_presentation_feedback) + m_updatePaintNodeShouldCommit = false; + + if (m_committedFrame.metadata.request_presentation_feedback) content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_chromiumCompositorData->frameData.metadata.frame_token)); + base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_committedFrame.metadata.frame_token)); + + m_resourceTracker->commitResources(); + frameNode->commit(m_pendingFrame, m_committedFrame, m_resourceTracker.get(), viewDelegate); + m_committedFrame = std::move(m_pendingFrame); + m_pendingFrame = viz::CompositorFrame(); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); return frameNode; } +void Compositor::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + m_updatePaintNodeShouldCommit = true; + std::move(m_submitCallback).Run(); +} + void Compositor::notifyFrameCommitted() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); m_beginFrameSource->DidFinishFrame(this); if (m_frameSinkClient) - m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourcesToRelease); - m_resourcesToRelease.clear(); + m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourceTracker->returnResources()); } void Compositor::sendPresentationFeedback(uint frame_token) diff --git a/src/core/compositor.h b/src/core/compositor.h index 4852d0590..d34255d26 100644 --- a/src/core/compositor.h +++ b/src/core/compositor.h @@ -42,6 +42,10 @@ #include <base/memory/weak_ptr.h> #include <components/viz/common/frame_sinks/begin_frame_source.h> +#include <components/viz/common/quads/compositor_frame.h> + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> @@ -51,7 +55,6 @@ class QSGNode; QT_END_NAMESPACE namespace viz { -class CompositorFrame; struct ReturnedResource; namespace mojom { class CompositorFrameSinkClient; @@ -60,8 +63,8 @@ class CompositorFrameSinkClient; namespace QtWebEngineCore { +class CompositorResourceTracker; class RenderWidgetHostViewQtDelegate; -class ChromiumCompositorData; // Receives viz::CompositorFrames from child compositors and provides QSGNodes // to the Qt Quick renderer. @@ -71,10 +74,10 @@ class ChromiumCompositorData; // Step 1. A new CompositorFrame is received from child compositors and handed // off to submitFrame(). The new frame will start off in a pending state. // -// Step 2. Once the new frame is ready to be rendered, Compositor will call -// update() on the delegate. +// Step 2. Once the new frame is ready to be rendered, Compositor will notify +// the client by running the callback given to submitFrame(). // -// Step 3. Once the delegate is ready to render, updatePaintNode() should be +// Step 3. Once the client is ready to render, updatePaintNode() should be // called to receive the scene graph for the new frame. This call will commit // the pending frame. Until the next frame is ready, all subsequent calls to // updatePaintNode() will keep using this same committed frame. @@ -87,15 +90,14 @@ public: explicit Compositor(); ~Compositor() override; - void setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate); void setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient); void setNeedsBeginFrames(bool needsBeginFrames); - void submitFrame(viz::CompositorFrame frame); - - QSGNode *updatePaintNode(QSGNode *oldNode); + void submitFrame(viz::CompositorFrame frame, base::OnceClosure callback); + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate); private: + void runSubmitCallback(); void notifyFrameCommitted(); void sendPresentationFeedback(uint frame_token); @@ -103,12 +105,13 @@ private: bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) override; void OnBeginFrameSourcePausedChanged(bool paused) override; - std::vector<viz::ReturnedResource> m_resourcesToRelease; - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; - RenderWidgetHostViewQtDelegate *m_viewDelegate = nullptr; + viz::CompositorFrame m_committedFrame; + viz::CompositorFrame m_pendingFrame; + base::OnceClosure m_submitCallback; + std::unique_ptr<CompositorResourceTracker> m_resourceTracker; std::unique_ptr<viz::SyntheticBeginFrameSource> m_beginFrameSource; viz::mojom::CompositorFrameSinkClient *m_frameSinkClient = nullptr; - bool m_havePendingFrame = false; + bool m_updatePaintNodeShouldCommit = false; bool m_needsBeginFrames = false; base::WeakPtrFactory<Compositor> m_weakPtrFactory{this}; diff --git a/src/core/compositor_resource.h b/src/core/compositor_resource.h new file mode 100644 index 000000000..c40ce3d5e --- /dev/null +++ b/src/core/compositor_resource.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_H +#define COMPOSITOR_RESOURCE_H + +#include <base/memory/ref_counted.h> +#include <components/viz/common/resources/transferable_resource.h> + +#include <QtCore/qglobal.h> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +# include "compositor_resource_fence.h" +#endif + +namespace viz { +class SharedBitmap; +} // namespace viz + +namespace QtWebEngineCore { + +using CompositorResourceId = quint32; + +// A resource (OpenGL texture or software shared bitmap). +// +// - Created by the CompositorResourceTracker from a newly submitted +// CompositorFrame's resource_list. +// +// - Until the frame is committed, its resources are in a 'pending' state and +// are inaccessible from outside the CompositorResourceTracker. +// +// - Once the frame is committed, its resources can be found via +// CompositorResourceTracker::findResource. +// +// - A committed resource's fields may not be updated and are safe to use from +// other threads without synchronization (unless noted otherwise). +struct CompositorResource : viz::TransferableResource +{ + CompositorResource(const viz::TransferableResource &tr) : viz::TransferableResource(tr) {} + + // Counts the number of times this resource has been encountered in + // CompositorFrames' resource lists. + // + // Corresponds to viz::ReturnedResource::count. + // + // Updated by CompositorResourceTracker on UI thread. + int import_count = 1; + + // Identifies the last frame that needed this resource. Used by + // CompositorResourceTracker to return unused resources back to child + // compositors. + // + // Updated by CompositorResourceTracker on UI thread. + quint32 last_used_for_frame = 0; + + // Bitmap (if is_software). + std::unique_ptr<viz::SharedBitmap> bitmap; + +#if QT_CONFIG(opengl) + // OpenGL texture id (if !is_software). + quint32 texture_id = 0; + + // Should be waited on before using the texture (non-null if !is_software). + scoped_refptr<CompositorResourceFence> texture_fence; +#endif // QT_CONFIG(opengl) +}; + +inline bool operator<(const CompositorResource &r1, const CompositorResource &r2) +{ + return r1.id < r2.id; +} + +inline bool operator<(const CompositorResource &r, CompositorResourceId id) +{ + return r.id < id; +} + +inline bool operator<(CompositorResourceId id, const CompositorResource &r) +{ + return id < r.id; +} + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_H diff --git a/src/core/compositor_resource_fence.cpp b/src/core/compositor_resource_fence.cpp new file mode 100644 index 000000000..b3f868d43 --- /dev/null +++ b/src/core/compositor_resource_fence.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_fence.h" + +#include <ui/gl/gl_context.h> + +#include <QtGui/qopenglcontext.h> + +namespace QtWebEngineCore { + +void CompositorResourceFence::wait() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + Q_ASSERT(context); + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); + resolved = true; + } + + if (eglClientWaitSyncKHR) + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglClientWaitSyncKHR(m_sync.egl.display, m_sync.egl.sync, 0, EGL_FOREVER_KHR); + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); + static WaitSyncPtr glWaitSync_ = 0; + if (!glWaitSync_) { + glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); + Q_ASSERT(glWaitSync_); + } + glWaitSync_(m_sync.arb.sync, 0, GL_TIMEOUT_IGNORED); + break; + } + + release(); +} + +void CompositorResourceFence::release() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) + return; + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); + resolved = true; + } + + if (eglDestroySyncKHR) { + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglDestroySyncKHR(m_sync.egl.display, m_sync.egl.sync); + m_sync.reset(); + } + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); + static DeleteSyncPtr glDeleteSync_ = 0; + if (!glDeleteSync_) { + glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); + Q_ASSERT(glDeleteSync_); + } + glDeleteSync_(m_sync.arb.sync); + m_sync.reset(); + break; + } + // If Chromium was able to create a sync, we should have been able to handle its type here too. + Q_ASSERT(!m_sync); +} + +// static +scoped_refptr<CompositorResourceFence> CompositorResourceFence::create() +{ + if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { + std::unique_ptr<gl::GLFence> glFence{gl::GLFence::Create()}; + return base::MakeRefCounted<CompositorResourceFence>(glFence->Transfer()); + } + return nullptr; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor_resource_fence.h b/src/core/compositor_resource_fence.h new file mode 100644 index 000000000..1c2ea3695 --- /dev/null +++ b/src/core/compositor_resource_fence.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_FENCE_H +#define COMPOSITOR_RESOURCE_FENCE_H + +#include <base/memory/ref_counted.h> +#include <ui/gl/gl_fence.h> + +namespace QtWebEngineCore { + +// Sync object created on GPU thread and consumed on render thread. +class CompositorResourceFence final : public base::RefCountedThreadSafe<CompositorResourceFence> +{ +public: + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); + + CompositorResourceFence() {} + CompositorResourceFence(const gl::TransferableFence &sync) : m_sync(sync) {}; + ~CompositorResourceFence() { release(); } + + // May be used only by Qt Quick render thread. + void wait(); + void release(); + + // May be used only by GPU thread. + static scoped_refptr<CompositorResourceFence> create(); + +private: + gl::TransferableFence m_sync; +}; + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_FENCE_H diff --git a/src/core/compositor_resource_tracker.cpp b/src/core/compositor_resource_tracker.cpp new file mode 100644 index 000000000..c1de37b33 --- /dev/null +++ b/src/core/compositor_resource_tracker.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_tracker.h" + +#include "chromium_gpu_helper.h" +#include "render_widget_host_view_qt_delegate.h" + +#include <base/message_loop/message_loop.h> +#include <components/viz/common/quads/compositor_frame.h> +#include <components/viz/common/resources/returned_resource.h> +#include <components/viz/service/display_embedder/server_shared_bitmap_manager.h> +#include <content/browser/browser_main_loop.h> +#include <content/public/browser/browser_thread.h> +#include <gpu/command_buffer/service/mailbox_manager.h> +#include <gpu/command_buffer/service/sync_point_manager.h> + +namespace QtWebEngineCore { + +CompositorResourceTracker::CompositorResourceTracker() +{} + +CompositorResourceTracker::~CompositorResourceTracker() +{} + +void CompositorResourceTracker::submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!m_submitCallback); + DCHECK(m_pendingResources.empty()); + DCHECK(m_pendingImports.empty()); + DCHECK(m_pendingResourceUpdates == 0); + + m_submitCallback = std::move(callback); + + m_pendingResources.reserve(frame.resource_list.size()); + m_pendingImports.reserve(frame.resource_list.size()); + + for (const viz::TransferableResource &transferableResource : frame.resource_list) { + auto it = m_committedResources.find(transferableResource.id); + if (it != m_committedResources.end()) + m_pendingImports.push_back(&*it); + else + m_pendingResources.emplace_back(transferableResource); + } + + if (m_pendingResources.empty()) { + scheduleRunSubmitCallback(); + return; + } + + m_pendingResourceUpdates = m_pendingResources.size(); + + std::vector<CompositorResource *> batch; + batch.reserve(m_pendingResources.size()); + + for (CompositorResource &resource : m_pendingResources) { + if (resource.is_software) + updateBitmap(&resource); + else if (!scheduleUpdateMailbox(&resource)) + batch.push_back(&resource); + } + + if (!batch.empty()) + scheduleUpdateMailboxes(std::move(batch)); +} + +void CompositorResourceTracker::commitResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(m_pendingResourceUpdates == 0); + + for (CompositorResource *resource : m_pendingImports) + resource->import_count++; + m_pendingImports.clear(); + + m_committedResources.insert(std::make_move_iterator(m_pendingResources.begin()), + std::make_move_iterator(m_pendingResources.end())); + m_pendingResources.clear(); + + ++m_committedFrameId; +} + +std::vector<viz::ReturnedResource> CompositorResourceTracker::returnResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::vector<viz::ReturnedResource> returnedResources; + base::EraseIf(m_committedResources, [&](const CompositorResource &resource) { + if (resource.last_used_for_frame != m_committedFrameId) { + viz::ReturnedResource returnedResource; + returnedResource.id = resource.id; + returnedResource.count = resource.import_count; + returnedResources.push_back(std::move(returnedResource)); + return true; + } + return false; + }); + return returnedResources; +} + +const CompositorResource *CompositorResourceTracker::findResource(CompositorResourceId id) const +{ + auto it = m_committedResources.find(id); + DCHECK(it != m_committedResources.end()); + + const_cast<CompositorResource &>(*it).last_used_for_frame = m_committedFrameId; + + return &*it; +} + +void CompositorResourceTracker::updateBitmap(CompositorResource *resource) +{ + content::BrowserMainLoop *browserMainLoop = content::BrowserMainLoop::GetInstance(); + viz::ServerSharedBitmapManager *bitmapManager = browserMainLoop->GetServerSharedBitmapManager(); + + resource->bitmap = bitmapManager->GetSharedBitmapFromId( + resource->size, + viz::BGRA_8888, + resource->mailbox_holder.mailbox); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +} + +quint32 CompositorResourceTracker::consumeMailbox(const gpu::MailboxHolder &mailboxHolder) +{ +#if QT_CONFIG(opengl) + gpu::MailboxManager *mailboxManager = mailbox_manager(); + DCHECK(mailboxManager); + if (mailboxHolder.sync_token.HasData()) + mailboxManager->PullTextureUpdates(mailboxHolder.sync_token); + return service_id(mailboxManager->ConsumeTexture(mailboxHolder.mailbox)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +bool CompositorResourceTracker::scheduleUpdateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + gpu::SyncPointManager *syncPointManager = sync_point_manager(); + DCHECK(syncPointManager); + return syncPointManager->WaitOutOfOrder( + resource->mailbox_holder.sync_token, + base::BindOnce(&CompositorResourceTracker::updateMailbox, + m_weakPtrFactory.GetWeakPtr(), + resource)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + resource->texture_fence = CompositorResourceFence::create(); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleUpdateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); + DCHECK(gpuTaskRunner); + gpuTaskRunner->PostTask( + FROM_HERE, + base::BindOnce(&CompositorResourceTracker::updateMailboxes, + m_weakPtrFactory.GetWeakPtr(), + std::move(resources))); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + for (CompositorResource *resource : resources) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + + scoped_refptr<CompositorResourceFence> fence = CompositorResourceFence::create(); + + for (CompositorResource *resource : resources) + resource->texture_fence = fence; + + if ((m_pendingResourceUpdates -= resources.size()) == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleRunSubmitCallback() +{ + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&CompositorResourceTracker::runSubmitCallback, + base::Unretained(this))); // FIXME(juvaldma) +} + +void CompositorResourceTracker::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::move(m_submitCallback).Run(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor_resource_tracker.h b/src/core/compositor_resource_tracker.h new file mode 100644 index 000000000..4c8dc64fc --- /dev/null +++ b/src/core/compositor_resource_tracker.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_TRACKER_H +#define COMPOSITOR_RESOURCE_TRACKER_H + +#include "compositor_resource.h" + +#include <base/callback.h> +#include <base/containers/flat_set.h> +#include <base/memory/weak_ptr.h> + +#include <atomic> +#include <vector> + +namespace viz { +class CompositorFrame; +struct ReturnedResource; +} // namespace viz + +namespace gpu { +struct MailboxHolder; +} // namespace gpu + +namespace QtWebEngineCore { + +// Ensures resources are not used before they are ready. +// +// The life cycle of a frame's resources: +// +// Step 1. A new CompositorFrame is received and given to submitResources(). +// The frame's resources will extracted and initialized to a pending state. +// +// Step 2. Once the new resources are ready to be committed, +// CompositorResourceTracker will notify the client by running the callback +// given to submitResources(). +// +// Step 3. Once the client is ready to render, commitResources() should be +// called. This will commit all the pending resources, making them available +// via findResource(). +// +// Step 4. Once all the resources have been used (via findResource()), +// returnResources() may be called to return a list of all the resources which +// were *not* used since the last commitResources(). Go to step 1. +class CompositorResourceTracker final +{ +public: + CompositorResourceTracker(); + ~CompositorResourceTracker(); + + void submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback); + void commitResources(); + std::vector<viz::ReturnedResource> returnResources(); + + // The returned pointer is invalidated by the next call to commitFrame() or + // returnResources(). It should therefore not be stored in data structures + // but used immediately. + // + // Do not ask for resources which do not exist. + const CompositorResource *findResource(CompositorResourceId id) const; + +private: + void updateBitmap(CompositorResource *resource); + + quint32 consumeMailbox(const gpu::MailboxHolder &mailboxHolder); + + bool scheduleUpdateMailbox(CompositorResource *resource); + void updateMailbox(CompositorResource *resource); + + void scheduleUpdateMailboxes(std::vector<CompositorResource *> resources); + void updateMailboxes(std::vector<CompositorResource *> resources); + + void scheduleRunSubmitCallback(); + void runSubmitCallback(); + + base::flat_set<CompositorResource> m_committedResources; + std::vector<CompositorResource> m_pendingResources; + std::vector<CompositorResource *> m_pendingImports; + base::OnceClosure m_submitCallback; + std::atomic<size_t> m_pendingResourceUpdates{0}; + quint32 m_committedFrameId = 0; + + base::WeakPtrFactory<CompositorResourceTracker> m_weakPtrFactory{this}; + + DISALLOW_COPY_AND_ASSIGN(CompositorResourceTracker); +}; + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_TRACKER_H diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 92fefe533..b65ffb560 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -55,6 +55,7 @@ SOURCES = \ common/qt_messages.cpp \ common/user_script_data.cpp \ compositor.cpp \ + compositor_resource_tracker.cpp \ content_client_qt.cpp \ content_browser_client_qt.cpp \ content_main_delegate_qt.cpp \ @@ -142,6 +143,8 @@ HEADERS = \ common/qt_messages.h \ common/user_script_data.h \ compositor.h \ + compositor_resource.h \ + compositor_resource_tracker.h \ content_client_qt.h \ content_browser_client_qt.h \ content_main_delegate_qt.h \ @@ -262,10 +265,12 @@ qtConfig(webengine-printing-and-pdf) { contains(QT_CONFIG, opengl) { SOURCES += \ + compositor_resource_fence.cpp \ yuv_video_node.cpp \ stream_video_node.cpp HEADERS += \ + compositor_resource_fence.h \ yuv_video_node.h \ stream_video_node.h } diff --git a/src/core/delegated_frame_node.cpp b/src/core/delegated_frame_node.cpp index 84fde7ca2..c4a6d8078 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/delegated_frame_node.cpp @@ -49,17 +49,14 @@ #include "delegated_frame_node.h" #include "chromium_gpu_helper.h" -#include "ozone/gl_surface_qt.h" #include "stream_video_node.h" #include "type_conversion.h" #include "yuv_video_node.h" +#include "compositor_resource_tracker.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_task_runner_handle.h" #include "cc/base/math_util.h" #include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/render_pass_draw_quad.h" @@ -68,14 +65,8 @@ #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/bsp_tree.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "content/browser/browser_main_loop.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_fence.h" #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -121,7 +112,7 @@ namespace QtWebEngineCore { #ifndef QT_NO_OPENGL class MailboxTexture : public QSGTexture, protected QOpenGLFunctions { public: - MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize); + MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1); ~MailboxTexture(); // QSGTexture: int textureId() const override { return m_textureId; } @@ -130,14 +121,9 @@ public: bool hasMipmaps() const override { return false; } void bind() override; - void setHasAlphaChannel(bool hasAlpha) { m_hasAlpha = hasAlpha; } - gpu::MailboxHolder &mailboxHolder() { return m_mailboxHolder; } - void fetchTexture(gpu::MailboxManager *mailboxManager); - void setTarget(GLenum target); - private: - gpu::MailboxHolder m_mailboxHolder; int m_textureId; + scoped_refptr<CompositorResourceFence> m_fence; QSize m_textureSize; bool m_hasAlpha; GLenum m_target; @@ -150,20 +136,6 @@ private: friend class DelegatedFrameNode; }; #endif // QT_NO_OPENGL -class ResourceHolder { -public: - ResourceHolder(const viz::TransferableResource &resource); - QSharedPointer<QSGTexture> initTexture(bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - QSGTexture *texture() const { return m_texture.data(); } - viz::ReturnedResource returnResource(); - void incImportCount() { ++m_importCount; } - bool needsToFetch() const { return !m_resource.is_software && m_texture && !m_texture.data()->textureId(); } - -private: - QWeakPointer<QSGTexture> m_texture; - viz::TransferableResource m_resource; - int m_importCount; -}; class RectClipNode : public QSGClipNode { @@ -447,99 +419,12 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } #ifndef QT_NO_OPENGL -static void waitChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); - } - resolved = true; - } - - if (eglClientWaitSyncKHR) - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglClientWaitSyncKHR(sync->egl.display, sync->egl.sync, 0, EGL_FOREVER_KHR); - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); - static WaitSyncPtr glWaitSync_ = 0; - if (!glWaitSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); - Q_ASSERT(glWaitSync_); - } - glWaitSync_(sync->arb.sync, 0, GL_TIMEOUT_IGNORED); - break; - } -} - -static void deleteChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); - } - resolved = true; - } - - if (eglDestroySyncKHR) { - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglDestroySyncKHR(sync->egl.display, sync->egl.sync); - sync->reset(); - } - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); - static DeleteSyncPtr glDeleteSync_ = 0; - if (!glDeleteSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); - Q_ASSERT(glDeleteSync_); - } - glDeleteSync_(sync->arb.sync); - sync->reset(); - break; - } - // If Chromium was able to create a sync, we should have been able to handle its type here too. - Q_ASSERT(!*sync); -} - -MailboxTexture::MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize) - : m_mailboxHolder(mailboxHolder) - , m_textureId(0) - , m_textureSize(textureSize) - , m_hasAlpha(false) - , m_target(GL_TEXTURE_2D) +MailboxTexture::MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) + : m_textureId(resource->texture_id) + , m_fence(resource->texture_fence) + , m_textureSize(toQt(resource->size)) + , m_hasAlpha(hasAlphaChannel) + , m_target(target >= 0 ? target : GL_TEXTURE_2D) #if defined(USE_OZONE) , m_ownsTexture(false) #endif @@ -570,6 +455,8 @@ MailboxTexture::~MailboxTexture() void MailboxTexture::bind() { + if (m_fence) + m_fence->wait(); glBindTexture(m_target, m_textureId); #ifdef Q_OS_QNX if (m_target == GL_TEXTURE_EXTERNAL_OES) { @@ -586,85 +473,7 @@ void MailboxTexture::bind() } #endif } - -void MailboxTexture::setTarget(GLenum target) -{ - m_target = target; -} - -void MailboxTexture::fetchTexture(gpu::MailboxManager *mailboxManager) -{ - gpu::TextureBase *tex = ConsumeTexture(mailboxManager, m_target, m_mailboxHolder.mailbox); - - // The texture might already have been deleted (e.g. when navigating away from a page). - if (tex) { - m_textureId = service_id(tex); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - m_eglStreamData = eglstream_connect_consumer(tex); - } -#endif - } -} -#endif //QT_NO_OPENGL - -ResourceHolder::ResourceHolder(const viz::TransferableResource &resource) - : m_resource(resource) - , m_importCount(1) -{ -} - -QSharedPointer<QSGTexture> ResourceHolder::initTexture(bool quadNeedsBlending, RenderWidgetHostViewQtDelegate *apiDelegate) -{ - QSharedPointer<QSGTexture> texture = m_texture.toStrongRef(); - if (!texture) { - if (m_resource.is_software) { - Q_ASSERT(apiDelegate); - std::unique_ptr<viz::SharedBitmap> sharedBitmap = - content::BrowserMainLoop::GetInstance()->GetServerSharedBitmapManager()->GetSharedBitmapFromId( - m_resource.size, viz::BGRA_8888, m_resource.mailbox_holder.mailbox); - // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending - // to draw it but Chromium keeps this information in the quads. - // The input format is currently always Format_ARGB32_Premultiplied, so assume that all - // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion - // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to - // return false. - QImage::Format format = quadNeedsBlending ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - QImage image = sharedBitmap - ? QImage(sharedBitmap->pixels(), m_resource.size.width(), m_resource.size.height(), format) - : QImage(m_resource.size.width(), m_resource.size.height(), format); - texture.reset(apiDelegate->createTextureFromImage(image.copy())); - } else { -#ifndef QT_NO_OPENGL - texture.reset(new MailboxTexture(m_resource.mailbox_holder, toQt(m_resource.size))); - static_cast<MailboxTexture *>(texture.data())->setHasAlphaChannel(quadNeedsBlending); -#else - Q_UNREACHABLE(); -#endif - } - texture->setFiltering(m_resource.filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest); - m_texture = texture; - } - // All quads using a resource should request the same blending state. - Q_ASSERT(texture->hasAlphaChannel() || !quadNeedsBlending); - return texture; -} - -viz::ReturnedResource ResourceHolder::returnResource() -{ - viz::ReturnedResource returned; - // The ResourceProvider ensures that the resource isn't used by the parent compositor's GL - // context in the GPU process by inserting a sync point to be waited for by the child - // compositor's GL context. We don't need this since we are triggering the delegated frame - // ack directly from our rendering thread. At this point (in updatePaintNode) we know that - // a frame that was compositing any of those resources has already been swapped and we thus - // don't need to use this mechanism. - returned.id = m_resource.id; - returned.count = m_importCount; - m_importCount = 0; - return returned; -} - +#endif // !QT_NO_OPENGL RectClipNode::RectClipNode(const QRectF &rect) : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) @@ -676,9 +485,8 @@ RectClipNode::RectClipNode(const QRectF &rect) } DelegatedFrameNode::DelegatedFrameNode() - : m_numPendingSyncPoints(0) #if defined(USE_OZONE) && !defined(QT_NO_OPENGL) - , m_contextShared(true) + : m_contextShared(true) #endif { setFlag(UsePreprocess); @@ -704,22 +512,6 @@ DelegatedFrameNode::~DelegatedFrameNode() void DelegatedFrameNode::preprocess() { -#ifndef QT_NO_OPENGL - // With the threaded render loop the GUI thread has been unlocked at this point. - // We can now wait for the Chromium GPU thread to produce textures that will be - // rendered on our quads and fetch the IDs from the mailboxes we were given. - QList<MailboxTexture *> mailboxesToFetch; - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; - ResourceHolderIterator end = m_chromiumCompositorData->resourceHolders.constEnd(); - for (ResourceHolderIterator it = m_chromiumCompositorData->resourceHolders.constBegin(); it != end ; ++it) { - if ((*it)->needsToFetch()) - mailboxesToFetch.append(static_cast<MailboxTexture *>((*it)->texture())); - } - - if (!mailboxesToFetch.isEmpty()) - fetchAndSyncMailboxes(mailboxesToFetch); -#endif - // Then render any intermediate RenderPass in order. typedef QPair<int, QSharedPointer<QSGLayer> > Pair; for (const Pair &pair : qAsConst(m_sgObjects.renderPassLayers)) { @@ -746,8 +538,8 @@ static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState, // Compares if the frame data that we got from the Chromium Compositor is // *structurally* equivalent to the one of the previous frame. // If it is, we will just reuse and update the old nodes where necessary. -static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, - viz::CompositorFrame *previousFrameData) +static bool areRenderPassStructuresEqual(const viz::CompositorFrame *frameData, + const viz::CompositorFrame *previousFrameData) { if (!previousFrameData) return false; @@ -799,12 +591,12 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, return true; } -void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, - std::vector<viz::ReturnedResource> *resourcesToRelease, +void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame, + const viz::CompositorFrame &committedFrame, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { - m_chromiumCompositorData = chromiumCompositorData; - viz::CompositorFrame* frameData = &m_chromiumCompositorData->frameData; + const viz::CompositorFrame* frameData = &pendingFrame; if (frameData->render_pass_list.empty()) return; @@ -812,27 +604,11 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - const float devicePixelRatio = m_chromiumCompositorData->frameDevicePixelRatio; + const float devicePixelRatio = frameData->metadata.device_scale_factor; matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); if (QSGTransformNode::matrix() != matrix) setMatrix(matrix); - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceCandidates; - qSwap(m_chromiumCompositorData->resourceHolders, resourceCandidates); - - // A frame's resource_list only contains the new resources to be added to the scene. Quads can - // still reference resources that were added in previous frames. Add them to the list of - // candidates to be picked up by quads, it's then our responsibility to return unused resources - // to the producing child compositor. - for (unsigned i = 0; i < frameData->resource_list.size(); ++i) { - const viz::TransferableResource &res = frameData->resource_list.at(i); - if (QSharedPointer<ResourceHolder> resource = resourceCandidates.value(res.id)) - resource->incImportCount(); - else - resourceCandidates[res.id] = QSharedPointer<ResourceHolder>(new ResourceHolder(res)); - } - - frameData->resource_list.clear(); QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); @@ -846,26 +622,22 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside // of the visible area, we also have to rebuild the tree whenever the window is resized. const bool buildNewTree = - !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || + !areRenderPassStructuresEqual(frameData, &committedFrame) || m_sceneGraphNodes.empty() || viewportSize != m_previousViewportSize; - m_chromiumCompositorData->previousFrameData = viz::CompositorFrame(); - SGObjects previousSGObjects; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; if (buildNewTree) { // Keep the old objects in scope to hold a ref on layers, resources and textures // that we can re-use. Destroy the remaining objects before returning. - qSwap(m_sgObjects, previousSGObjects); + qSwap(m_sgObjects, m_previousSGObjects); // Discard the scene graph nodes from the previous frame. while (QSGNode *oldChain = firstChild()) delete oldChain; m_sceneGraphNodes.clear(); nodeHandler.reset(new DelegatedNodeTreeCreator(&m_sceneGraphNodes, apiDelegate)); } else { - // Save the texture strong refs so they only go out of scope when the method returns and - // the new vector of texture strong refs has been filled. - qSwap(m_sgObjects.textureStrongRefs, textureStrongRefs); + qSwap(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures); + qSwap(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures); nodeHandler.reset(new DelegatedNodeTreeUpdater(&m_sceneGraphNodes)); } // The RenderPasses list is actually a tree where a parent RenderPass is connected @@ -885,7 +657,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, if (pass != rootRenderPass) { QSharedPointer<QSGLayer> rpLayer; if (buildNewTree) { - rpLayer = findRenderPassLayer(pass->id, previousSGObjects.renderPassLayers); + rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers); if (!rpLayer) { rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer()); // Avoid any premature texture update since we need to wait @@ -914,7 +686,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } if (scissorRect.IsEmpty()) { - holdResources(pass, resourceCandidates); + holdResources(pass, resourceTracker); continue; } @@ -940,13 +712,13 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, targetRect.Intersect(quadState->clip_rect); targetRect.Intersect(scissorRect); if (targetRect.IsEmpty()) { - holdResources(quad, resourceCandidates); + holdResources(quad, resourceTracker); continue; } if (quadState->sorting_context_id != currentSortingContextId) { flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); currentSortingContextId = quadState->sorting_context_id; } @@ -968,28 +740,23 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } handleQuad(quad, currentLayerChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } - // Send resources of remaining candidates back to the child compositors so that - // they can be freed or reused. - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator - ResourceHolderIterator; - ResourceHolderIterator end = resourceCandidates.constEnd(); - for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) - resourcesToRelease->push_back((*it)->returnResource()); + copyMailboxTextures(); m_previousViewportSize = viewportSize; + m_previousSGObjects = SGObjects(); } void DelegatedFrameNode::flushPolygons( base::circular_deque<std::unique_ptr<viz::DrawPolygon>> *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (polygonQueue->empty()) @@ -1009,7 +776,7 @@ void DelegatedFrameNode::flushPolygons( polygon->TransformToLayerSpace(inverseTransform); handlePolygon(polygon, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); }; viz::BspTree(polygonQueue).TraverseWithActionHandler(&actionHandler); @@ -1019,20 +786,20 @@ void DelegatedFrameNode::handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { const viz::DrawQuad *quad = polygon->original_ref(); if (!polygon->is_split()) { handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } else { std::vector<gfx::QuadF> clipRegionList; polygon->ToQuads2D(&clipRegionList); for (const auto & clipRegion : clipRegionList) handleClippedQuad(quad, clipRegion, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } } @@ -1041,7 +808,7 @@ void DelegatedFrameNode::handleClippedQuad( const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (currentLayerChain) { @@ -1059,21 +826,21 @@ void DelegatedFrameNode::handleClippedQuad( currentLayerChain = clipNode; } handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } void DelegatedFrameNode::handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { switch (quad->material) { case viz::DrawQuad::RENDER_PASS: { const viz::RenderPassDrawQuad *renderPassQuad = viz::RenderPassDrawQuad::MaterialCast(quad); if (!renderPassQuad->mask_texture_size.IsEmpty()) { - ResourceHolder *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceTracker); Q_UNUSED(resource); // FIXME: QTBUG-67652 } QSGLayer *layer = @@ -1086,7 +853,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TEXTURE_CONTENT: { const viz::TextureDrawQuad *tquad = viz::TextureDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); QSGTexture *texture = initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate); QSizeF textureSize; @@ -1137,7 +904,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TILED_CONTENT: { const viz::TileDrawQuad *tquad = viz::TileDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); nodeHandler->setupTextureContentNode( initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate), toQt(quad->rect), toQt(tquad->tex_coord_rect), @@ -1147,17 +914,17 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::YUV_VIDEO_CONTENT: { const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad); - ResourceHolder *yResource = - findAndHoldResource(vquad->y_plane_resource_id(), resourceCandidates); - ResourceHolder *uResource = - findAndHoldResource(vquad->u_plane_resource_id(), resourceCandidates); - ResourceHolder *vResource = - findAndHoldResource(vquad->v_plane_resource_id(), resourceCandidates); - ResourceHolder *aResource = 0; + const CompositorResource *yResource = + findAndHoldResource(vquad->y_plane_resource_id(), resourceTracker); + const CompositorResource *uResource = + findAndHoldResource(vquad->u_plane_resource_id(), resourceTracker); + const CompositorResource *vResource = + findAndHoldResource(vquad->v_plane_resource_id(), resourceTracker); + const CompositorResource *aResource = nullptr; // This currently requires --enable-vp8-alpha-playback and // needs a video with alpha data to be triggered. if (vquad->a_plane_resource_id()) - aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceCandidates); + aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceTracker); nodeHandler->setupYUVVideoNode( initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()), @@ -1173,11 +940,9 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::STREAM_VIDEO_CONTENT: { const viz::StreamVideoDrawQuad *squad = viz::StreamVideoDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(squad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(squad->resource_id(), resourceTracker); MailboxTexture *texture = static_cast<MailboxTexture *>( - initAndHoldTexture(resource, quad->ShouldDrawWithBlending())); - // since this is not default TEXTURE_2D type - texture->setTarget(GL_TEXTURE_EXTERNAL_OES); + initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate, GL_TEXTURE_EXTERNAL_OES)); nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), toQt(squad->matrix.matrix()), currentLayerChain); @@ -1192,75 +957,87 @@ void DelegatedFrameNode::handleQuad( } } -ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +const CompositorResource *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker) { - // ResourceHolders must survive when the scene graph destroys our node branch - QSharedPointer<ResourceHolder> &resource = m_chromiumCompositorData->resourceHolders[resourceId]; - if (!resource) - resource = candidates.take(resourceId); - Q_ASSERT(resource); - return resource.data(); + return resourceTracker->findResource(resourceId); } -void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker) { for (auto resource : quad->resources) - findAndHoldResource(resource, candidates); + findAndHoldResource(resource, resourceTracker); } -void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker) { for (const auto &quad : pass->quad_list) - holdResources(quad, candidates); + holdResources(quad, resourceTracker); } -QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) +template<class Container, class Key> +inline auto &findTexture(Container &map, Container &previousMap, const Key &key) { - // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, - // so we can't store them with the ResourceHolder in m_chromiumCompositorData. - // Hold them through a QSharedPointer solely on the root DelegatedFrameNode of the web view - // and access them through a QWeakPointer from the resource holder to find them later. - m_sgObjects.textureStrongRefs.append(resource->initTexture(quadIsAllOpaque, apiDelegate)); - return m_sgObjects.textureStrongRefs.last().data(); + auto &value = map[key]; + if (value) + return value; + value = previousMap[key]; + return value; } -void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch) +QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target) { -#ifndef QT_NO_OPENGL - QList<gl::TransferableFence> transferredFences; - { - QMutexLocker lock(&m_mutex); - QVector<MailboxTexture *> mailboxesToPull; - mailboxesToPull.reserve(mailboxesToFetch.size()); - - gpu::SyncPointManager *syncPointManager = sync_point_manager(); - scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); - Q_ASSERT(m_numPendingSyncPoints == 0); - m_numPendingSyncPoints = mailboxesToFetch.count(); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { - gpu::SyncToken &syncToken = mailboxTexture->mailboxHolder().sync_token; - const auto task = base::Bind(&DelegatedFrameNode::pullTexture, this, mailboxTexture); - if (!syncPointManager->WaitOutOfOrder(syncToken, std::move(task))) - mailboxesToPull.append(mailboxTexture); - } - if (!mailboxesToPull.isEmpty()) { - auto task = base::BindOnce(&DelegatedFrameNode::pullTextures, this, std::move(mailboxesToPull)); - gpuTaskRunner->PostTask(FROM_HERE, std::move(task)); - } + QSGTexture::Filtering filtering = resource->filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest; - m_mailboxesFetchedWaitCond.wait(&m_mutex); - m_textureFences.swap(transferredFences); + if (resource->is_software) { + QSharedPointer<QSGTexture> &texture = + findTexture(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures, resource->id); + if (texture) + return texture.data(); + texture = createBitmapTexture(resource, hasAlphaChannel, apiDelegate); + texture->setFiltering(filtering); + return texture.data(); + } else { + QSharedPointer<MailboxTexture> &texture = + findTexture(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures, resource->id); + if (texture) + return texture.data(); + texture = createMailboxTexture(resource, hasAlphaChannel, target); + texture->setFiltering(filtering); + return texture.data(); } +} - for (gl::TransferableFence sync : qAsConst(transferredFences)) { - // We need to wait on the fences on the Qt current context, and - // can therefore not use GLFence routines that uses a different - // concept of current context. - waitChromiumSync(&sync); - deleteChromiumSync(&sync); - } +QSharedPointer<QSGTexture> DelegatedFrameNode::createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + Q_ASSERT(apiDelegate); + viz::SharedBitmap *sharedBitmap = resource->bitmap.get(); + gfx::Size size = resource->size; + + // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending + // to draw it but Chromium keeps this information in the quads. + // The input format is currently always Format_ARGB32_Premultiplied, so assume that all + // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion + // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to + // return false. + QImage::Format format = hasAlphaChannel ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + QImage image = sharedBitmap + ? QImage(sharedBitmap->pixels(), size.width(), size.height(), format) + : QImage(size.width(), size.height(), format); + return QSharedPointer<QSGTexture>(apiDelegate->createTextureFromImage(image.copy())); +} -#if defined(USE_OZONE) +QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) +{ +#ifndef QT_NO_OPENGL + return QSharedPointer<MailboxTexture>::create(resource, hasAlphaChannel, target); +#else + Q_UNREACHABLE(); +#endif +} + +void DelegatedFrameNode::copyMailboxTextures() +{ +#if !defined(QT_NO_OPENGL) && defined(USE_OZONE) // Workaround when context is not shared QTBUG-48969 // Make slow copy between two contexts. if (!m_contextShared) { @@ -1275,13 +1052,17 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe GLuint fbo = 0; funcs->glGenFramebuffers(1, &fbo); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { + for (const QSharedPointer<MailboxTexture> &mailboxTexture : qAsConst(m_sgObjects.mailboxTextures)) { + if (mailboxTexture->m_ownsTexture) + continue; + // Read texture into QImage from shared context. // Switch to shared context. sharedContext->makeCurrent(m_offsurface.data()); funcs = sharedContext->functions(); QImage img(mailboxTexture->textureSize(), QImage::Format_RGBA8888_Premultiplied); funcs->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + mailboxTexture->m_fence->wait(); funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mailboxTexture->m_textureId, 0); GLenum status = funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -1315,69 +1096,6 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe currentContext->makeCurrent(surface); } #endif -#else - Q_UNUSED(mailboxesToFetch) -#endif //QT_NO_OPENGL -} - - -void DelegatedFrameNode::pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> textures) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - for (MailboxTexture *texture : textures) { - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - } - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(textures) -#endif -} - -void DelegatedFrameNode::pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *texture) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(texture) -#endif -} - -void DelegatedFrameNode::fenceAndUnlockQt(DelegatedFrameNode *frameNode) -{ -#ifndef QT_NO_OPENGL - if (!!gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { - // Create a fence on the Chromium GPU-thread and context - std::unique_ptr<gl::GLFence> fence = gl::GLFence::Create(); - // But transfer it to something generic since we need to read it using Qt's OpenGL. - frameNode->m_textureFences.append(fence->Transfer()); - } - if (frameNode->m_numPendingSyncPoints == 0) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&DelegatedFrameNode::unlockQt, frameNode)); -#else - Q_UNUSED(frameNode) -#endif -} - -void DelegatedFrameNode::unlockQt(DelegatedFrameNode *frameNode) -{ - QMutexLocker lock(&frameNode->m_mutex); - // Signal preprocess() the textures are ready - frameNode->m_mailboxesFetchedWaitCond.wakeOne(); } } // namespace QtWebEngineCore diff --git a/src/core/delegated_frame_node.h b/src/core/delegated_frame_node.h index e37ad08a3..a39ae864b 100644 --- a/src/core/delegated_frame_node.h +++ b/src/core/delegated_frame_node.h @@ -43,14 +43,8 @@ #include "base/containers/circular_deque.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "gpu/command_buffer/service/sync_point_manager.h" -#include "ui/gl/gl_fence.h" -#include <QMutex> #include <QSGNode> -#include <QSharedData> #include <QSharedPointer> -#include <QWaitCondition> #include <QtGui/QOffscreenSurface> #include "chromium_gpu_helper.h" @@ -72,77 +66,60 @@ class DrawPolygon; namespace QtWebEngineCore { +class CompositorResource; +class CompositorResourceTracker; class DelegatedNodeTreeHandler; class MailboxTexture; -class ResourceHolder; - -// Separating this data allows another DelegatedFrameNode to reconstruct the QSGNode tree from the mailbox textures -// and render pass information. -class ChromiumCompositorData : public QSharedData { -public: - ChromiumCompositorData() : frameDevicePixelRatio(1) { } - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceHolders; - viz::CompositorFrame frameData; - viz::CompositorFrame previousFrameData; - qreal frameDevicePixelRatio; -}; class DelegatedFrameNode : public QSGTransformNode { public: DelegatedFrameNode(); ~DelegatedFrameNode(); - void preprocess(); - void commit(ChromiumCompositorData *chromiumCompositorData, std::vector<viz::ReturnedResource> *resourcesToRelease, RenderWidgetHostViewQtDelegate *apiDelegate); + void preprocess() override; + void commit(const viz::CompositorFrame &pendingFrame, const viz::CompositorFrame &committedFrame, const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); private: void flushPolygons(base::circular_deque<std::unique_ptr<viz::DrawPolygon> > *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleClippedQuad( const viz::DrawQuad *quad, const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); - void fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch); - // Making those callbacks static bypasses base::Bind's ref-counting requirement - // of the this pointer when the callback is a method. - static void pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *mailbox); - static void pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> mailboxes); - static void fenceAndUnlockQt(DelegatedFrameNode *frameNode); - static void unlockQt(DelegatedFrameNode *frameNode); - ResourceHolder *findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - QSGTexture *initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); + const CompositorResource *findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker); + QSGTexture *initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate = 0, int target = -1); + QSharedPointer<QSGTexture> createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate); + QSharedPointer<MailboxTexture> createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target); + + void copyMailboxTextures(); - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; struct SGObjects { QVector<QPair<int, QSharedPointer<QSGLayer> > > renderPassLayers; QVector<QSharedPointer<QSGRootNode> > renderPassRootNodes; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; - } m_sgObjects; + QHash<unsigned, QSharedPointer<QSGTexture> > bitmapTextures; + QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextures; + } m_sgObjects, m_previousSGObjects; QVector<QSGNode*> m_sceneGraphNodes; - int m_numPendingSyncPoints; - QWaitCondition m_mailboxesFetchedWaitCond; - QMutex m_mutex; - QList<gl::TransferableFence> m_textureFences; #if defined(USE_OZONE) bool m_contextShared; QScopedPointer<QOffscreenSurface> m_offsurface; diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index a847d2c99..0b33c359c 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -368,7 +368,6 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); - m_compositor->setViewDelegate(delegate); } void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient) @@ -747,7 +746,9 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo if (dpiScale != 0 && dpiScale != 1) frame.metadata.device_scale_factor /= dpiScale; - m_compositor->submitFrame(std::move(frame)); + m_compositor->submitFrame( + std::move(frame), + base::BindOnce(&RenderWidgetHostViewQtDelegate::update, base::Unretained(m_delegate.get()))); if (m_loadVisuallyCommittedState == NotCommitted) { m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; @@ -964,7 +965,7 @@ QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) if (host()->SynchronizeVisualProperties()) m_pendingResize = false; } - return m_compositor->updatePaintNode(oldNode); + return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } void RenderWidgetHostViewQt::notifyResize() |