summaryrefslogtreecommitdiffstats
path: root/src/core/compositor
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-10-30 13:35:24 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-11-07 17:55:18 +0000
commit7ebbd9e39106b3dfbac9de8f3305a7ca8712474d (patch)
tree677919a08f99a5521aeadb7538f79e3a7c158fd6 /src/core/compositor
parent79f5361400fa91b30cc4a1b34335b49284ba3e07 (diff)
Move compositor and scene-graph integration to its own dir
Change-Id: Ib552dc30db5ee886631a108b4ccd866459c432e2 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'src/core/compositor')
-rw-r--r--src/core/compositor/chromium_gpu_helper.cpp98
-rw-r--r--src/core/compositor/chromium_gpu_helper.h84
-rw-r--r--src/core/compositor/compositor.cpp186
-rw-r--r--src/core/compositor/compositor.h124
-rw-r--r--src/core/compositor/compositor_resource.h122
-rw-r--r--src/core/compositor/compositor_resource_fence.cpp154
-rw-r--r--src/core/compositor/compositor_resource_fence.h71
-rw-r--r--src/core/compositor/compositor_resource_tracker.cpp251
-rw-r--r--src/core/compositor/compositor_resource_tracker.h126
-rw-r--r--src/core/compositor/delegated_frame_node.cpp1101
-rw-r--r--src/core/compositor/delegated_frame_node.h132
-rw-r--r--src/core/compositor/stream_video_node.cpp169
-rw-r--r--src/core/compositor/stream_video_node.h88
-rw-r--r--src/core/compositor/yuv_video_node.cpp352
-rw-r--r--src/core/compositor/yuv_video_node.h117
15 files changed, 3175 insertions, 0 deletions
diff --git a/src/core/compositor/chromium_gpu_helper.cpp b/src/core/compositor/chromium_gpu_helper.cpp
new file mode 100644
index 000000000..92a8b13ed
--- /dev/null
+++ b/src/core/compositor/chromium_gpu_helper.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE.Chromium file.
+
+#include "chromium_gpu_helper.h"
+
+// Including gpu/command_buffer headers before content/gpu headers makes sure that
+// guards are defined to prevent duplicate definition errors with forward declared
+// GL typedefs cascading through content header includes.
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/texture_base.h"
+
+#include "content/gpu/gpu_child_thread.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
+
+#ifdef Q_OS_QNX
+#include "content/common/gpu/stream_texture_qnx.h"
+#endif
+
+scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner()
+{
+ return content::GpuChildThread::instance()->main_thread_runner();
+}
+
+gpu::SyncPointManager *sync_point_manager()
+{
+ gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager();
+ return gpuChannelManager->sync_point_manager();
+}
+
+gpu::MailboxManager *mailbox_manager()
+{
+ gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager();
+ return gpuChannelManager->mailbox_manager();
+}
+
+gpu::TextureBase* ConsumeTexture(gpu::MailboxManager *mailboxManager, unsigned target, const gpu::Mailbox& mailbox)
+{
+ Q_UNUSED(target);
+ return mailboxManager->ConsumeTexture(mailbox);
+}
+
+unsigned int service_id(gpu::TextureBase *tex)
+{
+ return tex->service_id();
+}
+
+#ifdef Q_OS_QNX
+EGLStreamData eglstream_connect_consumer(gpu::Texture *tex)
+{
+ EGLStreamData egl_stream;
+ content::StreamTexture* image = static_cast<content::StreamTexture *>(tex->GetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0));
+ if (image) {
+ image->ConnectConsumerIfNeeded(&egl_stream.egl_display, &egl_stream.egl_str_handle);
+ }
+ return egl_stream;
+}
+#endif
diff --git a/src/core/compositor/chromium_gpu_helper.h b/src/core/compositor/chromium_gpu_helper.h
new file mode 100644
index 000000000..21b764997
--- /dev/null
+++ b/src/core/compositor/chromium_gpu_helper.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 CHROMIUM_GPU_HELPER_H
+#define CHROMIUM_GPU_HELPER_H
+
+#include <QtGlobal> // We need this for the Q_OS_QNX define.
+
+#include "base/memory/scoped_refptr.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace gpu {
+struct Mailbox;
+class SyncPointManager;
+class MailboxManager;
+class TextureBase;
+}
+
+// These functions wrap code that needs to include headers that are
+// incompatible with Qt GL headers.
+// From the outside, types from incompatible headers referenced in these
+// functions should only be forward-declared and considered as opaque types.
+
+scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner();
+gpu::SyncPointManager *sync_point_manager();
+gpu::MailboxManager *mailbox_manager();
+
+gpu::TextureBase* ConsumeTexture(gpu::MailboxManager *mailboxManager, unsigned target, const gpu::Mailbox& mailbox);
+unsigned int service_id(gpu::TextureBase *tex);
+
+#ifdef Q_OS_QNX
+typedef void* EGLDisplay;
+typedef void* EGLStreamKHR;
+
+struct EGLStreamData {
+ EGLDisplay egl_display;
+ EGLStreamKHR egl_str_handle;
+
+ EGLStreamData(): egl_display(NULL), egl_str_handle(NULL) {}
+};
+
+EGLStreamData eglstream_connect_consumer(gpu::Texture *tex);
+#endif
+
+#endif // CHROMIUM_GPU_HELPER_H
diff --git a/src/core/compositor/compositor.cpp b/src/core/compositor/compositor.cpp
new file mode 100644
index 000000000..2d955b917
--- /dev/null
+++ b/src/core/compositor/compositor.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** 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.h"
+
+#include "compositor_resource_tracker.h"
+#include "delegated_frame_node.h"
+
+#include <components/viz/common/resources/returned_resource.h>
+#include <content/public/browser/browser_thread.h>
+#include <services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h>
+
+namespace QtWebEngineCore {
+
+Compositor::Compositor()
+ : m_resourceTracker(new CompositorResourceTracker)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::SingleThreadTaskRunner *taskRunner =
+ content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI).get();
+ m_beginFrameSource =
+ std::make_unique<viz::DelayBasedBeginFrameSource>(
+ std::make_unique<viz::DelayBasedTimeSource>(taskRunner),
+ viz::BeginFrameSource::kNotRestartableId);
+}
+
+Compositor::~Compositor()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (m_frameSinkClient == frameSinkClient)
+ return;
+
+ // Accumulated resources belong to the old RendererCompositorFrameSink and
+ // should not be returned.
+ //
+ // TODO(juvaldma): Can there be a pending frame from the old client?
+ m_resourceTracker->returnResources();
+ m_frameSinkClient = frameSinkClient;
+}
+
+void Compositor::setNeedsBeginFrames(bool needsBeginFrames)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (m_needsBeginFrames == needsBeginFrames)
+ return;
+
+ if (needsBeginFrames)
+ m_beginFrameSource->AddObserver(this);
+ else
+ m_beginFrameSource->RemoveObserver(this);
+
+ m_needsBeginFrames = needsBeginFrames;
+}
+
+void Compositor::submitFrame(viz::CompositorFrame frame, base::OnceClosure callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(!m_submitCallback);
+
+ 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, RenderWidgetHostViewQtDelegate *viewDelegate)
+{
+ // DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ //
+ // This might be called from a Qt Quick render thread, but the UI thread
+ // will still be blocked for the duration of this call.
+
+ DelegatedFrameNode *frameNode = static_cast<DelegatedFrameNode *>(oldNode);
+ if (!frameNode)
+ frameNode = new DelegatedFrameNode;
+
+ if (!m_updatePaintNodeShouldCommit) {
+ frameNode->commit(m_committedFrame, viz::CompositorFrame(), m_resourceTracker.get(), viewDelegate);
+ return frameNode;
+ }
+ 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_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_resourceTracker->returnResources());
+}
+
+void Compositor::sendPresentationFeedback(uint frame_token)
+{
+ gfx::PresentationFeedback dummyFeedback(base::TimeTicks::Now(), base::TimeDelta(), gfx::PresentationFeedback::Flags::kVSync);
+ m_frameSinkClient->DidPresentCompositorFrame(frame_token, dummyFeedback);
+}
+
+bool Compositor::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ m_beginFrameSource->OnUpdateVSyncParameters(args.frame_time, args.interval);
+ if (m_frameSinkClient)
+ m_frameSinkClient->OnBeginFrame(args);
+
+ return true;
+}
+
+void Compositor::OnBeginFrameSourcePausedChanged(bool)
+{
+ // Ignored for now. If the begin frame source is paused, the renderer
+ // doesn't need to be informed about it and will just not receive more
+ // begin frames.
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/compositor/compositor.h b/src/core/compositor/compositor.h
new file mode 100644
index 000000000..d34255d26
--- /dev/null
+++ b/src/core/compositor/compositor.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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_H
+#define COMPOSITOR_H
+
+#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>
+
+QT_BEGIN_NAMESPACE
+class QSGNode;
+QT_END_NAMESPACE
+
+namespace viz {
+struct ReturnedResource;
+namespace mojom {
+class CompositorFrameSinkClient;
+} // namespace mojom
+} // namespace viz
+
+namespace QtWebEngineCore {
+
+class CompositorResourceTracker;
+class RenderWidgetHostViewQtDelegate;
+
+// Receives viz::CompositorFrames from child compositors and provides QSGNodes
+// to the Qt Quick renderer.
+//
+// The life cycle of a frame:
+//
+// 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 notify
+// the client by running the callback given to submitFrame().
+//
+// 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.
+//
+// Step 4. The Compositor will return unneeded resources back to the child
+// compositors. Go to step 1.
+class Compositor final : private viz::BeginFrameObserverBase
+{
+public:
+ explicit Compositor();
+ ~Compositor() override;
+
+ void setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient);
+ void setNeedsBeginFrames(bool needsBeginFrames);
+
+ void submitFrame(viz::CompositorFrame frame, base::OnceClosure callback);
+ QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate);
+
+private:
+ void runSubmitCallback();
+ void notifyFrameCommitted();
+ void sendPresentationFeedback(uint frame_token);
+
+ // viz::BeginFrameObserverBase
+ bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ 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_updatePaintNodeShouldCommit = false;
+ bool m_needsBeginFrames = false;
+
+ base::WeakPtrFactory<Compositor> m_weakPtrFactory{this};
+
+ DISALLOW_COPY_AND_ASSIGN(Compositor);
+};
+
+} // namespace QtWebEngineCore
+
+#endif // !COMPOSITOR_H
diff --git a/src/core/compositor/compositor_resource.h b/src/core/compositor/compositor_resource.h
new file mode 100644
index 000000000..c40ce3d5e
--- /dev/null
+++ b/src/core/compositor/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/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp
new file mode 100644
index 000000000..b3f868d43
--- /dev/null
+++ b/src/core/compositor/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/compositor_resource_fence.h b/src/core/compositor/compositor_resource_fence.h
new file mode 100644
index 000000000..1c2ea3695
--- /dev/null
+++ b/src/core/compositor/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/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp
new file mode 100644
index 000000000..b74075c56
--- /dev/null
+++ b/src/core/compositor/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,
+ m_weakPtrFactory.GetWeakPtr()));
+}
+
+void CompositorResourceTracker::runSubmitCallback()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ std::move(m_submitCallback).Run();
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/compositor/compositor_resource_tracker.h b/src/core/compositor/compositor_resource_tracker.h
new file mode 100644
index 000000000..887309395
--- /dev/null
+++ b/src/core/compositor/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 "locked_ptr.h"
+
+#include <base/callback.h>
+#include <base/containers/flat_set.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::LockedPtrFactory<CompositorResourceTracker> m_weakPtrFactory{this};
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorResourceTracker);
+};
+
+} // namespace QtWebEngineCore
+
+#endif // !COMPOSITOR_RESOURCE_TRACKER_H
diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp
new file mode 100644
index 000000000..c4a6d8078
--- /dev/null
+++ b/src/core/compositor/delegated_frame_node.cpp
@@ -0,0 +1,1101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+// On Mac we need to reset this define in order to prevent definition
+// of "check" macros etc. The "check" macro collides with a member function name in QtQuick.
+// See AssertMacros.h in the Mac SDK.
+#include <QtGlobal> // We need this for the Q_OS_MAC define.
+#if defined(Q_OS_MAC)
+#undef __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES
+#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#endif
+
+#include "delegated_frame_node.h"
+
+#include "chromium_gpu_helper.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 "cc/base/math_util.h"
+#include "components/viz/common/quads/compositor_frame.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"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/quads/stream_video_draw_quad.h"
+#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/service/display/bsp_tree.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+
+#ifndef QT_NO_OPENGL
+# include <QOpenGLContext>
+# include <QOpenGLFunctions>
+# include <QSGFlatColorMaterial>
+#endif
+#include <QSGTexture>
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QSGImageNode>
+#include <QSGRectangleNode>
+
+#if !defined(QT_NO_EGL)
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
+
+#ifndef GL_TIMEOUT_IGNORED
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
+#endif
+
+#ifndef GL_TEXTURE_RECTANGLE
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#endif
+
+#ifndef GL_LINEAR
+#define GL_LINEAR 0x2601
+#endif
+
+#ifndef GL_RGBA
+#define GL_RGBA 0x1908
+#endif
+
+#ifndef GL_RGB
+#define GL_RGB 0x1907
+#endif
+
+#ifndef GL_LINE_LOOP
+#define GL_LINE_LOOP 0x0002
+#endif
+
+namespace QtWebEngineCore {
+#ifndef QT_NO_OPENGL
+class MailboxTexture : public QSGTexture, protected QOpenGLFunctions {
+public:
+ MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1);
+ ~MailboxTexture();
+ // QSGTexture:
+ int textureId() const override { return m_textureId; }
+ QSize textureSize() const override { return m_textureSize; }
+ bool hasAlphaChannel() const override { return m_hasAlpha; }
+ bool hasMipmaps() const override { return false; }
+ void bind() override;
+
+private:
+ int m_textureId;
+ scoped_refptr<CompositorResourceFence> m_fence;
+ QSize m_textureSize;
+ bool m_hasAlpha;
+ GLenum m_target;
+#if defined(USE_OZONE)
+ bool m_ownsTexture;
+#endif
+#ifdef Q_OS_QNX
+ EGLStreamData m_eglStreamData;
+#endif
+ friend class DelegatedFrameNode;
+};
+#endif // QT_NO_OPENGL
+
+class RectClipNode : public QSGClipNode
+{
+public:
+ RectClipNode(const QRectF &);
+private:
+ QSGGeometry m_geometry;
+};
+
+class DelegatedNodeTreeHandler
+{
+public:
+ DelegatedNodeTreeHandler(QVector<QSGNode*> *sceneGraphNodes)
+ : m_sceneGraphNodes(sceneGraphNodes)
+ {
+ }
+
+ virtual ~DelegatedNodeTreeHandler(){}
+
+ virtual void setupRenderPassNode(QSGTexture *, const QRect &, const QRectF &, QSGNode *) = 0;
+ virtual void setupTextureContentNode(QSGTexture *, const QRect &, const QRectF &,
+ QSGImageNode::TextureCoordinatesTransformMode,
+ QSGNode *) = 0;
+ virtual void setupSolidColorNode(const QRect &, const QColor &, QSGNode *) = 0;
+
+#ifndef QT_NO_OPENGL
+ virtual void setupDebugBorderNode(QSGGeometry *, QSGFlatColorMaterial *, QSGNode *) = 0;
+ virtual void setupYUVVideoNode(QSGTexture *, QSGTexture *, QSGTexture *, QSGTexture *,
+ const QRectF &, const QRectF &, const QSizeF &, const QSizeF &,
+ gfx::ColorSpace, float, float, const QRectF &,
+ QSGNode *) = 0;
+#ifdef GL_OES_EGL_image_external
+ virtual void setupStreamVideoNode(MailboxTexture *, const QRectF &,
+ const QMatrix4x4 &, QSGNode *) = 0;
+#endif // GL_OES_EGL_image_external
+#endif // QT_NO_OPENGL
+protected:
+ QVector<QSGNode*> *m_sceneGraphNodes;
+};
+
+class DelegatedNodeTreeUpdater : public DelegatedNodeTreeHandler
+{
+public:
+ DelegatedNodeTreeUpdater(QVector<QSGNode*> *sceneGraphNodes)
+ : DelegatedNodeTreeHandler(sceneGraphNodes)
+ , m_nodeIterator(sceneGraphNodes->begin())
+ {
+ }
+
+ void setupRenderPassNode(QSGTexture *layer, const QRect &rect, const QRectF &sourceRect, QSGNode *) override
+ {
+ Q_ASSERT(layer);
+ Q_ASSERT(m_nodeIterator != m_sceneGraphNodes->end());
+ QSGInternalImageNode *imageNode = static_cast<QSGInternalImageNode*>(*m_nodeIterator++);
+ imageNode->setTargetRect(rect);
+ imageNode->setInnerTargetRect(rect);
+ imageNode->setSubSourceRect(layer->convertToNormalizedSourceRect(sourceRect));
+ imageNode->setTexture(layer);
+ imageNode->update();
+ }
+
+ void setupTextureContentNode(QSGTexture *texture, const QRect &rect, const QRectF &sourceRect,
+ QSGImageNode::TextureCoordinatesTransformMode texCoordTransForm,
+ QSGNode *) override
+ {
+ Q_ASSERT(m_nodeIterator != m_sceneGraphNodes->end());
+ QSGImageNode *textureNode = static_cast<QSGImageNode*>(*m_nodeIterator++);
+ if (textureNode->texture() != texture) {
+ // Chromium sometimes uses textures that doesn't completely fit
+ // in which case the geometry needs to be recalculated even if
+ // rect and src-rect matches.
+ if (textureNode->texture()->textureSize() != texture->textureSize())
+ textureNode->markDirty(QSGImageNode::DirtyGeometry);
+ textureNode->setTexture(texture);
+ }
+ if (textureNode->textureCoordinatesTransform() != texCoordTransForm)
+ textureNode->setTextureCoordinatesTransform(texCoordTransForm);
+ if (textureNode->rect() != rect)
+ textureNode->setRect(rect);
+ if (textureNode->sourceRect() != sourceRect)
+ textureNode->setSourceRect(sourceRect);
+ if (textureNode->filtering() != texture->filtering())
+ textureNode->setFiltering(texture->filtering());
+ }
+ void setupSolidColorNode(const QRect &rect, const QColor &color, QSGNode *) override
+ {
+ Q_ASSERT(m_nodeIterator != m_sceneGraphNodes->end());
+ QSGRectangleNode *rectangleNode = static_cast<QSGRectangleNode*>(*m_nodeIterator++);
+
+ if (rectangleNode->rect() != rect)
+ rectangleNode->setRect(rect);
+ if (rectangleNode->color() != color)
+ rectangleNode->setColor(color);
+ }
+#ifndef QT_NO_OPENGL
+ void setupDebugBorderNode(QSGGeometry *geometry, QSGFlatColorMaterial *material,
+ QSGNode *) override
+ {
+ Q_ASSERT(m_nodeIterator != m_sceneGraphNodes->end());
+ QSGGeometryNode *geometryNode = static_cast<QSGGeometryNode*>(*m_nodeIterator++);
+
+ geometryNode->setGeometry(geometry);
+ geometryNode->setMaterial(material);
+ }
+
+ void setupYUVVideoNode(QSGTexture *, QSGTexture *, QSGTexture *, QSGTexture *,
+ const QRectF &, const QRectF &, const QSizeF &, const QSizeF &,
+ gfx::ColorSpace, float, float, const QRectF &,
+ QSGNode *) override
+ {
+ Q_UNREACHABLE();
+ }
+#ifdef GL_OES_EGL_image_external
+ void setupStreamVideoNode(MailboxTexture *, const QRectF &,
+ const QMatrix4x4 &, QSGNode *) override
+ {
+ Q_UNREACHABLE();
+ }
+#endif // GL_OES_EGL_image_external
+#endif // QT_NO_OPENGL
+
+private:
+ QVector<QSGNode*>::iterator m_nodeIterator;
+};
+
+class DelegatedNodeTreeCreator : public DelegatedNodeTreeHandler
+{
+public:
+ DelegatedNodeTreeCreator(QVector<QSGNode*> *sceneGraphNodes,
+ RenderWidgetHostViewQtDelegate *apiDelegate)
+ : DelegatedNodeTreeHandler(sceneGraphNodes)
+ , m_apiDelegate(apiDelegate)
+ {
+ }
+
+ void setupRenderPassNode(QSGTexture *layer, const QRect &rect, const QRectF &sourceRect,
+ QSGNode *layerChain) override
+ {
+ Q_ASSERT(layer);
+ // Only QSGInternalImageNode currently supports QSGLayer textures.
+ QSGInternalImageNode *imageNode = m_apiDelegate->createInternalImageNode();
+ imageNode->setTargetRect(rect);
+ imageNode->setInnerTargetRect(rect);
+ imageNode->setSubSourceRect(layer->convertToNormalizedSourceRect(sourceRect));
+ imageNode->setTexture(layer);
+ imageNode->update();
+
+ layerChain->appendChildNode(imageNode);
+ m_sceneGraphNodes->append(imageNode);
+ }
+
+ void setupTextureContentNode(QSGTexture *texture, const QRect &rect, const QRectF &sourceRect,
+ QSGImageNode::TextureCoordinatesTransformMode texCoordTransForm,
+ QSGNode *layerChain) override
+ {
+ QSGImageNode *textureNode = m_apiDelegate->createImageNode();
+ textureNode->setTextureCoordinatesTransform(texCoordTransForm);
+ textureNode->setRect(rect);
+ textureNode->setSourceRect(sourceRect);
+ textureNode->setTexture(texture);
+ textureNode->setFiltering(texture->filtering());
+
+ layerChain->appendChildNode(textureNode);
+ m_sceneGraphNodes->append(textureNode);
+ }
+
+ void setupSolidColorNode(const QRect &rect, const QColor &color,
+ QSGNode *layerChain) override
+ {
+ QSGRectangleNode *rectangleNode = m_apiDelegate->createRectangleNode();
+ rectangleNode->setRect(rect);
+ rectangleNode->setColor(color);
+
+ layerChain->appendChildNode(rectangleNode);
+ m_sceneGraphNodes->append(rectangleNode);
+ }
+
+#ifndef QT_NO_OPENGL
+ void setupDebugBorderNode(QSGGeometry *geometry, QSGFlatColorMaterial *material,
+ QSGNode *layerChain) override
+ {
+ QSGGeometryNode *geometryNode = new QSGGeometryNode;
+ geometryNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
+
+ geometryNode->setGeometry(geometry);
+ geometryNode->setMaterial(material);
+
+ layerChain->appendChildNode(geometryNode);
+ m_sceneGraphNodes->append(geometryNode);
+ }
+
+ void setupYUVVideoNode(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture,
+ QSGTexture *aTexture, const QRectF &yaTexCoordRect,
+ const QRectF &uvTexCoordRect, const QSizeF &yaTexSize,
+ const QSizeF &uvTexSize, gfx::ColorSpace colorspace,
+ float rMul, float rOff, const QRectF &rect,
+ QSGNode *layerChain) override
+ {
+ YUVVideoNode *videoNode = new YUVVideoNode(
+ yTexture,
+ uTexture,
+ vTexture,
+ aTexture,
+ yaTexCoordRect,
+ uvTexCoordRect,
+ yaTexSize,
+ uvTexSize,
+ colorspace,
+ rMul,
+ rOff);
+ videoNode->setRect(rect);
+
+ layerChain->appendChildNode(videoNode);
+ m_sceneGraphNodes->append(videoNode);
+ }
+#ifdef GL_OES_EGL_image_external
+ void setupStreamVideoNode(MailboxTexture *texture, const QRectF &rect,
+ const QMatrix4x4 &textureMatrix, QSGNode *layerChain) override
+ {
+ StreamVideoNode *svideoNode = new StreamVideoNode(texture, false, ExternalTarget);
+ svideoNode->setRect(rect);
+ svideoNode->setTextureMatrix(textureMatrix);
+ layerChain->appendChildNode(svideoNode);
+ m_sceneGraphNodes->append(svideoNode);
+ }
+#endif // GL_OES_EGL_image_external
+#endif // QT_NO_OPENGL
+
+private:
+ RenderWidgetHostViewQtDelegate *m_apiDelegate;
+};
+
+
+static inline QSharedPointer<QSGLayer> findRenderPassLayer(const int &id, const QVector<QPair<int, QSharedPointer<QSGLayer> > > &list)
+{
+ typedef QPair<int, QSharedPointer<QSGLayer> > Pair;
+ for (const Pair &pair : list)
+ if (pair.first == id)
+ return pair.second;
+ return QSharedPointer<QSGLayer>();
+}
+
+static QSGNode *buildRenderPassChain(QSGNode *chainParent)
+{
+ // Chromium already ordered the quads from back to front for us, however the
+ // Qt scene graph layers individual geometries in their own z-range and uses
+ // the depth buffer to visually stack nodes according to their item tree order.
+
+ // This gets rid of the z component of all quads, once any x and y perspective
+ // transformation has been applied to vertices not on the z=0 plane. Qt will
+ // use an orthographic projection to render them.
+ QSGTransformNode *zCompressNode = new QSGTransformNode;
+ QMatrix4x4 zCompressMatrix;
+ zCompressMatrix.scale(1, 1, 0);
+ zCompressNode->setMatrix(zCompressMatrix);
+ chainParent->appendChildNode(zCompressNode);
+ return zCompressNode;
+}
+
+static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState *layerState)
+{
+ QSGNode *layerChain = chainParent;
+ if (layerState->is_clipped) {
+ RectClipNode *clipNode = new RectClipNode(toQt(layerState->clip_rect));
+ layerChain->appendChildNode(clipNode);
+ layerChain = clipNode;
+ }
+ if (!layerState->quad_to_target_transform.IsIdentity()) {
+ QSGTransformNode *transformNode = new QSGTransformNode;
+ transformNode->setMatrix(toQt(layerState->quad_to_target_transform.matrix()));
+ layerChain->appendChildNode(transformNode);
+ layerChain = transformNode;
+ }
+ if (layerState->opacity < 1.0) {
+ QSGOpacityNode *opacityNode = new QSGOpacityNode;
+ opacityNode->setOpacity(layerState->opacity);
+ layerChain->appendChildNode(opacityNode);
+ layerChain = opacityNode;
+ }
+ return layerChain;
+}
+
+#ifndef QT_NO_OPENGL
+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
+{
+ initializeOpenGLFunctions();
+
+ // Assume that resources without a size will be used with a full source rect.
+ // Setting a size of 1x1 will let any texture node compute a normalized source
+ // rect of (0, 0) to (1, 1) while an empty texture size would set (0, 0) on all corners.
+ if (m_textureSize.isEmpty())
+ m_textureSize = QSize(1, 1);
+}
+
+MailboxTexture::~MailboxTexture()
+{
+#if defined(USE_OZONE)
+ // This is rare case, where context is not shared
+ // we created extra texture in current context, so
+ // delete it now
+ if (m_ownsTexture) {
+ QOpenGLContext *currentContext = QOpenGLContext::currentContext() ;
+ QOpenGLFunctions *funcs = currentContext->functions();
+ GLuint id(m_textureId);
+ funcs->glDeleteTextures(1, &id);
+ }
+#endif
+}
+
+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) {
+ static bool resolved = false;
+ static PFNEGLSTREAMCONSUMERACQUIREKHRPROC eglStreamConsumerAcquire = 0;
+
+ if (!resolved) {
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ eglStreamConsumerAcquire = (PFNEGLSTREAMCONSUMERACQUIREKHRPROC)context->getProcAddress("eglStreamConsumerAcquireKHR");
+ resolved = true;
+ }
+ if (eglStreamConsumerAcquire)
+ eglStreamConsumerAcquire(m_eglStreamData.egl_display, m_eglStreamData.egl_str_handle);
+ }
+#endif
+}
+#endif // !QT_NO_OPENGL
+
+RectClipNode::RectClipNode(const QRectF &rect)
+ : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
+{
+ QSGGeometry::updateRectGeometry(&m_geometry, rect);
+ setGeometry(&m_geometry);
+ setClipRect(rect);
+ setIsRectangular(true);
+}
+
+DelegatedFrameNode::DelegatedFrameNode()
+#if defined(USE_OZONE) && !defined(QT_NO_OPENGL)
+ : m_contextShared(true)
+#endif
+{
+ setFlag(UsePreprocess);
+#if defined(USE_OZONE) && !defined(QT_NO_OPENGL)
+ QOpenGLContext *currentContext = QOpenGLContext::currentContext() ;
+ QOpenGLContext *sharedContext = qt_gl_global_share_context();
+ if (currentContext && sharedContext && !QOpenGLContext::areSharing(currentContext, sharedContext)) {
+ static bool allowNotSharedContextWarningShown = true;
+ if (allowNotSharedContextWarningShown) {
+ allowNotSharedContextWarningShown = false;
+ qWarning("Context is not shared, textures will be copied between contexts.");
+ }
+ m_offsurface.reset(new QOffscreenSurface);
+ m_offsurface->create();
+ m_contextShared = false;
+ }
+#endif
+}
+
+DelegatedFrameNode::~DelegatedFrameNode()
+{
+}
+
+void DelegatedFrameNode::preprocess()
+{
+ // Then render any intermediate RenderPass in order.
+ typedef QPair<int, QSharedPointer<QSGLayer> > Pair;
+ for (const Pair &pair : qAsConst(m_sgObjects.renderPassLayers)) {
+ // The layer is non-live, request a one-time update here.
+ pair.second->scheduleUpdate();
+ // Proceed with the actual update.
+ pair.second->updateTexture();
+ }
+}
+
+static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState,
+ const viz::SharedQuadState *prevLayerState)
+{
+ if (layerState->sorting_context_id != 0 || prevLayerState->sorting_context_id != 0)
+ return false;
+ if (layerState->is_clipped != prevLayerState->is_clipped
+ || layerState->clip_rect != prevLayerState->clip_rect)
+ return false;
+ if (layerState->quad_to_target_transform != prevLayerState->quad_to_target_transform)
+ return false;
+ return qFuzzyCompare(layerState->opacity, prevLayerState->opacity);
+}
+
+// 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(const viz::CompositorFrame *frameData,
+ const viz::CompositorFrame *previousFrameData)
+{
+ if (!previousFrameData)
+ return false;
+
+ if (previousFrameData->render_pass_list.size() != frameData->render_pass_list.size())
+ return false;
+
+ for (unsigned i = 0; i < frameData->render_pass_list.size(); ++i) {
+ viz::RenderPass *newPass = frameData->render_pass_list.at(i).get();
+ viz::RenderPass *prevPass = previousFrameData->render_pass_list.at(i).get();
+
+ if (newPass->id != prevPass->id)
+ return false;
+
+ if (newPass->quad_list.size() != prevPass->quad_list.size())
+ return false;
+
+ viz::QuadList::ConstBackToFrontIterator it = newPass->quad_list.BackToFrontBegin();
+ viz::QuadList::ConstBackToFrontIterator end = newPass->quad_list.BackToFrontEnd();
+ viz::QuadList::ConstBackToFrontIterator prevIt = prevPass->quad_list.BackToFrontBegin();
+ viz::QuadList::ConstBackToFrontIterator prevEnd = prevPass->quad_list.BackToFrontEnd();
+ for (; it != end && prevIt != prevEnd; ++it, ++prevIt) {
+ const viz::DrawQuad *quad = *it;
+ const viz::DrawQuad *prevQuad = *prevIt;
+ if (quad->material != prevQuad->material)
+ return false;
+#ifndef QT_NO_OPENGL
+ if (quad->material == viz::DrawQuad::YUV_VIDEO_CONTENT)
+ return false;
+#ifdef GL_OES_EGL_image_external
+ if (quad->material == viz::DrawQuad::STREAM_VIDEO_CONTENT)
+ return false;
+#endif // GL_OES_EGL_image_external
+#endif // QT_NO_OPENGL
+ if (!areSharedQuadStatesEqual(quad->shared_quad_state, prevQuad->shared_quad_state))
+ return false;
+ if (quad->shared_quad_state->is_clipped && quad->visible_rect != prevQuad->visible_rect) {
+ gfx::Rect targetRect1 =
+ cc::MathUtil::MapEnclosingClippedRect(quad->shared_quad_state->quad_to_target_transform, quad->visible_rect);
+ gfx::Rect targetRect2 =
+ cc::MathUtil::MapEnclosingClippedRect(quad->shared_quad_state->quad_to_target_transform, prevQuad->visible_rect);
+ targetRect1.Intersect(quad->shared_quad_state->clip_rect);
+ targetRect2.Intersect(quad->shared_quad_state->clip_rect);
+ if (targetRect1.IsEmpty() != targetRect2.IsEmpty())
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame,
+ const viz::CompositorFrame &committedFrame,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate)
+{
+ const viz::CompositorFrame* frameData = &pendingFrame;
+ if (frameData->render_pass_list.empty())
+ return;
+
+ // DelegatedFrameNode is a transform node only for the purpose of
+ // countering the scale of devicePixel-scaled tiles when rendering them
+ // to the final surface.
+ QMatrix4x4 matrix;
+ const float devicePixelRatio = frameData->metadata.device_scale_factor;
+ matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio);
+ if (QSGTransformNode::matrix() != matrix)
+ setMatrix(matrix);
+
+ QScopedPointer<DelegatedNodeTreeHandler> nodeHandler;
+
+ const QSizeF viewportSizeInPt = apiDelegate->screenRect().size();
+ const QSizeF viewportSizeF = viewportSizeInPt * devicePixelRatio;
+ const QSize viewportSize(std::ceil(viewportSizeF.width()), std::ceil(viewportSizeF.height()));
+
+ // We first compare if the render passes from the previous frame data are structurally
+ // equivalent to the render passes in the current frame data. If they are, we are going
+ // to reuse the old nodes. Otherwise, we will delete the old nodes and build a new tree.
+ //
+ // 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, &committedFrame) ||
+ m_sceneGraphNodes.empty() ||
+ viewportSize != m_previousViewportSize;
+
+ 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, 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 {
+ 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
+ // to its dependencies through a RenderPassId reference in one or more RenderPassQuads.
+ // The list is already ordered with intermediate RenderPasses placed before their
+ // parent, with the last one in the list being the root RenderPass, the one
+ // that we displayed to the user.
+ // All RenderPasses except the last one are rendered to an FBO.
+ viz::RenderPass *rootRenderPass = frameData->render_pass_list.back().get();
+
+ gfx::Rect viewportRect(toGfx(viewportSize));
+ for (unsigned i = 0; i < frameData->render_pass_list.size(); ++i) {
+ viz::RenderPass *pass = frameData->render_pass_list.at(i).get();
+
+ QSGNode *renderPassParent = 0;
+ gfx::Rect scissorRect;
+ if (pass != rootRenderPass) {
+ QSharedPointer<QSGLayer> rpLayer;
+ if (buildNewTree) {
+ rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers);
+ if (!rpLayer) {
+ rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer());
+ // Avoid any premature texture update since we need to wait
+ // for the GPU thread to produce the dependent resources first.
+ rpLayer->setLive(false);
+ }
+ QSharedPointer<QSGRootNode> rootNode(new QSGRootNode);
+ rpLayer->setItem(rootNode.data());
+ m_sgObjects.renderPassLayers.append(QPair<int,
+ QSharedPointer<QSGLayer> >(pass->id, rpLayer));
+ m_sgObjects.renderPassRootNodes.append(rootNode);
+ renderPassParent = rootNode.data();
+ } else
+ rpLayer = findRenderPassLayer(pass->id, m_sgObjects.renderPassLayers);
+
+ rpLayer->setRect(toQt(pass->output_rect));
+ rpLayer->setSize(toQt(pass->output_rect.size()));
+ rpLayer->setFormat(pass->has_transparent_background ? GL_RGBA : GL_RGB);
+ rpLayer->setHasMipmaps(pass->generate_mipmap);
+ rpLayer->setMirrorVertical(true);
+ scissorRect = pass->output_rect;
+ } else {
+ renderPassParent = this;
+ scissorRect = viewportRect;
+ scissorRect += rootRenderPass->output_rect.OffsetFromOrigin();
+ }
+
+ if (scissorRect.IsEmpty()) {
+ holdResources(pass, resourceTracker);
+ continue;
+ }
+
+ QSGNode *renderPassChain = nullptr;
+ if (buildNewTree)
+ renderPassChain = buildRenderPassChain(renderPassParent);
+
+ base::circular_deque<std::unique_ptr<viz::DrawPolygon>> polygonQueue;
+ int nextPolygonId = 0;
+ int currentSortingContextId = 0;
+ const viz::SharedQuadState *currentLayerState = nullptr;
+ QSGNode *currentLayerChain = nullptr;
+ const auto quadListBegin = pass->quad_list.BackToFrontBegin();
+ const auto quadListEnd = pass->quad_list.BackToFrontEnd();
+ for (auto it = quadListBegin; it != quadListEnd; ++it) {
+ const viz::DrawQuad *quad = *it;
+ const viz::SharedQuadState *quadState = quad->shared_quad_state;
+
+ gfx::Rect targetRect =
+ cc::MathUtil::MapEnclosingClippedRect(quadState->quad_to_target_transform,
+ quad->visible_rect);
+ if (quadState->is_clipped)
+ targetRect.Intersect(quadState->clip_rect);
+ targetRect.Intersect(scissorRect);
+ if (targetRect.IsEmpty()) {
+ holdResources(quad, resourceTracker);
+ continue;
+ }
+
+ if (quadState->sorting_context_id != currentSortingContextId) {
+ flushPolygons(&polygonQueue, renderPassChain,
+ nodeHandler.data(), resourceTracker, apiDelegate);
+ currentSortingContextId = quadState->sorting_context_id;
+ }
+
+ if (currentSortingContextId != 0) {
+ std::unique_ptr<viz::DrawPolygon> polygon(
+ new viz::DrawPolygon(
+ quad,
+ gfx::RectF(quad->visible_rect),
+ quadState->quad_to_target_transform,
+ nextPolygonId++));
+ if (polygon->points().size() > 2u)
+ polygonQueue.push_back(std::move(polygon));
+ continue;
+ }
+
+ if (renderPassChain && currentLayerState != quadState) {
+ currentLayerState = quadState;
+ currentLayerChain = buildLayerChain(renderPassChain, quadState);
+ }
+
+ handleQuad(quad, currentLayerChain,
+ nodeHandler.data(), resourceTracker, apiDelegate);
+ }
+ flushPolygons(&polygonQueue, renderPassChain,
+ nodeHandler.data(), resourceTracker, apiDelegate);
+ }
+
+ copyMailboxTextures();
+
+ m_previousViewportSize = viewportSize;
+ m_previousSGObjects = SGObjects();
+}
+
+void DelegatedFrameNode::flushPolygons(
+ base::circular_deque<std::unique_ptr<viz::DrawPolygon>> *polygonQueue,
+ QSGNode *renderPassChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate)
+{
+ if (polygonQueue->empty())
+ return;
+
+ const auto actionHandler = [&](viz::DrawPolygon *polygon) {
+ const viz::DrawQuad *quad = polygon->original_ref();
+ const viz::SharedQuadState *quadState = quad->shared_quad_state;
+
+ QSGNode *currentLayerChain = nullptr;
+ if (renderPassChain)
+ currentLayerChain = buildLayerChain(renderPassChain, quad->shared_quad_state);
+
+ gfx::Transform inverseTransform;
+ bool invertible = quadState->quad_to_target_transform.GetInverse(&inverseTransform);
+ DCHECK(invertible);
+ polygon->TransformToLayerSpace(inverseTransform);
+
+ handlePolygon(polygon, currentLayerChain,
+ nodeHandler, resourceTracker, apiDelegate);
+ };
+
+ viz::BspTree(polygonQueue).TraverseWithActionHandler(&actionHandler);
+}
+
+void DelegatedFrameNode::handlePolygon(
+ const viz::DrawPolygon *polygon,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate)
+{
+ const viz::DrawQuad *quad = polygon->original_ref();
+
+ if (!polygon->is_split()) {
+ handleQuad(quad, currentLayerChain,
+ nodeHandler, resourceTracker, apiDelegate);
+ } else {
+ std::vector<gfx::QuadF> clipRegionList;
+ polygon->ToQuads2D(&clipRegionList);
+ for (const auto & clipRegion : clipRegionList)
+ handleClippedQuad(quad, clipRegion, currentLayerChain,
+ nodeHandler, resourceTracker, apiDelegate);
+ }
+}
+
+void DelegatedFrameNode::handleClippedQuad(
+ const viz::DrawQuad *quad,
+ const gfx::QuadF &clipRegion,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate)
+{
+ if (currentLayerChain) {
+ auto clipGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+ auto clipGeometryVertices = clipGeometry->vertexDataAsPoint2D();
+ clipGeometryVertices[0].set(clipRegion.p1().x(), clipRegion.p1().y());
+ clipGeometryVertices[1].set(clipRegion.p2().x(), clipRegion.p2().y());
+ clipGeometryVertices[2].set(clipRegion.p4().x(), clipRegion.p4().y());
+ clipGeometryVertices[3].set(clipRegion.p3().x(), clipRegion.p3().y());
+ auto clipNode = new QSGClipNode;
+ clipNode->setGeometry(clipGeometry);
+ clipNode->setIsRectangular(false);
+ clipNode->setFlag(QSGNode::OwnsGeometry);
+ currentLayerChain->appendChildNode(clipNode);
+ currentLayerChain = clipNode;
+ }
+ handleQuad(quad, currentLayerChain,
+ nodeHandler, resourceTracker, apiDelegate);
+}
+
+void DelegatedFrameNode::handleQuad(
+ const viz::DrawQuad *quad,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ 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()) {
+ const CompositorResource *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceTracker);
+ Q_UNUSED(resource); // FIXME: QTBUG-67652
+ }
+ QSGLayer *layer =
+ findRenderPassLayer(renderPassQuad->render_pass_id, m_sgObjects.renderPassLayers).data();
+
+ if (layer)
+ nodeHandler->setupRenderPassNode(layer, toQt(quad->rect), toQt(renderPassQuad->tex_coord_rect), currentLayerChain);
+
+ break;
+ }
+ case viz::DrawQuad::TEXTURE_CONTENT: {
+ const viz::TextureDrawQuad *tquad = viz::TextureDrawQuad::MaterialCast(quad);
+ const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker);
+ QSGTexture *texture =
+ initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate);
+ QSizeF textureSize;
+ if (texture)
+ textureSize = texture->textureSize();
+ gfx::RectF uv_rect =
+ gfx::ScaleRect(gfx::BoundingRect(tquad->uv_top_left, tquad->uv_bottom_right),
+ textureSize.width(), textureSize.height());
+
+ nodeHandler->setupTextureContentNode(
+ texture, toQt(quad->rect), toQt(uv_rect),
+ tquad->y_flipped ? QSGImageNode::MirrorVertically : QSGImageNode::NoTransform,
+ currentLayerChain);
+ break;
+ }
+ case viz::DrawQuad::SOLID_COLOR: {
+ const viz::SolidColorDrawQuad *scquad = viz::SolidColorDrawQuad::MaterialCast(quad);
+ // Qt only supports MSAA and this flag shouldn't be needed.
+ // If we ever want to use QSGRectangleNode::setAntialiasing for this we should
+ // try to see if we can do something similar for tile quads first.
+ Q_UNUSED(scquad->force_anti_aliasing_off);
+ nodeHandler->setupSolidColorNode(toQt(quad->rect), toQt(scquad->color), currentLayerChain);
+ break;
+#ifndef QT_NO_OPENGL
+ }
+ case viz::DrawQuad::DEBUG_BORDER: {
+ const viz::DebugBorderDrawQuad *dbquad = viz::DebugBorderDrawQuad::MaterialCast(quad);
+
+ QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+ geometry->setDrawingMode(GL_LINE_LOOP);
+ geometry->setLineWidth(dbquad->width);
+ // QSGGeometry::updateRectGeometry would actually set the
+ // corners in the following order:
+ // top-left, bottom-left, top-right, bottom-right, leading to a nice criss cross,
+ // instead of having a closed loop.
+ const gfx::Rect &r(dbquad->rect);
+ geometry->vertexDataAsPoint2D()[0].set(r.x(), r.y());
+ geometry->vertexDataAsPoint2D()[1].set(r.x() + r.width(), r.y());
+ geometry->vertexDataAsPoint2D()[2].set(r.x() + r.width(), r.y() + r.height());
+ geometry->vertexDataAsPoint2D()[3].set(r.x(), r.y() + r.height());
+
+ QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
+ material->setColor(toQt(dbquad->color));
+
+ nodeHandler->setupDebugBorderNode(geometry, material, currentLayerChain);
+ break;
+#endif
+ }
+ case viz::DrawQuad::TILED_CONTENT: {
+ const viz::TileDrawQuad *tquad = viz::TileDrawQuad::MaterialCast(quad);
+ const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker);
+ nodeHandler->setupTextureContentNode(
+ initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate),
+ toQt(quad->rect), toQt(tquad->tex_coord_rect),
+ QSGImageNode::NoTransform, currentLayerChain);
+ break;
+#ifndef QT_NO_OPENGL
+ }
+ case viz::DrawQuad::YUV_VIDEO_CONTENT: {
+ const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad);
+ 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(), resourceTracker);
+
+ nodeHandler->setupYUVVideoNode(
+ initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()),
+ initAndHoldTexture(uResource, quad->ShouldDrawWithBlending()),
+ initAndHoldTexture(vResource, quad->ShouldDrawWithBlending()),
+ aResource ? initAndHoldTexture(aResource, quad->ShouldDrawWithBlending()) : 0,
+ toQt(vquad->ya_tex_coord_rect), toQt(vquad->uv_tex_coord_rect),
+ toQt(vquad->ya_tex_size), toQt(vquad->uv_tex_size), vquad->video_color_space,
+ vquad->resource_multiplier, vquad->resource_offset, toQt(quad->rect),
+ currentLayerChain);
+ break;
+#ifdef GL_OES_EGL_image_external
+ }
+ case viz::DrawQuad::STREAM_VIDEO_CONTENT: {
+ const viz::StreamVideoDrawQuad *squad = viz::StreamVideoDrawQuad::MaterialCast(quad);
+ const CompositorResource *resource = findAndHoldResource(squad->resource_id(), resourceTracker);
+ MailboxTexture *texture = static_cast<MailboxTexture *>(
+ initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate, GL_TEXTURE_EXTERNAL_OES));
+
+ nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), toQt(squad->matrix.matrix()),
+ currentLayerChain);
+ break;
+#endif // GL_OES_EGL_image_external
+#endif // QT_NO_OPENGL
+ }
+ case viz::DrawQuad::SURFACE_CONTENT:
+ Q_UNREACHABLE();
+ default:
+ qWarning("Unimplemented quad material: %d", quad->material);
+ }
+}
+
+const CompositorResource *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker)
+{
+ return resourceTracker->findResource(resourceId);
+}
+
+void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker)
+{
+ for (auto resource : quad->resources)
+ findAndHoldResource(resource, resourceTracker);
+}
+
+void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker)
+{
+ for (const auto &quad : pass->quad_list)
+ holdResources(quad, resourceTracker);
+}
+
+template<class Container, class Key>
+inline auto &findTexture(Container &map, Container &previousMap, const Key &key)
+{
+ auto &value = map[key];
+ if (value)
+ return value;
+ value = previousMap[key];
+ return value;
+}
+
+QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target)
+{
+ QSGTexture::Filtering filtering = resource->filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest;
+
+ 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();
+ }
+}
+
+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()));
+}
+
+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) {
+ QOpenGLContext *currentContext = QOpenGLContext::currentContext() ;
+ QOpenGLContext *sharedContext = qt_gl_global_share_context();
+
+ QSurface *surface = currentContext->surface();
+ Q_ASSERT(m_offsurface);
+ sharedContext->makeCurrent(m_offsurface.data());
+ QOpenGLFunctions *funcs = sharedContext->functions();
+
+ GLuint fbo = 0;
+ funcs->glGenFramebuffers(1, &fbo);
+
+ 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) {
+ qWarning("fbo error, skipping slow copy...");
+ continue;
+ }
+ funcs->glReadPixels(0, 0, mailboxTexture->textureSize().width(), mailboxTexture->textureSize().height(),
+ GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+
+ // Restore current context.
+ // Create texture from QImage in current context.
+ currentContext->makeCurrent(surface);
+ GLuint texture = 0;
+ funcs = currentContext->functions();
+ funcs->glGenTextures(1, &texture);
+ funcs->glBindTexture(GL_TEXTURE_2D, texture);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mailboxTexture->textureSize().width(), mailboxTexture->textureSize().height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ mailboxTexture->m_textureId = texture;
+ mailboxTexture->m_ownsTexture = true;
+ }
+ // Cleanup allocated resources
+ sharedContext->makeCurrent(m_offsurface.data());
+ funcs = sharedContext->functions();
+ funcs->glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ funcs->glDeleteFramebuffers(1, &fbo);
+ currentContext->makeCurrent(surface);
+ }
+#endif
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/compositor/delegated_frame_node.h b/src/core/compositor/delegated_frame_node.h
new file mode 100644
index 000000000..a39ae864b
--- /dev/null
+++ b/src/core/compositor/delegated_frame_node.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 DELEGATED_FRAME_NODE_H
+#define DELEGATED_FRAME_NODE_H
+
+#include "base/containers/circular_deque.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/render_pass.h"
+#include <QSGNode>
+#include <QSharedPointer>
+#include <QtGui/QOffscreenSurface>
+
+#include "chromium_gpu_helper.h"
+#include "render_widget_host_view_qt_delegate.h"
+
+QT_BEGIN_NAMESPACE
+class QSGLayer;
+QT_END_NAMESPACE
+
+namespace gfx {
+class QuadF;
+}
+
+namespace viz {
+class DelegatedFrameData;
+class DrawQuad;
+class DrawPolygon;
+}
+
+namespace QtWebEngineCore {
+
+class CompositorResource;
+class CompositorResourceTracker;
+class DelegatedNodeTreeHandler;
+class MailboxTexture;
+
+class DelegatedFrameNode : public QSGTransformNode {
+public:
+ DelegatedFrameNode();
+ ~DelegatedFrameNode();
+ 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,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate);
+ void handlePolygon(
+ const viz::DrawPolygon *polygon,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate);
+ void handleClippedQuad(
+ const viz::DrawQuad *quad,
+ const gfx::QuadF &clipRegion,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate);
+ void handleQuad(
+ const viz::DrawQuad *quad,
+ QSGNode *currentLayerChain,
+ DelegatedNodeTreeHandler *nodeHandler,
+ const CompositorResourceTracker *resourceTracker,
+ RenderWidgetHostViewQtDelegate *apiDelegate);
+
+ 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();
+
+ struct SGObjects {
+ QVector<QPair<int, QSharedPointer<QSGLayer> > > renderPassLayers;
+ QVector<QSharedPointer<QSGRootNode> > renderPassRootNodes;
+ QHash<unsigned, QSharedPointer<QSGTexture> > bitmapTextures;
+ QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextures;
+ } m_sgObjects, m_previousSGObjects;
+ QVector<QSGNode*> m_sceneGraphNodes;
+#if defined(USE_OZONE)
+ bool m_contextShared;
+ QScopedPointer<QOffscreenSurface> m_offsurface;
+#endif
+ QSize m_previousViewportSize;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // DELEGATED_FRAME_NODE_H
diff --git a/src/core/compositor/stream_video_node.cpp b/src/core/compositor/stream_video_node.cpp
new file mode 100644
index 000000000..29922f866
--- /dev/null
+++ b/src/core/compositor/stream_video_node.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "stream_video_node.h"
+
+#include <QtQuick/qsgtexture.h>
+
+namespace QtWebEngineCore {
+
+class StreamVideoMaterialShader : public QSGMaterialShader
+{
+public:
+ StreamVideoMaterialShader(TextureTarget target) : m_target(target) { }
+ virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ char const *const *attributeNames() const override {
+ static const char *names[] = {
+ "a_position",
+ "a_texCoord",
+ 0
+ };
+ return names;
+ }
+
+protected:
+ const char *vertexShader() const override {
+ // Keep in sync with cc::VertexShaderVideoTransform
+ static const char *shader =
+ "attribute highp vec4 a_position;\n"
+ "attribute mediump vec2 a_texCoord;\n"
+ "uniform highp mat4 matrix;\n"
+ "uniform highp mat4 texMatrix;\n"
+ "varying mediump vec2 v_texCoord;\n"
+ "void main() {\n"
+ " gl_Position = matrix * a_position;\n"
+ " v_texCoord = vec4(texMatrix * vec4(a_texCoord.x, 1.0 - a_texCoord.y, 0.0, 1.0)).xy;\n"
+ "}";
+ return shader;
+ }
+
+ const char *fragmentShader() const override {
+ // Keep in sync with cc::FragmentShaderRGBATexAlpha
+ static const char *shaderExternal =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "varying mediump vec2 v_texCoord;\n"
+ "uniform samplerExternalOES s_texture;\n"
+ "uniform lowp float alpha;\n"
+ "void main() {\n"
+ " lowp vec4 texColor = texture2D(s_texture, v_texCoord);\n"
+ " gl_FragColor = texColor * alpha;\n"
+ "}";
+ static const char *shader2DRect =
+ "#extension GL_ARB_texture_rectangle : require\n"
+ "varying mediump vec2 v_texCoord;\n"
+ "uniform sampler2DRect s_texture;\n"
+ "uniform lowp float alpha;\n"
+ "void main() {\n"
+ " lowp vec4 texColor = texture2DRect(s_texture, v_texCoord);\n"
+ " gl_FragColor = texColor * alpha;\n"
+ "}";
+ if (m_target == ExternalTarget)
+ return shaderExternal;
+ else
+ return shader2DRect;
+ }
+
+ virtual void initialize() {
+ m_id_matrix = program()->uniformLocation("matrix");
+ m_id_sTexture = program()->uniformLocation("s_texture");
+ m_id_texMatrix = program()->uniformLocation("texMatrix");
+ m_id_opacity = program()->uniformLocation("alpha");
+ }
+
+ int m_id_matrix;
+ int m_id_texMatrix;
+ int m_id_sTexture;
+ int m_id_opacity;
+ TextureTarget m_target;
+};
+
+void StreamVideoMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+
+ StreamVideoMaterial *mat = static_cast<StreamVideoMaterial *>(newMaterial);
+ program()->setUniformValue(m_id_sTexture, 0);
+
+ mat->m_texture->bind();
+
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_id_opacity, state.opacity());
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+
+ program()->setUniformValue(m_id_texMatrix, mat->m_texMatrix);
+}
+
+StreamVideoMaterial::StreamVideoMaterial(QSGTexture *texture, TextureTarget target)
+ : m_texture(texture)
+ , m_target(target)
+{
+}
+
+QSGMaterialShader *StreamVideoMaterial::createShader() const
+{
+ return new StreamVideoMaterialShader(m_target);
+}
+
+StreamVideoNode::StreamVideoNode(QSGTexture *texture, bool flip, TextureTarget target)
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_flip(flip)
+{
+ setGeometry(&m_geometry);
+ setFlag(QSGNode::OwnsMaterial);
+ m_material = new StreamVideoMaterial(texture, target);
+ setMaterial(m_material);
+}
+
+void StreamVideoNode::setRect(const QRectF &rect)
+{
+ if (m_flip)
+ QSGGeometry::updateTexturedRectGeometry(geometry(), rect, QRectF(0, 1, 1, -1));
+ else
+ QSGGeometry::updateTexturedRectGeometry(geometry(), rect, QRectF(0, 0, 1, 1));
+}
+
+void StreamVideoNode::setTextureMatrix(const QMatrix4x4 &matrix)
+{
+ m_material->m_texMatrix = matrix;
+}
+
+} // namespace
diff --git a/src/core/compositor/stream_video_node.h b/src/core/compositor/stream_video_node.h
new file mode 100644
index 000000000..9d937791f
--- /dev/null
+++ b/src/core/compositor/stream_video_node.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 STREAM_VIDEO_NODE_H
+#define STREAM_VIDEO_NODE_H
+
+#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/qsgnode.h>
+
+QT_FORWARD_DECLARE_CLASS(QSGTexture)
+
+namespace QtWebEngineCore {
+
+// These classes duplicate, QtQuick style, the logic of GLRenderer::DrawStreamVideoQuad.
+// Their behavior should stay as close as possible to GLRenderer.
+
+enum TextureTarget { ExternalTarget, RectangleTarget };
+
+class StreamVideoMaterial : public QSGMaterial
+{
+public:
+ StreamVideoMaterial(QSGTexture *texture, TextureTarget target);
+
+ QSGMaterialType *type() const override
+ {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ QSGMaterialShader *createShader() const override;
+
+ QSGTexture *m_texture;
+ QMatrix4x4 m_texMatrix;
+ TextureTarget m_target;
+};
+
+class StreamVideoNode : public QSGGeometryNode
+{
+public:
+ StreamVideoNode(QSGTexture *texture, bool flip, TextureTarget target);
+ void setRect(const QRectF &rect);
+ void setTextureMatrix(const QMatrix4x4 &matrix);
+
+private:
+ QSGGeometry m_geometry;
+ bool m_flip;
+ StreamVideoMaterial *m_material;
+};
+
+} // namespace
+
+#endif // STREAM_VIDEO_NODE_H
diff --git a/src/core/compositor/yuv_video_node.cpp b/src/core/compositor/yuv_video_node.cpp
new file mode 100644
index 000000000..4a436d952
--- /dev/null
+++ b/src/core/compositor/yuv_video_node.cpp
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+// Based on cc/output/gl_renderer.cc and cc/output/shader.cc:
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE.Chromium file.
+
+#include "yuv_video_node.h"
+
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qopenglfunctions.h>
+#include <QtQuick/qsgtexture.h>
+
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/color_transform.h"
+
+namespace QtWebEngineCore {
+
+class YUVVideoMaterialShader : public QSGMaterialShader
+{
+public:
+ YUVVideoMaterialShader(const gfx::ColorSpace &colorSpace)
+ {
+ static const char *shaderHead =
+ "varying mediump vec2 v_yaTexCoord;\n"
+ "varying mediump vec2 v_uvTexCoord;\n"
+ "uniform sampler2D y_texture;\n"
+ "uniform sampler2D u_texture;\n"
+ "uniform sampler2D v_texture;\n"
+ "uniform mediump float alpha;\n"
+ "uniform mediump vec4 ya_clamp_rect;\n"
+ "uniform mediump vec4 uv_clamp_rect;\n";
+ static const char *shader =
+ "void main() {\n"
+ " mediump vec2 ya_clamped =\n"
+ " max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));\n"
+ " mediump float y_raw = texture2D(y_texture, ya_clamped).x;\n"
+ " mediump vec2 uv_clamped =\n"
+ " max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));\n"
+ " mediump float u_unsigned = texture2D(u_texture, uv_clamped).x;\n"
+ " mediump float v_unsigned = texture2D(v_texture, uv_clamped).x;\n"
+ " mediump vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned);\n"
+ " mediump vec3 rgb = DoColorConversion(yuv);\n"
+ " gl_FragColor = vec4(rgb, 1.0) * alpha;\n"
+ "}";
+ // Invalid or unspecified color spaces should be treated as REC709.
+ gfx::ColorSpace src = colorSpace.IsValid() ? colorSpace : gfx::ColorSpace::CreateREC709();
+ gfx::ColorSpace dst = gfx::ColorSpace::CreateSRGB();
+ std::unique_ptr<gfx::ColorTransform> transform =
+ gfx::ColorTransform::NewColorTransform(src, dst, gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
+
+ QByteArray header(shaderHead);
+ if (QOpenGLContext::currentContext()->isOpenGLES())
+ header = QByteArray("precision mediump float;\n") + header;
+
+ m_csShader = QByteArray::fromStdString(transform->GetShaderSource());
+ m_fragmentShader = header + m_csShader + QByteArray(shader);
+ }
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+ char const *const *attributeNames() const override {
+ static const char *names[] = {
+ "a_position",
+ "a_texCoord",
+ 0
+ };
+ return names;
+ }
+
+protected:
+ const char *vertexShader() const override {
+ // Keep in sync with logic in VertexShader in components/viz/service/display/shader.cc
+ const char *shader =
+ "attribute highp vec4 a_position;\n"
+ "attribute mediump vec2 a_texCoord;\n"
+ "uniform highp mat4 matrix;\n"
+ "varying mediump vec2 v_yaTexCoord;\n"
+ "varying mediump vec2 v_uvTexCoord;\n"
+ "uniform mediump vec2 yaTexScale;\n"
+ "uniform mediump vec2 yaTexOffset;\n"
+ "uniform mediump vec2 uvTexScale;\n"
+ "uniform mediump vec2 uvTexOffset;\n"
+ "void main() {\n"
+ " gl_Position = matrix * a_position;\n"
+ " v_yaTexCoord = a_texCoord * yaTexScale + yaTexOffset;\n"
+ " v_uvTexCoord = a_texCoord * uvTexScale + uvTexOffset;\n"
+ "}";
+ return shader;
+ }
+
+ const char *fragmentShader() const override {
+ return m_fragmentShader.constData();
+ }
+
+ void initialize() override {
+ m_id_matrix = program()->uniformLocation("matrix");
+ m_id_yaTexScale = program()->uniformLocation("yaTexScale");
+ m_id_uvTexScale = program()->uniformLocation("uvTexScale");
+ m_id_yaTexOffset = program()->uniformLocation("yaTexOffset");
+ m_id_uvTexOffset = program()->uniformLocation("uvTexOffset");
+ m_id_yaClampRect = program()->uniformLocation("ya_clamp_rect");
+ m_id_uvClampRect = program()->uniformLocation("uv_clamp_rect");
+ m_id_yTexture = program()->uniformLocation("y_texture");
+ m_id_uTexture = program()->uniformLocation("u_texture");
+ m_id_vTexture = program()->uniformLocation("v_texture");
+ m_id_yuvMatrix = program()->uniformLocation("yuv_matrix");
+ m_id_yuvAdjust = program()->uniformLocation("yuv_adj");
+ m_id_opacity = program()->uniformLocation("alpha");
+ }
+
+ int m_id_matrix;
+ int m_id_yaTexScale;
+ int m_id_uvTexScale;
+ int m_id_yaTexOffset;
+ int m_id_uvTexOffset;
+ int m_id_yaClampRect;
+ int m_id_uvClampRect;
+ int m_id_yTexture;
+ int m_id_uTexture;
+ int m_id_vTexture;
+ int m_id_yuvMatrix;
+ int m_id_yuvAdjust;
+ int m_id_opacity;
+ QByteArray m_csShader;
+ QByteArray m_fragmentShader;
+};
+
+class YUVAVideoMaterialShader : public YUVVideoMaterialShader
+{
+public:
+ YUVAVideoMaterialShader(const gfx::ColorSpace &colorSpace) : YUVVideoMaterialShader(colorSpace)
+ {
+ static const char *shaderHead =
+ "varying mediump vec2 v_yaTexCoord;\n"
+ "varying mediump vec2 v_uvTexCoord;\n"
+ "uniform sampler2D y_texture;\n"
+ "uniform sampler2D u_texture;\n"
+ "uniform sampler2D v_texture;\n"
+ "uniform sampler2D a_texture;\n"
+ "uniform mediump float alpha;\n"
+ "uniform mediump vec4 ya_clamp_rect;\n"
+ "uniform mediump vec4 uv_clamp_rect;\n";
+ static const char *shader =
+ "void main() {\n"
+ " mediump vec2 ya_clamped =\n"
+ " max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));\n"
+ " mediump float y_raw = texture2D(y_texture, ya_clamped).x;\n"
+ " mediump vec2 uv_clamped =\n"
+ " max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));\n"
+ " mediump float u_unsigned = texture2D(u_texture, uv_clamped).x;\n"
+ " mediump float v_unsigned = texture2D(v_texture, uv_clamped).x;\n"
+ " mediump float a_raw = texture2D(a_texture, ya_clamped).x;\n"
+ " mediump vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned);\n"
+ " mediump vec3 rgb = DoColorConversion(yuv);\n"
+ " gl_FragColor = vec4(rgb, 1.0) * (alpha * a_raw);\n"
+ "}";
+ QByteArray header(shaderHead);
+ if (QOpenGLContext::currentContext()->isOpenGLES())
+ header = QByteArray("precision mediump float;\n") + header;
+ m_fragmentShader = header + m_csShader + QByteArray(shader);
+ }
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+protected:
+ void initialize() override {
+ // YUVVideoMaterialShader has a subset of the uniforms.
+ YUVVideoMaterialShader::initialize();
+ m_id_aTexture = program()->uniformLocation("a_texture");
+ }
+
+ int m_id_aTexture;
+};
+
+void YUVVideoMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+
+ // Keep logic in sync with logic in GLRenderer::DrawYUVVideoQuad:
+
+ YUVVideoMaterial *mat = static_cast<YUVVideoMaterial *>(newMaterial);
+ program()->setUniformValue(m_id_yTexture, 0);
+ program()->setUniformValue(m_id_uTexture, 1);
+ program()->setUniformValue(m_id_vTexture, 2);
+
+ QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
+
+ glFuncs.glActiveTexture(GL_TEXTURE1);
+ mat->m_uTexture->bind();
+ glFuncs.glActiveTexture(GL_TEXTURE2);
+ mat->m_vTexture->bind();
+ glFuncs.glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+ mat->m_yTexture->bind();
+
+ const QSizeF yaSizeScale(1.0f / mat->m_yaTexSize.width(), 1.0f / mat->m_yaTexSize.height());
+ const QSizeF uvSizeScale(1.0f / mat->m_uvTexSize.width(), 1.0f / mat->m_uvTexSize.height());
+
+ const QPointF yaTexOffset(mat->m_yaTexCoordRect.left() * yaSizeScale.width(), mat->m_yaTexCoordRect.top() * yaSizeScale.height());
+ const QPointF uvTexOffset(mat->m_uvTexCoordRect.left() * uvSizeScale.width(), mat->m_uvTexCoordRect.top() * uvSizeScale.height());
+ const QSizeF yaTexScale(mat->m_yaTexCoordRect.width() * yaSizeScale.width(), mat->m_yaTexCoordRect.height() * yaSizeScale.height());
+ const QSizeF uvTexScale(mat->m_uvTexCoordRect.width() * uvSizeScale.width(), mat->m_uvTexCoordRect.height() * uvSizeScale.height());
+ program()->setUniformValue(m_id_yaTexOffset, yaTexOffset);
+ program()->setUniformValue(m_id_uvTexOffset, uvTexOffset);
+ program()->setUniformValue(m_id_yaTexScale, yaTexScale);
+ program()->setUniformValue(m_id_uvTexScale, uvTexScale);
+ QRectF yaClampRect(yaTexOffset, yaTexScale);
+ QRectF uvClampRect(uvTexOffset, uvTexScale);
+ yaClampRect = yaClampRect.marginsRemoved(QMarginsF(yaSizeScale.width() * 0.5f, yaSizeScale.height() * 0.5f,
+ yaSizeScale.width() * 0.5f, yaSizeScale.height() * 0.5f));
+ uvClampRect = uvClampRect.marginsRemoved(QMarginsF(uvSizeScale.width() * 0.5f, uvSizeScale.height() * 0.5f,
+ uvSizeScale.width() * 0.5f, uvSizeScale.height() * 0.5f));
+
+ const QVector4D yaClampV(yaClampRect.left(), yaClampRect.top(), yaClampRect.right(), yaClampRect.bottom());
+ const QVector4D uvClampV(uvClampRect.left(), uvClampRect.top(), uvClampRect.right(), uvClampRect.bottom());
+ program()->setUniformValue(m_id_yaClampRect, yaClampV);
+ program()->setUniformValue(m_id_uvClampRect, uvClampV);
+
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_id_opacity, state.opacity());
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+}
+
+void YUVAVideoMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ YUVVideoMaterialShader::updateState(state, newMaterial, oldMaterial);
+
+ YUVAVideoMaterial *mat = static_cast<YUVAVideoMaterial *>(newMaterial);
+ program()->setUniformValue(m_id_aTexture, 3);
+
+ QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
+
+ glFuncs.glActiveTexture(GL_TEXTURE3);
+ mat->m_aTexture->bind();
+
+ // Reset the default texture unit.
+ glFuncs.glActiveTexture(GL_TEXTURE0);
+}
+
+
+YUVVideoMaterial::YUVVideoMaterial(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace,
+ float rMul, float rOff)
+ : m_yTexture(yTexture)
+ , m_uTexture(uTexture)
+ , m_vTexture(vTexture)
+ , m_yaTexCoordRect(yaTexCoordRect)
+ , m_uvTexCoordRect(uvTexCoordRect)
+ , m_yaTexSize(yaTexSize)
+ , m_uvTexSize(uvTexSize)
+ , m_colorSpace(colorspace)
+ , m_resourceMultiplier(rMul)
+ , m_resourceOffset(rOff)
+{
+}
+
+QSGMaterialShader *YUVVideoMaterial::createShader() const
+{
+ return new YUVVideoMaterialShader(m_colorSpace);
+}
+
+int YUVVideoMaterial::compare(const QSGMaterial *other) const
+{
+ const YUVVideoMaterial *m = static_cast<const YUVVideoMaterial *>(other);
+ if (int diff = m_yTexture->textureId() - m->m_yTexture->textureId())
+ return diff;
+ if (int diff = m_uTexture->textureId() - m->m_uTexture->textureId())
+ return diff;
+ return m_vTexture->textureId() - m->m_vTexture->textureId();
+}
+
+YUVAVideoMaterial::YUVAVideoMaterial(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture, QSGTexture *aTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace,
+ float rMul, float rOff)
+ : YUVVideoMaterial(yTexture, uTexture, vTexture, yaTexCoordRect, uvTexCoordRect, yaTexSize, uvTexSize, colorspace, rMul, rOff)
+ , m_aTexture(aTexture)
+{
+ setFlag(Blending, aTexture);
+}
+
+QSGMaterialShader *YUVAVideoMaterial::createShader() const
+{
+ return new YUVAVideoMaterialShader(m_colorSpace);
+}
+
+int YUVAVideoMaterial::compare(const QSGMaterial *other) const
+{
+ if (int diff = YUVVideoMaterial::compare(other))
+ return diff;
+ const YUVAVideoMaterial *m = static_cast<const YUVAVideoMaterial *>(other);
+ return (m_aTexture ? m_aTexture->textureId() : 0) - (m->m_aTexture ? m->m_aTexture->textureId() : 0);
+}
+
+YUVVideoNode::YUVVideoNode(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture, QSGTexture *aTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace, float rMul, float rOff)
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+{
+ setGeometry(&m_geometry);
+ setFlag(QSGNode::OwnsMaterial);
+ if (aTexture)
+ m_material = new YUVAVideoMaterial(yTexture, uTexture, vTexture, aTexture, yaTexCoordRect, uvTexCoordRect, yaTexSize, uvTexSize, colorspace, rMul, rOff);
+ else
+ m_material = new YUVVideoMaterial(yTexture, uTexture, vTexture, yaTexCoordRect, uvTexCoordRect, yaTexSize, uvTexSize, colorspace, rMul, rOff);
+ setMaterial(m_material);
+}
+
+void YUVVideoNode::setRect(const QRectF &rect)
+{
+ QSGGeometry::updateTexturedRectGeometry(geometry(), rect, QRectF(0, 0, 1, 1));
+}
+
+} // namespace
diff --git a/src/core/compositor/yuv_video_node.h b/src/core/compositor/yuv_video_node.h
new file mode 100644
index 000000000..dca8fa5e2
--- /dev/null
+++ b/src/core/compositor/yuv_video_node.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 YUV_VIDEO_NODE_H
+#define YUV_VIDEO_NODE_H
+
+#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/qsgnode.h>
+
+#include "ui/gfx/color_space.h"
+
+QT_FORWARD_DECLARE_CLASS(QSGTexture)
+
+namespace QtWebEngineCore {
+
+// These classes duplicate, QtQuick style, the logic of GLRenderer::DrawYUVVideoQuad.
+// Their behavior should stay as close as possible to GLRenderer.
+
+class YUVVideoMaterial : public QSGMaterial
+{
+public:
+ YUVVideoMaterial(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace, float rMul, float rOff);
+
+ QSGMaterialType *type() const override
+ {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ QSGMaterialShader *createShader() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ QSGTexture *m_yTexture;
+ QSGTexture *m_uTexture;
+ QSGTexture *m_vTexture;
+ QRectF m_yaTexCoordRect;
+ QRectF m_uvTexCoordRect;
+ QSizeF m_yaTexSize;
+ QSizeF m_uvTexSize;
+ gfx::ColorSpace m_colorSpace;
+ float m_resourceMultiplier;
+ float m_resourceOffset;
+};
+
+class YUVAVideoMaterial : public YUVVideoMaterial
+{
+public:
+ YUVAVideoMaterial(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture, QSGTexture *aTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace, float rMul, float rOff);
+
+ QSGMaterialType *type() const override
+ {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ QSGMaterialShader *createShader() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ QSGTexture *m_aTexture;
+};
+
+class YUVVideoNode : public QSGGeometryNode
+{
+public:
+ YUVVideoNode(QSGTexture *yTexture, QSGTexture *uTexture, QSGTexture *vTexture, QSGTexture *aTexture,
+ const QRectF &yaTexCoordRect, const QRectF &uvTexCoordRect, const QSizeF &yaTexSize, const QSizeF &uvTexSize,
+ const gfx::ColorSpace &colorspace, float rMul, float rOff);
+ void setRect(const QRectF &rect);
+
+private:
+ QSGGeometry m_geometry;
+ YUVVideoMaterial *m_material;
+};
+
+} // namespace
+
+#endif // YUV_VIDEO_NODE_H