From 7ebbd9e39106b3dfbac9de8f3305a7ca8712474d Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 30 Oct 2018 13:35:24 +0100 Subject: Move compositor and scene-graph integration to its own dir Change-Id: Ib552dc30db5ee886631a108b4ccd866459c432e2 Reviewed-by: Michal Klocek --- src/core/compositor/chromium_gpu_helper.cpp | 98 ++ src/core/compositor/chromium_gpu_helper.h | 84 ++ src/core/compositor/compositor.cpp | 186 ++++ src/core/compositor/compositor.h | 124 +++ src/core/compositor/compositor_resource.h | 122 +++ src/core/compositor/compositor_resource_fence.cpp | 154 +++ src/core/compositor/compositor_resource_fence.h | 71 ++ .../compositor/compositor_resource_tracker.cpp | 251 +++++ src/core/compositor/compositor_resource_tracker.h | 126 +++ src/core/compositor/delegated_frame_node.cpp | 1101 ++++++++++++++++++++ src/core/compositor/delegated_frame_node.h | 132 +++ src/core/compositor/stream_video_node.cpp | 169 +++ src/core/compositor/stream_video_node.h | 88 ++ src/core/compositor/yuv_video_node.cpp | 352 +++++++ src/core/compositor/yuv_video_node.h | 117 +++ 15 files changed, 3175 insertions(+) create mode 100644 src/core/compositor/chromium_gpu_helper.cpp create mode 100644 src/core/compositor/chromium_gpu_helper.h create mode 100644 src/core/compositor/compositor.cpp create mode 100644 src/core/compositor/compositor.h create mode 100644 src/core/compositor/compositor_resource.h create mode 100644 src/core/compositor/compositor_resource_fence.cpp create mode 100644 src/core/compositor/compositor_resource_fence.h create mode 100644 src/core/compositor/compositor_resource_tracker.cpp create mode 100644 src/core/compositor/compositor_resource_tracker.h create mode 100644 src/core/compositor/delegated_frame_node.cpp create mode 100644 src/core/compositor/delegated_frame_node.h create mode 100644 src/core/compositor/stream_video_node.cpp create mode 100644 src/core/compositor/stream_video_node.h create mode 100644 src/core/compositor/yuv_video_node.cpp create mode 100644 src/core/compositor/yuv_video_node.h (limited to 'src/core/compositor') 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 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(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 // 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 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 +#include +#include + +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( + std::make_unique(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(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 +#include +#include + +#include +#include + +#include +#include + +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 m_resourceTracker; + std::unique_ptr m_beginFrameSource; + viz::mojom::CompositorFrameSinkClient *m_frameSinkClient = nullptr; + bool m_updatePaintNodeShouldCommit = false; + bool m_needsBeginFrames = false; + + base::WeakPtrFactory 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 +#include + +#include +#include + +#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 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 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 + +#include + +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::create() +{ + if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { + std::unique_ptr glFence{gl::GLFence::Create()}; + return base::MakeRefCounted(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 +#include + +namespace QtWebEngineCore { + +// Sync object created on GPU thread and consumed on render thread. +class CompositorResourceFence final : public base::RefCountedThreadSafe +{ +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 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 +#include +#include +#include +#include +#include +#include +#include + +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 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 CompositorResourceTracker::returnResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::vector 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(*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 resources) +{ +#if QT_CONFIG(opengl) + scoped_refptr 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 resources) +{ +#if QT_CONFIG(opengl) + for (CompositorResource *resource : resources) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + + scoped_refptr 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 +#include + +#include +#include + +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 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 resources); + void updateMailboxes(std::vector resources); + + void scheduleRunSubmitCallback(); + void runSubmitCallback(); + + base::flat_set m_committedResources; + std::vector m_pendingResources; + std::vector m_pendingImports; + base::OnceClosure m_submitCallback; + std::atomic m_pendingResourceUpdates{0}; + quint32 m_committedFrameId = 0; + + base::LockedPtrFactory 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 // 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 +# include +# include +#endif +#include +#include + +#include +#include + +#if !defined(QT_NO_EGL) +#include +#include +#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 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 *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 *m_sceneGraphNodes; +}; + +class DelegatedNodeTreeUpdater : public DelegatedNodeTreeHandler +{ +public: + DelegatedNodeTreeUpdater(QVector *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(*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(*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(*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(*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::iterator m_nodeIterator; +}; + +class DelegatedNodeTreeCreator : public DelegatedNodeTreeHandler +{ +public: + DelegatedNodeTreeCreator(QVector *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 findRenderPassLayer(const int &id, const QVector > > &list) +{ + typedef QPair > Pair; + for (const Pair &pair : list) + if (pair.first == id) + return pair.second; + return QSharedPointer(); +} + +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 > 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 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 rpLayer; + if (buildNewTree) { + rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers); + if (!rpLayer) { + rpLayer = QSharedPointer(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 rootNode(new QSGRootNode); + rpLayer->setItem(rootNode.data()); + m_sgObjects.renderPassLayers.append(QPair >(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> 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 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> *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 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( + 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 +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 &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 &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 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(apiDelegate->createTextureFromImage(image.copy())); +} + +QSharedPointer DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) +{ +#ifndef QT_NO_OPENGL + return QSharedPointer::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 : 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 +#include +#include + +#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 > *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 createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate); + QSharedPointer createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target); + + void copyMailboxTextures(); + + struct SGObjects { + QVector > > renderPassLayers; + QVector > renderPassRootNodes; + QHash > bitmapTextures; + QHash > mailboxTextures; + } m_sgObjects, m_previousSGObjects; + QVector m_sceneGraphNodes; +#if defined(USE_OZONE) + bool m_contextShared; + QScopedPointer 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 + +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(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 +#include + +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 +#include +#include + +#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 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(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(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(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(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 +#include + +#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 -- cgit v1.2.3