diff options
Diffstat (limited to 'src/core/compositor')
26 files changed, 1832 insertions, 1077 deletions
diff --git a/src/core/compositor/compositor.cpp b/src/core/compositor/compositor.cpp index 655126f20..4c0bd4c0d 100644 --- a/src/core/compositor/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "compositor.h" @@ -43,8 +7,8 @@ #include "components/viz/common/surfaces/frame_sink_id.h" #include <QHash> -#include <QImage> #include <QMutex> +#include <QQuickWindow> namespace QtWebEngineCore { @@ -129,10 +93,8 @@ void Compositor::Observer::unbind() Compositor::Handle<Compositor> Compositor::Observer::compositor() { - if (!m_binding) - return nullptr; g_bindings.lock(); - if (m_binding->compositor) + if (m_binding && m_binding->compositor) return m_binding->compositor; // delay unlock g_bindings.unlock(); return nullptr; @@ -163,32 +125,35 @@ void Compositor::unbind() Compositor::Handle<Compositor::Observer> Compositor::observer() { - if (!m_binding) - return nullptr; g_bindings.lock(); - if (m_binding->observer) + if (m_binding && m_binding->observer) return m_binding->observer; // delay unlock g_bindings.unlock(); return nullptr; } -QImage Compositor::image() +void Compositor::waitForTexture() { - Q_UNREACHABLE(); - return {}; } -void Compositor::waitForTexture() +void Compositor::releaseTexture() +{ +} + +QSGTexture *Compositor::texture(QQuickWindow *, uint32_t textureOptions) { Q_UNREACHABLE(); + return nullptr; } -int Compositor::textureId() +bool Compositor::textureIsFlipped() { Q_UNREACHABLE(); - return 0; + return false; } +void Compositor::releaseResources() { } + // static void Compositor::unlockBindings() { diff --git a/src/core/compositor/compositor.h b/src/core/compositor/compositor.h index 7968e8201..501559060 100644 --- a/src/core/compositor/compositor.h +++ b/src/core/compositor/compositor.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef COMPOSITOR_H #define COMPOSITOR_H @@ -43,8 +7,9 @@ #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> QT_BEGIN_NAMESPACE -class QImage; +class QQuickWindow; class QSize; +class QSGTexture; QT_END_NAMESPACE namespace viz { @@ -55,10 +20,9 @@ namespace QtWebEngineCore { // Produces composited frames for display. // -// Used by quick/widgets libraries for accessing the frame and -// controlling frame swapping. Must be cast to a subclass to access -// the frame as QImage or OpenGL texture, etc. -class Q_WEBENGINECORE_PRIVATE_EXPORT Compositor +// Used by quick/widgets libraries for accessing the frames and +// controlling frame swapping. +class Q_WEBENGINECORE_EXPORT Compositor { struct Binding; @@ -66,7 +30,8 @@ public: // Identifies the implementation type. enum class Type { Software, - OpenGL, + OpenGL, // TODO: Legacy, remove it with DisplaySkiaOutputDevice! + Native }; // Identifies a compositor. @@ -110,7 +75,7 @@ public: // Observes the compositor corresponding to the given id. // // Only one observer can exist per compositor. - class Q_WEBENGINECORE_PRIVATE_EXPORT Observer + class Q_WEBENGINECORE_EXPORT Observer { public: // Binding to compositor @@ -125,7 +90,7 @@ public: protected: Observer() = default; - ~Observer() = default; + ~Observer() { if (m_binding) unbind(); } private: Binding *m_binding = nullptr; @@ -153,31 +118,26 @@ public: virtual QSize size() = 0; // Whether frame needs an alpha channel. - // - // In software mode, the image format can be either - // QImage::Format_ARGB32_Premultiplied or - // QImage::Format_RGBA8888_Premultiplied - // - // In OpenGL mode, the texture format is either GL_RGBA or GL_RGB. - virtual bool hasAlphaChannel() = 0; - - // (Software) QImage of the frame. - // - // This is a big image so we should try not to make copies of it. - // In particular, the client should drop its QImage reference - // before calling swapFrame(), otherwise each swap will cause a - // detach. - virtual QImage image(); + virtual bool requiresAlphaChannel() = 0; - // (OpenGL) Wait on texture fence in Qt's current OpenGL context. + // Wait on texture to be ready aka. sync. virtual void waitForTexture(); - // (OpenGL) Texture of the frame. - virtual int textureId(); + // Release any held texture resources + virtual void releaseTexture(); + + // QSGTexture of the frame. + virtual QSGTexture *texture(QQuickWindow *win, uint32_t textureOptions); + + // Is the texture produced upside down? + virtual bool textureIsFlipped(); + + // Release resources created in texture() + virtual void releaseResources(); protected: Compositor(Type type) : m_type(type) { } - virtual ~Compositor() = default; + virtual ~Compositor() { if (m_binding) unbind(); } private: template<typename T> diff --git a/src/core/compositor/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp index d03e260d6..bd1c95cd9 100644 --- a/src/core/compositor/compositor_resource_fence.cpp +++ b/src/core/compositor/compositor_resource_fence.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "compositor_resource_fence.h" #include "ozone/gl_surface_qt.h" diff --git a/src/core/compositor/compositor_resource_fence.h b/src/core/compositor/compositor_resource_fence.h index 574416b8b..ab92ca5c7 100644 --- a/src/core/compositor/compositor_resource_fence.h +++ b/src/core/compositor/compositor_resource_fence.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef COMPOSITOR_RESOURCE_FENCE_H #define COMPOSITOR_RESOURCE_FENCE_H diff --git a/src/core/compositor/content_gpu_client_qt.cpp b/src/core/compositor/content_gpu_client_qt.cpp index f934979a0..6ef8048f2 100644 --- a/src/core/compositor/content_gpu_client_qt.cpp +++ b/src/core/compositor/content_gpu_client_qt.cpp @@ -1,45 +1,8 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "content_gpu_client_qt.h" - -#include "web_engine_context.h" +#include "ozone/gl_share_context_qt.h" namespace QtWebEngineCore { @@ -51,9 +14,11 @@ ContentGpuClientQt::~ContentGpuClientQt() { } -gpu::SyncPointManager *ContentGpuClientQt::GetSyncPointManager() +gl::GLShareGroup *ContentGpuClientQt::GetInProcessGpuShareGroup() { - return WebEngineContext::syncPointManager(); + if (!m_shareGroupQt.get()) + m_shareGroupQt = new ShareGroupQt; + return m_shareGroupQt.get(); } } // namespace diff --git a/src/core/compositor/content_gpu_client_qt.h b/src/core/compositor/content_gpu_client_qt.h index d7ad43881..33314e0bb 100644 --- a/src/core/compositor/content_gpu_client_qt.h +++ b/src/core/compositor/content_gpu_client_qt.h @@ -1,47 +1,16 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef CONTENT_GPU_CLIENT_QT_H #define CONTENT_GPU_CLIENT_QT_H #include "content/public/gpu/content_gpu_client.h" +namespace gl { +class GLShareGroup; +} + namespace QtWebEngineCore { +class ShareGroupQt; class ContentGpuClientQt : public content::ContentGpuClient { public: @@ -49,7 +18,10 @@ public: ~ContentGpuClientQt() override; // content::ContentGpuClient implementation. - gpu::SyncPointManager *GetSyncPointManager() override; + gl::GLShareGroup *GetInProcessGpuShareGroup() override; + +private: + scoped_refptr<ShareGroupQt> m_shareGroupQt; }; } diff --git a/src/core/compositor/display_gl_output_surface.cpp b/src/core/compositor/display_gl_output_surface.cpp deleted file mode 100644 index dfc3cb7c6..000000000 --- a/src/core/compositor/display_gl_output_surface.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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 "display_gl_output_surface.h" - -#include "type_conversion.h" - -#include "base/threading/thread_task_runner_handle.h" -#include "components/viz/service/display/display.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "gpu/command_buffer/client/gles2_implementation.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "gpu/command_buffer/service/texture_base.h" -#include "gpu/ipc/in_process_command_buffer.h" -#include "ui/gfx/buffer_format_util.h" - -namespace QtWebEngineCore { - -DisplayGLOutputSurface::DisplayGLOutputSurface( - scoped_refptr<viz::VizProcessContextProvider> contextProvider) - : OutputSurface(contextProvider) - , Compositor(Compositor::Type::OpenGL) - , m_commandBuffer(contextProvider->command_buffer()) - , m_gl(contextProvider->ContextGL()) - , m_vizContextProvider(contextProvider) -{ - capabilities_.uses_default_gl_framebuffer = false; - m_gl->GenFramebuffers(1, &m_fboId); -} - -DisplayGLOutputSurface::~DisplayGLOutputSurface() -{ - unbind(); - m_vizContextProvider->SetUpdateVSyncParametersCallback(viz::UpdateVSyncParametersCallback()); - m_gl->DeleteFramebuffers(1, &m_fboId); -} - -// Called from viz::Display::Initialize. -void DisplayGLOutputSurface::BindToClient(viz::OutputSurfaceClient *client) -{ - m_client = client; -} - -void DisplayGLOutputSurface::SetFrameSinkId(const viz::FrameSinkId &id) -{ - bind(id); -} - -// Triggered by ui::Compositor::SetVisible(true). -void DisplayGLOutputSurface::EnsureBackbuffer() -{ -} - -// Triggered by ui::Compositor::SetVisible(false). Framebuffer must be cleared. -void DisplayGLOutputSurface::DiscardBackbuffer() -{ - NOTIMPLEMENTED(); - // m_gl->DiscardBackbufferCHROMIUM(); -} - -// Called from viz::DirectRenderer::DrawFrame before rendering starts, but only -// if the parameters differ from the previous Reshape call. -// -// Parameters: -// -// - sizeInPixels comes from ui::Compositor::SetScaleAndSize via -// viz::HostContextFactoryPrivate::ResizeDisplay. -// -// - devicePixelRatio comes from viz::CompositorFrame::device_scale_factor() -// via viz::RootCompositorFrameSinkImpl::SubmitCompositorFrame and -// viz::Display::SetLocalSurfaceId. -// -// - colorSpace and hasAlpha correspond to the color_space and -// has_transparent_background properties of the root viz::RenderPass. -// -// - useStencil should create a stencil buffer, but this is only needed for -// overdraw feedback (--show-overdraw-feedback), so it's safe to ignore. -// Accordingly, capabilities_.supports_stencil should be set to false. -// -void DisplayGLOutputSurface::Reshape(const gfx::Size &sizeInPixels, - float devicePixelRatio, - const gfx::ColorSpace &colorSpace, - gfx::BufferFormat format, - bool /*useStencil*/) -{ - bool hasAlpha = gfx::AlphaBitsForBufferFormat(format) > 0; - m_currentShape = Shape{sizeInPixels, devicePixelRatio, colorSpace, hasAlpha}; - m_gl->ResizeCHROMIUM(sizeInPixels.width(), sizeInPixels.height(), devicePixelRatio, - colorSpace.AsGLColorSpace(), hasAlpha); -} - -std::unique_ptr<DisplayGLOutputSurface::Buffer> DisplayGLOutputSurface::makeBuffer(const Shape &shape) -{ - std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(this); - buffer->shape = shape; - m_gl->GenTextures(1, &buffer->clientId); - m_gl->BindTexture(GL_TEXTURE_2D, buffer->clientId); - m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - uint32_t width = shape.sizeInPixels.width(); - uint32_t height = shape.sizeInPixels.height(); - uint32_t format = shape.hasAlpha ? GL_RGBA : GL_RGB; - m_gl->TexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr); - return buffer; -} - -void DisplayGLOutputSurface::deleteBufferResources(Buffer *buffer) -{ - m_gl->DeleteTextures(1, &buffer->clientId); -} - -// Called by viz::GLRenderer during rendering whenever it switches to the root -// render pass. -void DisplayGLOutputSurface::BindFramebuffer() -{ - if (!m_backBuffer || m_backBuffer->shape != m_currentShape) - m_backBuffer = makeBuffer(m_currentShape); - - m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fboId); - m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_backBuffer->clientId, 0); -} - -// Called from viz::Display::DrawAndSwap after rendering. -// -// Parameters: -// -// - frame.size is the same as the size given to Reshape. -// -// - frame.sub_buffer_rect and frame.content_bounds are never used since these -// are only enabled if gl::GLSurface::SupportsPostSubBuffer() or -// gl::GLSurface::SupportsSwapBuffersWithBounds() are true, respectively, -// but this not the case for any offscreen gl::GLSurface. -// -// - frame.latency_info is viz::CompositorFrame::metadata.latency_info. -void DisplayGLOutputSurface::SwapBuffers(viz::OutputSurfaceFrame frame) -{ - DCHECK(frame.size == m_currentShape.sizeInPixels); - DCHECK(!frame.sub_buffer_rect.has_value()); - DCHECK(frame.content_bounds.empty()); - DCHECK(m_backBuffer); - - m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fboId); - m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - gpu::SyncToken syncToken; - m_gl->GenSyncTokenCHROMIUM(syncToken.GetData()); - - unsigned int clientId = m_backBuffer->clientId; - - // Now some thread-hopping: - // - // - We start here on the viz thread (client side of command buffer). - // - // - Then we'll jump to the gpu thread (service side of command buffer) to - // get the real OpenGL texture id. - // - // - Then we'll get a call from the Qt Quick Scene Graph thread (could be - // a separate thread or the main thread). - // - // - Finally we'll return to the viz thread to acknowledge the swap. - - { - QMutexLocker locker(&m_mutex); - m_taskRunner = base::ThreadTaskRunnerHandle::Get(); - m_middleBuffer = std::move(m_backBuffer); - m_middleBuffer->serviceId = 0; - } - - m_commandBuffer->GetTextureQt( - clientId, - base::BindOnce(&DisplayGLOutputSurface::swapBuffersOnGpuThread, base::Unretained(this)), - std::vector<gpu::SyncToken>{syncToken}); -} - -void DisplayGLOutputSurface::swapBuffersOnGpuThread(unsigned int id, std::unique_ptr<gl::GLFence> fence) -{ - { - QMutexLocker locker(&m_mutex); - m_middleBuffer->serviceId = id; - m_middleBuffer->fence = CompositorResourceFence::create(std::move(fence)); - m_readyToUpdate = true; - } - - if (auto obs = observer()) - obs->readyToSwap(); -} - -void DisplayGLOutputSurface::swapBuffersOnVizThread() -{ - { - QMutexLocker locker(&m_mutex); - m_backBuffer = std::move(m_middleBuffer); - } - - const auto now = base::TimeTicks::Now(); - m_client->DidReceiveSwapBuffersAck(gfx::SwapTimings{now, now}); - m_client->DidReceivePresentationFeedback( - gfx::PresentationFeedback(now, base::TimeDelta(), - gfx::PresentationFeedback::Flags::kVSync)); -} - -void DisplayGLOutputSurface::SetDrawRectangle(const gfx::Rect &) -{ -} - -// Returning true here will cause viz::GLRenderer to try to render the output -// surface as an overlay plane (see viz::DirectRenderer::DrawFrame and -// viz::GLRenderer::ScheduleOverlays). -bool DisplayGLOutputSurface::IsDisplayedAsOverlayPlane() const -{ - return false; -} - -// Only used if IsDisplayedAsOverlayPlane was true (called from -// viz::GLRenderer::ScheduleOverlays). -unsigned DisplayGLOutputSurface::GetOverlayTextureId() const -{ - return 0; -} - -// Called by viz::GLRenderer but always false in all implementations except for -// android_webview::ParentOutputSurface. -bool DisplayGLOutputSurface::HasExternalStencilTest() const -{ - return false; -} - -// Only called if HasExternalStencilTest was true. Dead code? -void DisplayGLOutputSurface::ApplyExternalStencil() -{ - NOTREACHED(); -} - -// Called from GLRenderer::GetFramebufferCopyTextureFormat when using -// glCopyTexSubImage2D on our framebuffer. -uint32_t DisplayGLOutputSurface::GetFramebufferCopyTextureFormat() -{ - return m_currentShape.hasAlpha ? GL_RGBA : GL_RGB; -} - -// Called from viz::DirectRenderer::DrawFrame, only used for overlays. -unsigned DisplayGLOutputSurface::UpdateGpuFence() -{ - NOTREACHED(); - return 0; -} - -void DisplayGLOutputSurface::SetUpdateVSyncParametersCallback(viz::UpdateVSyncParametersCallback callback) -{ - m_vizContextProvider->SetUpdateVSyncParametersCallback(std::move(callback)); -} - -void DisplayGLOutputSurface::SetDisplayTransformHint(gfx::OverlayTransform) -{ -} - -gfx::OverlayTransform DisplayGLOutputSurface::GetDisplayTransform() -{ - return gfx::OVERLAY_TRANSFORM_NONE; -} - -void DisplayGLOutputSurface::swapFrame() -{ - QMutexLocker locker(&m_mutex); - if (m_readyToUpdate) { - std::swap(m_middleBuffer, m_frontBuffer); - m_taskRunner->PostTask(FROM_HERE, - base::BindOnce(&DisplayGLOutputSurface::swapBuffersOnVizThread, - base::Unretained(this))); - m_taskRunner.reset(); - m_readyToUpdate = false; - } -} - -void DisplayGLOutputSurface::waitForTexture() -{ - if (m_frontBuffer && m_frontBuffer->fence) { - m_frontBuffer->fence->wait(); - m_frontBuffer->fence.reset(); - } -} - -int DisplayGLOutputSurface::textureId() -{ - return m_frontBuffer ? m_frontBuffer->serviceId : 0; -} - -QSize DisplayGLOutputSurface::size() -{ - return m_frontBuffer ? toQt(m_frontBuffer->shape.sizeInPixels) : QSize(); -} - -bool DisplayGLOutputSurface::hasAlphaChannel() -{ - return m_frontBuffer ? m_frontBuffer->shape.hasAlpha : false; -} - -float DisplayGLOutputSurface::devicePixelRatio() -{ - return m_frontBuffer ? m_frontBuffer->shape.devicePixelRatio : 1; -} - -} // namespace QtWebEngineCore diff --git a/src/core/compositor/display_gl_output_surface.h b/src/core/compositor/display_gl_output_surface.h deleted file mode 100644 index 30fb0cd0d..000000000 --- a/src/core/compositor/display_gl_output_surface.h +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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 DISPLAY_GL_OUTPUT_SURFACE_H -#define DISPLAY_GL_OUTPUT_SURFACE_H - -#include "compositor_resource_fence.h" -#include "compositor.h" - -#include "components/viz/common/display/update_vsync_parameters_callback.h" -#include "components/viz/service/display/output_surface.h" -#include "components/viz/service/display_embedder/viz_process_context_provider.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/sync_token.h" - -#include <QMutex> - -namespace QtWebEngineCore { - -class DisplayGLOutputSurface final : public viz::OutputSurface, public Compositor -{ -public: - DisplayGLOutputSurface(scoped_refptr<viz::VizProcessContextProvider> contextProvider); - ~DisplayGLOutputSurface() override; - - // Overridden from viz::OutputSurface. - void BindToClient(viz::OutputSurfaceClient *client) override; - void EnsureBackbuffer() override; - void DiscardBackbuffer() override; - void BindFramebuffer() override; - void SetDrawRectangle(const gfx::Rect &drawRect) override; - bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - void Reshape(const gfx::Size &size, - float devicePixelRatio, - const gfx::ColorSpace &colorSpace, - gfx::BufferFormat format, - bool useStencil) override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - uint32_t GetFramebufferCopyTextureFormat() override; - void SwapBuffers(viz::OutputSurfaceFrame frame) override; - unsigned UpdateGpuFence() override; - void SetUpdateVSyncParametersCallback(viz::UpdateVSyncParametersCallback callback) override; - void SetDisplayTransformHint(gfx::OverlayTransform transform) override; - gfx::OverlayTransform GetDisplayTransform() override; - void SetFrameSinkId(const viz::FrameSinkId &id) override; - - // Overridden from Compositor. - void swapFrame() override; - void waitForTexture() override; - int textureId() override; - QSize size() override; - bool hasAlphaChannel() override; - float devicePixelRatio() override; - -private: - struct Shape - { - gfx::Size sizeInPixels; - float devicePixelRatio; - gfx::ColorSpace colorSpace; - bool hasAlpha; - - bool operator==(const Shape &that) const - { - return (sizeInPixels == that.sizeInPixels && - devicePixelRatio == that.devicePixelRatio && - colorSpace == that.colorSpace && - hasAlpha == that.hasAlpha); - } - bool operator!=(const Shape &that) const { return !(*this == that); } - }; - - struct Buffer - { - DisplayGLOutputSurface *parent; - Shape shape; - uint32_t clientId = 0; - uint32_t serviceId = 0; - scoped_refptr<CompositorResourceFence> fence; - - Buffer(DisplayGLOutputSurface *parent) : parent(parent) {} - ~Buffer() { parent->deleteBufferResources(this); } - }; - - class Texture; - - void swapBuffersOnGpuThread(unsigned int id, std::unique_ptr<gl::GLFence> fence); - void swapBuffersOnVizThread(); - - std::unique_ptr<Buffer> makeBuffer(const Shape &shape); - void deleteBufferResources(Buffer *buffer); - void attachBuffer(); - void detachBuffer(); - - gpu::InProcessCommandBuffer *const m_commandBuffer; - gpu::gles2::GLES2Interface *const m_gl; - mutable QMutex m_mutex; - uint32_t m_fboId = 0; - viz::OutputSurfaceClient *m_client = nullptr; - Shape m_currentShape; - std::unique_ptr<Buffer> m_backBuffer; - std::unique_ptr<Buffer> m_middleBuffer; - std::unique_ptr<Buffer> m_frontBuffer; - bool m_readyToUpdate = false; - scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; - scoped_refptr<viz::VizProcessContextProvider> m_vizContextProvider; -}; - -} // namespace QtWebEngineCore - -#endif // !DISPLAY_GL_OUTPUT_SURFACE_H diff --git a/src/core/compositor/display_overrides.cpp b/src/core/compositor/display_overrides.cpp index d41765272..ebaa96dbb 100644 --- a/src/core/compositor/display_overrides.cpp +++ b/src/core/compositor/display_overrides.cpp @@ -1,100 +1,100 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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 "display_gl_output_surface.h" +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "display_skia_output_device.h" #include "display_software_output_surface.h" +#include "native_skia_output_device.h" #include "components/viz/service/display_embedder/output_surface_provider_impl.h" #include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h" #include "gpu/ipc/in_process_command_buffer.h" + #include <qtgui-config.h> +#include <QtQuick/qquickwindow.h> -std::unique_ptr<viz::OutputSurface> -viz::OutputSurfaceProviderImpl::CreateGLOutputSurface( - scoped_refptr<VizProcessContextProvider> context_provider) -{ #if QT_CONFIG(opengl) - return std::make_unique<QtWebEngineCore::DisplayGLOutputSurface>(std::move(context_provider)); -#else - return nullptr; -#endif // QT_CONFIG(opengl) -} +#include "native_skia_output_device_opengl.h" +#endif + +#if BUILDFLAG(ENABLE_VULKAN) +#include "native_skia_output_device_vulkan.h" +#endif + +#if defined(Q_OS_WIN) +#include "native_skia_output_device_direct3d11.h" +#endif + +#if defined(Q_OS_MACOS) +#include "native_skia_output_device_metal.h" +#endif std::unique_ptr<viz::OutputSurface> -viz::OutputSurfaceProviderImpl::CreateSoftwareOutputSurface() +viz::OutputSurfaceProviderImpl::CreateSoftwareOutputSurface(const RendererSettings &renderer_settings) { - return std::make_unique<QtWebEngineCore::DisplaySoftwareOutputSurface>(); + return std::make_unique<QtWebEngineCore::DisplaySoftwareOutputSurface>(renderer_settings.requires_alpha_channel); } std::unique_ptr<viz::SkiaOutputDevice> viz::SkiaOutputSurfaceImplOnGpu::CreateOutputDevice() { + static const auto graphicsApi = QQuickWindow::graphicsApi(); + #if QT_CONFIG(opengl) - return std::make_unique<QtWebEngineCore::DisplaySkiaOutputDevice>( - context_state_, - shared_gpu_deps_->memory_tracker(), - GetDidSwapBuffersCompleteCallback()); + if (graphicsApi == QSGRendererInterface::OpenGL) { + if (gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE) { +#if !defined(Q_OS_MACOS) + return std::make_unique<QtWebEngineCore::DisplaySkiaOutputDevice>( + context_state_, renderer_settings_.requires_alpha_channel, + shared_gpu_deps_->memory_tracker(), GetDidSwapBuffersCompleteCallback()); #else - return nullptr; + qFatal("macOS only supports ANGLE."); +#endif // !defined(Q_OS_MACOS) + } + + return std::make_unique<QtWebEngineCore::NativeSkiaOutputDeviceOpenGL>( + context_state_, renderer_settings_.requires_alpha_channel, + shared_gpu_deps_->memory_tracker(), dependency_.get(), shared_image_factory_.get(), + shared_image_representation_factory_.get(), GetDidSwapBuffersCompleteCallback()); + } #endif // QT_CONFIG(opengl) -} -void gpu::InProcessCommandBuffer::GetTextureQt( - unsigned int client_id, - GetTextureCallback callback, - const std::vector<SyncToken>& sync_token_fences) -{ - ScheduleGpuTask(base::BindOnce(&InProcessCommandBuffer::GetTextureQtOnGpuThread, - gpu_thread_weak_ptr_factory_.GetWeakPtr(), - client_id, - std::move(callback)), - sync_token_fences); -} +#if BUILDFLAG(ENABLE_VULKAN) + if (graphicsApi == QSGRendererInterface::Vulkan) { +#if !defined(Q_OS_MACOS) + return std::make_unique<QtWebEngineCore::NativeSkiaOutputDeviceVulkan>( + context_state_, renderer_settings_.requires_alpha_channel, + shared_gpu_deps_->memory_tracker(), dependency_.get(), shared_image_factory_.get(), + shared_image_representation_factory_.get(), GetDidSwapBuffersCompleteCallback()); +#else + qFatal("Vulkan is not supported on macOS."); +#endif // !defined(Q_OS_MACOS) + } +#endif // BUILDFLAG(ENABLE_VULKAN) -void gpu::InProcessCommandBuffer::GetTextureQtOnGpuThread( - unsigned int client_id, GetTextureCallback callback) -{ - if (!MakeCurrent()) { - LOG(ERROR) << "MakeCurrent failed for GetTextureQt"; - return; +#if defined(Q_OS_WIN) + if (graphicsApi == QSGRendererInterface::Direct3D11) { + if (gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE) + qFatal("Direct3D11 is only supported over ANGLE."); + + return std::make_unique<QtWebEngineCore::NativeSkiaOutputDeviceDirect3D11>( + context_state_, renderer_settings_.requires_alpha_channel, + shared_gpu_deps_->memory_tracker(), dependency_.get(), shared_image_factory_.get(), + shared_image_representation_factory_.get(), GetDidSwapBuffersCompleteCallback()); + } +#endif + +#if defined(Q_OS_MACOS) + if (graphicsApi == QSGRendererInterface::Metal) { + if (gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE) + qFatal("Metal is only supported over ANGLE."); + + return std::make_unique<QtWebEngineCore::NativeSkiaOutputDeviceMetal>( + context_state_, renderer_settings_.requires_alpha_channel, + shared_gpu_deps_->memory_tracker(), dependency_.get(), shared_image_factory_.get(), + shared_image_representation_factory_.get(), GetDidSwapBuffersCompleteCallback()); } - gpu::TextureBase *texture = decoder_->GetTextureBase(client_id); - std::move(callback).Run(texture ? texture->service_id() : 0, gl::GLFence::Create()); +#endif + + qFatal() << "Unsupported Graphics API:" << graphicsApi; + return nullptr; } diff --git a/src/core/compositor/display_skia_output_device.cpp b/src/core/compositor/display_skia_output_device.cpp index 69cb65eeb..0ca466fe8 100644 --- a/src/core/compositor/display_skia_output_device.cpp +++ b/src/core/compositor/display_skia_output_device.cpp @@ -1,48 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "display_skia_output_device.h" +#include "compositor_resource_fence.h" #include "type_conversion.h" #include "gpu/command_buffer/service/skia_utils.h" -#include "skia/ext/legacy_display_globals.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkSurfaceProps.h" +#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" +#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" + +#include <QSGTexture> namespace QtWebEngineCore { @@ -53,30 +23,25 @@ public: : m_parent(parent) , m_shape(m_parent->m_shape) { - auto formatIndex = static_cast<int>(m_shape.format); - const auto &colorType = m_parent->capabilities_.sk_color_types[formatIndex]; - DCHECK(colorType != kUnknown_SkColorType) - << "SkColorType is invalid for format: " << formatIndex; + } + void initialize() + { + const auto &colorType = m_shape.imageInfo.colorType(); + DCHECK(colorType != kUnknown_SkColorType); m_texture = m_parent->m_contextState->gr_context()->createBackendTexture( - m_shape.sizeInPixels.width(), m_shape.sizeInPixels.height(), colorType, + m_shape.imageInfo.width(), m_shape.imageInfo.height(), colorType, GrMipMapped::kNo, GrRenderable::kYes); DCHECK(m_texture.isValid()); - if (m_texture.backend() == GrBackendApi::kVulkan) { - GrVkImageInfo info; - bool result = m_texture.getVkImageInfo(&info); - DCHECK(result); - m_estimatedSize = info.fAlloc.fSize; - } else { - auto info = SkImageInfo::Make(m_shape.sizeInPixels.width(), m_shape.sizeInPixels.height(), - colorType, kUnpremul_SkAlphaType); - m_estimatedSize = info.computeMinByteSize(); - } + DCHECK(m_texture.backend() == GrBackendApi::kOpenGL); + auto info = SkImageInfo::Make(m_shape.imageInfo.width(), m_shape.imageInfo.height(), + colorType, kUnpremul_SkAlphaType); + m_estimatedSize = info.computeMinByteSize(); m_parent->memory_type_tracker_->TrackMemAlloc(m_estimatedSize); - SkSurfaceProps surfaceProps = skia::LegacyDisplayGlobals::GetSkSurfaceProps(); - m_surface = SkSurface::MakeFromBackendTexture( + SkSurfaceProps surfaceProps = SkSurfaceProps{0, kUnknown_SkPixelGeometry}; + m_surface = SkSurfaces::WrapBackendTexture( m_parent->m_contextState->gr_context(), m_texture, m_parent->capabilities_.output_surface_origin == gfx::SurfaceOrigin::kTopLeft ? kTopLeft_GrSurfaceOrigin @@ -87,8 +52,10 @@ public: ~Buffer() { - DeleteGrBackendTexture(m_parent->m_contextState.get(), &m_texture); - m_parent->memory_type_tracker_->TrackMemFree(m_estimatedSize); + if (m_texture.isValid()) { + DeleteGrBackendTexture(m_parent->m_contextState.get(), &m_texture); + m_parent->memory_type_tracker_->TrackMemFree(m_estimatedSize); + } } void createFence() @@ -119,16 +86,20 @@ private: DisplaySkiaOutputDevice::DisplaySkiaOutputDevice( scoped_refptr<gpu::SharedContextState> contextState, + bool requiresAlpha, gpu::MemoryTracker *memoryTracker, DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) - : SkiaOutputDevice( - contextState->gr_context(), - memoryTracker, - didSwapBufferCompleteCallback) + : SkiaOutputDevice(contextState->gr_context(), contextState->graphite_context(), + memoryTracker, didSwapBufferCompleteCallback) , Compositor(Compositor::Type::OpenGL) , m_contextState(contextState) + , m_requiresAlpha(requiresAlpha) { capabilities_.uses_default_gl_framebuffer = false; + capabilities_.supports_surfaceless = true; + capabilities_.preserve_buffer_content = true; + capabilities_.only_invalidates_damage_rect = false; + capabilities_.number_of_buffers = 3; capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = kRGBA_8888_SkColorType; @@ -148,20 +119,20 @@ void DisplaySkiaOutputDevice::SetFrameSinkId(const viz::FrameSinkId &id) { bind(id); } - -bool DisplaySkiaOutputDevice::Reshape(const gfx::Size& sizeInPixels, - float devicePixelRatio, - const gfx::ColorSpace& colorSpace, - gfx::BufferFormat format, +bool DisplaySkiaOutputDevice::Reshape(const SkImageInfo &image_info, + const gfx::ColorSpace &colorSpace, + int sample_count, + float device_scale_factor, gfx::OverlayTransform transform) { - m_shape = Shape{sizeInPixels, devicePixelRatio, colorSpace, format}; + m_shape = Shape{image_info, device_scale_factor, colorSpace}; DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE); return true; } -void DisplaySkiaOutputDevice::SwapBuffers(BufferPresentedCallback feedback, - viz::OutputSurfaceFrame frame) +void DisplaySkiaOutputDevice::Present(const absl::optional<gfx::Rect> &update_rect, + BufferPresentedCallback feedback, + viz::OutputSurfaceFrame frame) { DCHECK(m_backBuffer); @@ -171,8 +142,8 @@ void DisplaySkiaOutputDevice::SwapBuffers(BufferPresentedCallback feedback, { QMutexLocker locker(&m_mutex); - m_taskRunner = base::ThreadTaskRunnerHandle::Get(); - m_middleBuffer = std::move(m_backBuffer); + m_taskRunner = base::SingleThreadTaskRunner::GetCurrentDefault(); + std::swap(m_middleBuffer, m_backBuffer); m_readyToUpdate = true; } @@ -188,10 +159,12 @@ void DisplaySkiaOutputDevice::DiscardBackbuffer() { } -SkSurface *DisplaySkiaOutputDevice::BeginPaint(std::vector<GrBackendSemaphore> *) +SkSurface *DisplaySkiaOutputDevice::BeginPaint(std::vector<GrBackendSemaphore> *end_semaphores) { - if (!m_backBuffer || m_backBuffer->shape() != m_shape) + if (!m_backBuffer || m_backBuffer->shape() != m_shape) { m_backBuffer = std::make_unique<Buffer>(this); + m_backBuffer->initialize(); + } return m_backBuffer->surface(); } @@ -218,26 +191,33 @@ void DisplaySkiaOutputDevice::waitForTexture() m_frontBuffer->consumeFence(); } -int DisplaySkiaOutputDevice::textureId() +QSGTexture *DisplaySkiaOutputDevice::texture(QQuickWindow *win, uint32_t textureOptions) { if (!m_frontBuffer) - return 0; + return nullptr; + + QQuickWindow::CreateTextureOptions texOpts(textureOptions); + QSGTexture *texture = nullptr; GrGLTextureInfo info; - if (!m_frontBuffer->texture().getGLTextureInfo(&info)) - return 0; + if (GrBackendTextures::GetGLTextureInfo(m_frontBuffer->texture(), &info)) + texture = QNativeInterface::QSGOpenGLTexture::fromNative(info.fID, win, size(), texOpts); + return texture; +} - return info.fID; +bool DisplaySkiaOutputDevice::textureIsFlipped() +{ + return true; } QSize DisplaySkiaOutputDevice::size() { - return m_frontBuffer ? toQt(m_frontBuffer->shape().sizeInPixels) : QSize(); + return m_frontBuffer ? toQt(m_frontBuffer->shape().imageInfo.dimensions()) : QSize(); } -bool DisplaySkiaOutputDevice::hasAlphaChannel() +bool DisplaySkiaOutputDevice::requiresAlphaChannel() { - return true; + return m_requiresAlpha; } float DisplaySkiaOutputDevice::devicePixelRatio() @@ -249,11 +229,11 @@ void DisplaySkiaOutputDevice::SwapBuffersFinished() { { QMutexLocker locker(&m_mutex); - m_backBuffer = std::move(m_middleBuffer); + std::swap(m_backBuffer, m_middleBuffer); } FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK), - gfx::Size(m_shape.sizeInPixels.width(), m_shape.sizeInPixels.height()), + gfx::Size(m_shape.imageInfo.width(), m_shape.imageInfo.height()), std::move(m_frame)); } diff --git a/src/core/compositor/display_skia_output_device.h b/src/core/compositor/display_skia_output_device.h index 2993e9147..e6a97b810 100644 --- a/src/core/compositor/display_skia_output_device.h +++ b/src/core/compositor/display_skia_output_device.h @@ -1,53 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef DISPLAY_SKIA_OUTPUT_DEVICE_H #define DISPLAY_SKIA_OUTPUT_DEVICE_H -#include "compositor_resource_fence.h" +#include <QtCore/QMutex> +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> + #include "compositor.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/task/single_thread_task_runner.h" #include "components/viz/service/display_embedder/skia_output_device.h" #include "gpu/command_buffer/service/shared_context_state.h" -#include <QMutex> +QT_BEGIN_NAMESPACE +class QQuickWindow; +QT_END_NAMESPACE namespace QtWebEngineCore { @@ -55,19 +23,21 @@ class DisplaySkiaOutputDevice final : public viz::SkiaOutputDevice, public Compo { public: DisplaySkiaOutputDevice(scoped_refptr<gpu::SharedContextState> contextState, + bool requiresAlpha, gpu::MemoryTracker *memoryTracker, DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); ~DisplaySkiaOutputDevice() override; // Overridden from SkiaOutputDevice. void SetFrameSinkId(const viz::FrameSinkId &frame_sink_id) override; - bool Reshape(const gfx::Size& size, - float devicePixelRatio, - const gfx::ColorSpace& colorSpace, - gfx::BufferFormat format, + bool Reshape(const SkImageInfo &image_info, + const gfx::ColorSpace &color_space, + int sample_count, + float device_scale_factor, gfx::OverlayTransform transform) override; - void SwapBuffers(BufferPresentedCallback feedback, - viz::OutputSurfaceFrame frame) override; + void Present(const absl::optional<gfx::Rect>& update_rect, + BufferPresentedCallback feedback, + viz::OutputSurfaceFrame frame) override; void EnsureBackbuffer() override; void DiscardBackbuffer() override; SkSurface *BeginPaint(std::vector<GrBackendSemaphore> *semaphores) override; @@ -76,25 +46,24 @@ public: // Overridden from Compositor. void swapFrame() override; void waitForTexture() override; - int textureId() override; + QSGTexture *texture(QQuickWindow *win, uint32_t texOpts) override; + bool textureIsFlipped() override; QSize size() override; - bool hasAlphaChannel() override; + bool requiresAlphaChannel() override; float devicePixelRatio() override; private: struct Shape { - gfx::Size sizeInPixels; + SkImageInfo imageInfo; float devicePixelRatio; gfx::ColorSpace colorSpace; - gfx::BufferFormat format; bool operator==(const Shape &that) const { - return (sizeInPixels == that.sizeInPixels && + return (imageInfo == that.imageInfo && devicePixelRatio == that.devicePixelRatio && - colorSpace == that.colorSpace && - format == that.format); + colorSpace == that.colorSpace); } bool operator!=(const Shape &that) const { return !(*this == that); } }; @@ -111,6 +80,7 @@ private: std::unique_ptr<Buffer> m_backBuffer; viz::OutputSurfaceFrame m_frame; bool m_readyToUpdate = false; + bool m_requiresAlpha; scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; }; diff --git a/src/core/compositor/display_software_output_surface.cpp b/src/core/compositor/display_software_output_surface.cpp index 98c104ddf..2c208ca57 100644 --- a/src/core/compositor/display_software_output_surface.cpp +++ b/src/core/compositor/display_software_output_surface.cpp @@ -1,54 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "display_software_output_surface.h" #include "compositor.h" -#include "render_widget_host_view_qt_delegate.h" #include "type_conversion.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/task/single_thread_task_runner.h" #include "components/viz/service/display/display.h" #include "components/viz/service/display/output_surface_frame.h" #include <QMutex> #include <QPainter> +#include <QQuickWindow> namespace QtWebEngineCore { @@ -56,36 +20,34 @@ class DisplaySoftwareOutputSurface::Device final : public viz::SoftwareOutputDev public Compositor { public: - Device(); - ~Device(); + Device(bool requiresAlpha); // Overridden from viz::SoftwareOutputDevice. void Resize(const gfx::Size &sizeInPixels, float devicePixelRatio) override; - void OnSwapBuffers(SwapBuffersCallback swap_ack_callback) override; + void OnSwapBuffers(SwapBuffersCallback swap_ack_callback, gfx::FrameData data) override; // Overridden from Compositor. void swapFrame() override; - QImage image() override; + QSGTexture *texture(QQuickWindow *win, uint32_t) override; + bool textureIsFlipped() override; float devicePixelRatio() override; QSize size() override; - bool hasAlphaChannel() override; + bool requiresAlphaChannel() override; private: mutable QMutex m_mutex; float m_devicePixelRatio = 1.0; + bool m_requiresAlpha; scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; SwapBuffersCallback m_swapCompletionCallback; QImage m_image; float m_imageDevicePixelRatio = 1.0; }; -DisplaySoftwareOutputSurface::Device::Device() +DisplaySoftwareOutputSurface::Device::Device(bool requiresAlpha) : Compositor(Type::Software) -{} - -DisplaySoftwareOutputSurface::Device::~Device() + , m_requiresAlpha(requiresAlpha) { - unbind(); } void DisplaySoftwareOutputSurface::Device::Resize(const gfx::Size &sizeInPixels, float devicePixelRatio) @@ -94,14 +56,17 @@ void DisplaySoftwareOutputSurface::Device::Resize(const gfx::Size &sizeInPixels, return; m_devicePixelRatio = devicePixelRatio; viewport_pixel_size_ = sizeInPixels; - surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(sizeInPixels.width(), sizeInPixels.height())); + surface_ = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(sizeInPixels.width(), sizeInPixels.height())); } -void DisplaySoftwareOutputSurface::Device::OnSwapBuffers(SwapBuffersCallback swap_ack_callback) +void DisplaySoftwareOutputSurface::Device::OnSwapBuffers(SwapBuffersCallback swap_ack_callback, gfx::FrameData data) { - QMutexLocker locker(&m_mutex); - m_taskRunner = base::ThreadTaskRunnerHandle::Get(); - m_swapCompletionCallback = std::move(swap_ack_callback); + { // MEMO don't hold a lock together with an 'observer', as the call from Qt's scene graph may come at the same time + QMutexLocker locker(&m_mutex); + m_taskRunner = base::SingleThreadTaskRunner::GetCurrentDefault(); + m_swapCompletionCallback = std::move(swap_ack_callback); + } + if (auto obs = observer()) obs->readyToSwap(); } @@ -146,9 +111,14 @@ void DisplaySoftwareOutputSurface::Device::swapFrame() m_taskRunner.reset(); } -QImage DisplaySoftwareOutputSurface::Device::image() +QSGTexture *DisplaySoftwareOutputSurface::Device::texture(QQuickWindow *win, uint32_t) +{ + return win->createTextureFromImage(m_image); +} + +bool DisplaySoftwareOutputSurface::Device::textureIsFlipped() { - return m_image; + return false; } float DisplaySoftwareOutputSurface::Device::devicePixelRatio() @@ -161,13 +131,13 @@ QSize DisplaySoftwareOutputSurface::Device::size() return m_image.size(); } -bool DisplaySoftwareOutputSurface::Device::hasAlphaChannel() +bool DisplaySoftwareOutputSurface::Device::requiresAlphaChannel() { - return m_image.format() == QImage::Format_ARGB32_Premultiplied; + return m_requiresAlpha; } -DisplaySoftwareOutputSurface::DisplaySoftwareOutputSurface() - : SoftwareOutputSurface(std::make_unique<Device>()) +DisplaySoftwareOutputSurface::DisplaySoftwareOutputSurface(bool requiresAlpha) + : SoftwareOutputSurface(std::make_unique<Device>(requiresAlpha)) {} DisplaySoftwareOutputSurface::~DisplaySoftwareOutputSurface() {} diff --git a/src/core/compositor/display_software_output_surface.h b/src/core/compositor/display_software_output_surface.h index 27687c7c0..d23664d56 100644 --- a/src/core/compositor/display_software_output_surface.h +++ b/src/core/compositor/display_software_output_surface.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 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) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef DISPLAY_SOFTWARE_OUTPUT_SURFACE_H #define DISPLAY_SOFTWARE_OUTPUT_SURFACE_H @@ -47,7 +11,7 @@ namespace QtWebEngineCore { class DisplaySoftwareOutputSurface final : public viz::SoftwareOutputSurface { public: - DisplaySoftwareOutputSurface(); + DisplaySoftwareOutputSurface(bool requiresAlpha); ~DisplaySoftwareOutputSurface() override; // Overridden from viz::OutputSurface. diff --git a/src/core/compositor/native_skia_output_device.cpp b/src/core/compositor/native_skia_output_device.cpp new file mode 100644 index 000000000..708692df7 --- /dev/null +++ b/src/core/compositor/native_skia_output_device.cpp @@ -0,0 +1,422 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "native_skia_output_device.h" + +#include "type_conversion.h" + +#include "components/viz/common/resources/shared_image_format.h" +#include "components/viz/common/resources/shared_image_format_utils.h" +#include "components/viz/service/display_embedder/skia_output_surface_dependency.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/command_buffer/common/shared_image_usage.h" +#include "gpu/command_buffer/service/shared_image/shared_image_factory.h" +#include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/command_buffer/service/skia_utils.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/core/SkSurfaceProps.h" +#include "ui/gfx/native_pixmap.h" +#include "ui/gfx/gpu_fence.h" +#include "ui/gl/gl_fence.h" + +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_platform.h" +#endif + +namespace QtWebEngineCore { + +namespace { + +// Helper function for moving a GpuFence from a fence handle to a unique_ptr. +std::unique_ptr<gfx::GpuFence> TakeGpuFence(gfx::GpuFenceHandle fence) +{ + return fence.is_null() ? nullptr : std::make_unique<gfx::GpuFence>(std::move(fence)); +} + +} // namespace + +NativeSkiaOutputDevice::NativeSkiaOutputDevice( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) + : SkiaOutputDevice(contextState->gr_context(), contextState->graphite_context(), memoryTracker, + didSwapBufferCompleteCallback) + , Compositor(Type::Native) + , m_contextState(contextState) + , m_requiresAlpha(requiresAlpha) + , m_factory(shared_image_factory) + , m_representationFactory(shared_image_representation_factory) + , m_deps(dependency) +{ + capabilities_.uses_default_gl_framebuffer = false; + capabilities_.supports_surfaceless = true; + capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft; + capabilities_.preserve_buffer_content = true; + capabilities_.only_invalidates_damage_rect = false; + +#if defined(USE_OZONE) + m_isNativeBufferSupported = ui::OzonePlatform::GetInstance() + ->GetPlatformRuntimeProperties() + .supports_native_pixmaps; +#endif +} + +NativeSkiaOutputDevice::~NativeSkiaOutputDevice() +{ +} + +void NativeSkiaOutputDevice::SetFrameSinkId(const viz::FrameSinkId &id) +{ + bind(id); +} + +bool NativeSkiaOutputDevice::Reshape(const SkImageInfo &image_info, + const gfx::ColorSpace &colorSpace, + int sample_count, + float device_scale_factor, + gfx::OverlayTransform transform) +{ + m_shape = Shape{image_info, device_scale_factor, colorSpace, sample_count}; + DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE); + return true; +} + +void NativeSkiaOutputDevice::Present(const absl::optional<gfx::Rect> &update_rect, + BufferPresentedCallback feedback, + viz::OutputSurfaceFrame frame) +{ + DCHECK(m_backBuffer); + + StartSwapBuffers(std::move(feedback)); + m_frame = std::move(frame); + { + QMutexLocker locker(&m_mutex); + m_backBuffer->createFence(); + m_gpuTaskRunner = base::SingleThreadTaskRunner::GetCurrentDefault(); + std::swap(m_middleBuffer, m_backBuffer); + m_readyToUpdate = true; + } + + if (auto obs = observer()) + obs->readyToSwap(); +} + +void NativeSkiaOutputDevice::EnsureBackbuffer() +{ +} + +void NativeSkiaOutputDevice::DiscardBackbuffer() +{ +} + +SkSurface *NativeSkiaOutputDevice::BeginPaint(std::vector<GrBackendSemaphore> *end_semaphores) +{ + { + QMutexLocker locker(&m_mutex); + if (!m_backBuffer || m_backBuffer->shape() != m_shape) { + m_backBuffer = std::make_unique<Buffer>(this); + if (!m_backBuffer->initialize()) + return nullptr; + } + } + auto surface = m_backBuffer->beginWriteSkia(); + *end_semaphores = m_backBuffer->takeEndWriteSkiaSemaphores(); + return surface; +} + +void NativeSkiaOutputDevice::EndPaint() +{ + m_backBuffer->endWriteSkia(true); +} + +void NativeSkiaOutputDevice::swapFrame() +{ + QMutexLocker locker(&m_mutex); + if (m_readyToUpdate) { + std::swap(m_frontBuffer, m_middleBuffer); + m_gpuTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&NativeSkiaOutputDevice::SwapBuffersFinished, + base::Unretained(this))); + m_readyToUpdate = false; + if (m_frontBuffer) { + m_readyWithTexture = true; + m_frontBuffer->beginPresent(); + } + if (m_middleBuffer) + m_middleBuffer->freeTexture(); + m_gpuTaskRunner.reset(); + } +} + +void NativeSkiaOutputDevice::waitForTexture() +{ + if (m_readyWithTexture) + m_frontBuffer->consumeFence(); +} + +void NativeSkiaOutputDevice::releaseTexture() +{ + if (m_readyWithTexture) { + m_frontBuffer->endPresent(); + m_readyWithTexture = false; + } +} + +void NativeSkiaOutputDevice::releaseResources() +{ + if (m_frontBuffer) + m_frontBuffer->freeTexture(); +} + +bool NativeSkiaOutputDevice::textureIsFlipped() +{ + return false; +} + +QSize NativeSkiaOutputDevice::size() +{ + return m_frontBuffer ? toQt(m_frontBuffer->shape().imageInfo.dimensions()) : QSize(); +} + +bool NativeSkiaOutputDevice::requiresAlphaChannel() +{ + return m_requiresAlpha; +} + +float NativeSkiaOutputDevice::devicePixelRatio() +{ + return m_frontBuffer ? m_frontBuffer->shape().devicePixelRatio : 1; +} + +void NativeSkiaOutputDevice::SwapBuffersFinished() +{ + { + QMutexLocker locker(&m_mutex); + std::swap(m_backBuffer, m_middleBuffer); + } + + FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK), + gfx::Size(m_shape.imageInfo.width(), m_shape.imageInfo.height()), + std::move(m_frame)); +} + +NativeSkiaOutputDevice::Buffer::Buffer(NativeSkiaOutputDevice *parent) + : m_parent(parent), m_shape(m_parent->m_shape) +{ +} + +NativeSkiaOutputDevice::Buffer::~Buffer() +{ + if (m_scopedSkiaWriteAccess) + endWriteSkia(false); + + if (!m_mailbox.IsZero()) + m_parent->m_factory->DestroySharedImage(m_mailbox); +} + +// The following Buffer methods are based on +// components/viz/service/display_embedder/output_presenter.cc: Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +bool NativeSkiaOutputDevice::Buffer::initialize() +{ + static const uint32_t kDefaultSharedImageUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT + | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE + | gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; + auto mailbox = gpu::Mailbox::GenerateForSharedImage(); + + SkColorType skColorType = m_shape.imageInfo.colorType(); + if (!m_parent->m_factory->CreateSharedImage( + mailbox, viz::SkColorTypeToSinglePlaneSharedImageFormat(skColorType), + { m_shape.imageInfo.width(), m_shape.imageInfo.height() }, m_shape.colorSpace, + kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, m_parent->m_deps->GetSurfaceHandle(), + kDefaultSharedImageUsage, "QWE_SharedImageBuffer")) { + LOG(ERROR) << "CreateSharedImage failed."; + return false; + } + m_mailbox = mailbox; + + m_skiaRepresentation = + m_parent->m_representationFactory->ProduceSkia(m_mailbox, m_parent->m_contextState); + if (!m_skiaRepresentation) { + LOG(ERROR) << "ProduceSkia() failed."; + return false; + } + + if (m_parent->m_isNativeBufferSupported) { + m_overlayRepresentation = m_parent->m_representationFactory->ProduceOverlay(m_mailbox); + if (!m_overlayRepresentation) { + LOG(ERROR) << "ProduceOverlay() failed"; + return false; + } + } + + return true; +} + +SkSurface *NativeSkiaOutputDevice::Buffer::beginWriteSkia() +{ + DCHECK(!m_scopedSkiaWriteAccess); + DCHECK(!m_presentCount); + DCHECK(m_endSemaphores.empty()); + + std::vector<GrBackendSemaphore> beginSemaphores; + SkSurfaceProps surface_props{ 0, kUnknown_SkPixelGeometry }; + + // Buffer queue is internal to GPU proc and handles texture initialization, + // so allow uncleared access. + m_scopedSkiaWriteAccess = m_skiaRepresentation->BeginScopedWriteAccess( + m_shape.sampleCount, surface_props, &beginSemaphores, &m_endSemaphores, + gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes); + DCHECK(m_scopedSkiaWriteAccess); + if (!beginSemaphores.empty()) { + m_scopedSkiaWriteAccess->surface()->wait(beginSemaphores.size(), beginSemaphores.data(), + /*deleteSemaphoresAfterWait=*/false); + } + return m_scopedSkiaWriteAccess->surface(); +} + +void NativeSkiaOutputDevice::Buffer::endWriteSkia(bool force_flush) +{ + // The Flush now takes place in finishPaintCurrentBuffer on the CPU side. + // check if end_semaphores is not empty then flush here + DCHECK(m_scopedSkiaWriteAccess); + if (!m_endSemaphores.empty() || force_flush) { + GrFlushInfo flush_info = {}; + flush_info.fNumSemaphores = m_endSemaphores.size(); + flush_info.fSignalSemaphores = m_endSemaphores.data(); + auto *direct_context = + m_scopedSkiaWriteAccess->surface()->recordingContext()->asDirectContext(); + DCHECK(direct_context); + direct_context->flush(m_scopedSkiaWriteAccess->surface(), {}); + m_scopedSkiaWriteAccess->ApplyBackendSurfaceEndState(); + direct_context->flush(m_scopedSkiaWriteAccess->surface(), flush_info, nullptr); + direct_context->submit(); + } + m_scopedSkiaWriteAccess.reset(); + m_endSemaphores.clear(); + + // SkiaRenderer always draws the full frame. + m_skiaRepresentation->SetCleared(); +} + +std::vector<GrBackendSemaphore> NativeSkiaOutputDevice::Buffer::takeEndWriteSkiaSemaphores() +{ + return std::exchange(m_endSemaphores, {}); +} + +void NativeSkiaOutputDevice::Buffer::createSkImageOnGPUThread() +{ + if (!m_scopedSkiaReadAccess) { + m_skImageMutex.unlock(); + return; + } + + m_cachedSkImage = m_scopedSkiaReadAccess->CreateSkImage(m_parent->m_contextState.get()); + m_skImageMutex.unlock(); + if (!m_cachedSkImage) + qWarning("SKIA: Failed to create SkImage."); +} + +void NativeSkiaOutputDevice::Buffer::beginPresent() +{ + if (++m_presentCount != 1) { + DCHECK(m_scopedOverlayReadAccess || m_scopedSkiaReadAccess); + return; + } + + DCHECK(!m_scopedSkiaWriteAccess); + DCHECK(!m_scopedOverlayReadAccess && !m_scopedSkiaReadAccess); + + if (m_overlayRepresentation) { + m_scopedOverlayReadAccess = m_overlayRepresentation->BeginScopedReadAccess(); + DCHECK(m_scopedOverlayReadAccess); + m_acquireFence = TakeGpuFence(m_scopedOverlayReadAccess->TakeAcquireFence()); + } else { + DCHECK(m_skiaRepresentation); + std::vector<GrBackendSemaphore> beginSemaphores; + m_scopedSkiaReadAccess = + m_skiaRepresentation->BeginScopedReadAccess(&beginSemaphores, nullptr); + DCHECK(m_scopedSkiaReadAccess); + if (!beginSemaphores.empty()) + qWarning("SKIA: Unexpected semaphores while reading texture, wait is not implemented."); + + m_skImageMutex.tryLock(); + m_parent->m_gpuTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&NativeSkiaOutputDevice::Buffer::createSkImageOnGPUThread, + base::Unretained(this))); + } +} + +void NativeSkiaOutputDevice::Buffer::endPresent() +{ + if (!m_presentCount) + return; + DCHECK(m_scopedOverlayReadAccess || m_scopedSkiaReadAccess); + if (--m_presentCount) + return; + + if (m_scopedOverlayReadAccess) { + DCHECK(!m_scopedSkiaReadAccess); + m_scopedOverlayReadAccess.reset(); + } else if (m_scopedSkiaReadAccess) { + DCHECK(!m_scopedOverlayReadAccess); + QMutexLocker locker(&m_skImageMutex); + m_scopedSkiaReadAccess.reset(); + } +} + +void NativeSkiaOutputDevice::Buffer::freeTexture() +{ + if (textureCleanupCallback) { + textureCleanupCallback(); + textureCleanupCallback = nullptr; + } +} + +void NativeSkiaOutputDevice::Buffer::createFence() +{ + // For some reason we still need to create this, but we do not need to wait on it. + if (m_parent->m_contextState->gr_context_type() == gpu::GrContextType::kGL) + m_fence = gl::GLFence::Create(); +} + +void NativeSkiaOutputDevice::Buffer::consumeFence() +{ + if (m_acquireFence) { + m_acquireFence->Wait(); + m_acquireFence.reset(); + } +} + +sk_sp<SkImage> NativeSkiaOutputDevice::Buffer::skImage() +{ + QMutexLocker locker(&m_skImageMutex); + return m_cachedSkImage; +} +#if defined(USE_OZONE) +scoped_refptr<gfx::NativePixmap> NativeSkiaOutputDevice::Buffer::nativePixmap() +{ + DCHECK(m_presentCount); + if (!m_scopedOverlayReadAccess) + return nullptr; + + return m_scopedOverlayReadAccess->GetNativePixmap(); +} +#elif defined(Q_OS_WIN) +absl::optional<gl::DCLayerOverlayImage> NativeSkiaOutputDevice::Buffer::overlayImage() const +{ + DCHECK(m_presentCount); + return m_scopedOverlayReadAccess->GetDCLayerOverlayImage(); +} +#elif defined(Q_OS_MACOS) +gfx::ScopedIOSurface NativeSkiaOutputDevice::Buffer::ioSurface() const +{ + DCHECK(m_presentCount); + return m_scopedOverlayReadAccess->GetIOSurface(); +} +#endif + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/native_skia_output_device.h b/src/core/compositor/native_skia_output_device.h new file mode 100644 index 000000000..2c35cef77 --- /dev/null +++ b/src/core/compositor/native_skia_output_device.h @@ -0,0 +1,183 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef NATIVE_SKIA_OUTPUT_DEVICE_H +#define NATIVE_SKIA_OUTPUT_DEVICE_H + +#include "compositor.h" + +#include "base/task/single_thread_task_runner.h" +#include "components/viz/service/display_embedder/skia_output_device.h" +#include "gpu/command_buffer/service/shared_context_state.h" +#include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/config/gpu_preferences.h" + +#include <QMutex> + +#if defined(Q_OS_WIN) +#include "ui/gl/dc_layer_overlay_image.h" +#endif + +#if defined(Q_OS_MACOS) +#include "ui/gfx/mac/io_surface.h" +#endif + +QT_BEGIN_NAMESPACE +class QQuickWindow; +QT_END_NAMESPACE + +namespace gl { +class GLFence; +} + +namespace gfx { +class GpuFence; +class NativePixmap; +} + +namespace gpu { +class SharedImageFactory; +class SharedImageRepresentationFactory; +} + +namespace viz { +class SkiaOutputSurfaceDependency; +} + +namespace QtWebEngineCore { + +class NativeSkiaOutputDevice : public viz::SkiaOutputDevice, public Compositor +{ +public: + NativeSkiaOutputDevice(scoped_refptr<gpu::SharedContextState> contextState, + bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, + viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); + ~NativeSkiaOutputDevice() override; + + // Overridden from SkiaOutputDevice. + void SetFrameSinkId(const viz::FrameSinkId &frame_sink_id) override; + bool Reshape(const SkImageInfo &image_info, + const gfx::ColorSpace &color_space, + int sample_count, + float device_scale_factor, + gfx::OverlayTransform transform) override; + void Present(const absl::optional<gfx::Rect>& update_rect, + BufferPresentedCallback feedback, + viz::OutputSurfaceFrame frame) override; + void EnsureBackbuffer() override; + void DiscardBackbuffer() override; + SkSurface *BeginPaint(std::vector<GrBackendSemaphore> *semaphores) override; + void EndPaint() override; + + // Overridden from Compositor. + void swapFrame() override; + void waitForTexture() override; + void releaseTexture() override; + void releaseResources() override; + bool textureIsFlipped() override; + QSize size() override; + bool requiresAlphaChannel() override; + float devicePixelRatio() override; + +protected: + struct Shape + { + SkImageInfo imageInfo; + float devicePixelRatio; + gfx::ColorSpace colorSpace; + int sampleCount; + + bool operator==(const Shape &that) const + { + return (imageInfo == that.imageInfo && + devicePixelRatio == that.devicePixelRatio && + colorSpace == that.colorSpace && + sampleCount == that.sampleCount); + } + bool operator!=(const Shape &that) const { return !(*this == that); } + }; + + class Buffer + { + public: + Buffer(NativeSkiaOutputDevice *parent); + ~Buffer(); + + bool initialize(); + SkSurface *beginWriteSkia(); + void endWriteSkia(bool force_flush); + std::vector<GrBackendSemaphore> takeEndWriteSkiaSemaphores(); + void beginPresent(); + void endPresent(); + void freeTexture(); + void createFence(); + void consumeFence(); + + sk_sp<SkImage> skImage(); +#if defined(USE_OZONE) + scoped_refptr<gfx::NativePixmap> nativePixmap(); +#elif defined(Q_OS_WIN) + absl::optional<gl::DCLayerOverlayImage> overlayImage() const; +#elif defined(Q_OS_MACOS) + gfx::ScopedIOSurface ioSurface() const; +#endif + + const Shape &shape() const { return m_shape; } + viz::SharedImageFormat sharedImageFormat() const { return m_skiaRepresentation->format(); } + + std::function<void()> textureCleanupCallback; + + private: + void createSkImageOnGPUThread(); + + NativeSkiaOutputDevice *m_parent; + Shape m_shape; + uint64_t m_estimatedSize = 0; // FIXME: estimate size + std::unique_ptr<gfx::GpuFence> m_acquireFence; + std::unique_ptr<gl::GLFence> m_fence; + gpu::Mailbox m_mailbox; + std::unique_ptr<gpu::SkiaImageRepresentation> m_skiaRepresentation; + std::unique_ptr<gpu::SkiaImageRepresentation::ScopedWriteAccess> m_scopedSkiaWriteAccess; + std::unique_ptr<gpu::SkiaImageRepresentation::ScopedReadAccess> m_scopedSkiaReadAccess; + std::unique_ptr<gpu::OverlayImageRepresentation> m_overlayRepresentation; + std::unique_ptr<gpu::OverlayImageRepresentation::ScopedReadAccess> + m_scopedOverlayReadAccess; + std::vector<GrBackendSemaphore> m_endSemaphores; + int m_presentCount = 0; + + mutable QMutex m_skImageMutex; + sk_sp<SkImage> m_cachedSkImage; + }; + +protected: + scoped_refptr<gpu::SharedContextState> m_contextState; + std::unique_ptr<Buffer> m_frontBuffer; + bool m_readyWithTexture = false; + bool m_isNativeBufferSupported = true; + +private: + friend class NativeSkiaOutputDevice::Buffer; + + void SwapBuffersFinished(); + + mutable QMutex m_mutex; + Shape m_shape; + std::unique_ptr<Buffer> m_middleBuffer; + std::unique_ptr<Buffer> m_backBuffer; + viz::OutputSurfaceFrame m_frame; + bool m_readyToUpdate = false; + bool m_requiresAlpha; + scoped_refptr<base::SingleThreadTaskRunner> m_gpuTaskRunner; + + const raw_ptr<gpu::SharedImageFactory> m_factory; + const raw_ptr<gpu::SharedImageRepresentationFactory> m_representationFactory; + const raw_ptr<viz::SkiaOutputSurfaceDependency> m_deps; +}; + +} // namespace QtWebEngineCore + +#endif // !NATIVE_SKIA_OUTPUT_DEVICE_H diff --git a/src/core/compositor/native_skia_output_device_direct3d11.cpp b/src/core/compositor/native_skia_output_device_direct3d11.cpp new file mode 100644 index 000000000..352fa9f92 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_direct3d11.cpp @@ -0,0 +1,88 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "native_skia_output_device_direct3d11.h" + +#include <QtCore/private/qsystemerror_p.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qsgtexture.h> + +#include <d3d11_1.h> + +namespace QtWebEngineCore { + +NativeSkiaOutputDeviceDirect3D11::NativeSkiaOutputDeviceDirect3D11( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) + : NativeSkiaOutputDevice(contextState, requiresAlpha, memoryTracker, dependency, + shared_image_factory, shared_image_representation_factory, + didSwapBufferCompleteCallback) +{ + SkColorType skColorType = kRGBA_8888_SkColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRX_8888)] = skColorType; +} + +NativeSkiaOutputDeviceDirect3D11::~NativeSkiaOutputDeviceDirect3D11() { } + +QSGTexture *NativeSkiaOutputDeviceDirect3D11::texture(QQuickWindow *win, uint32_t textureOptions) +{ + if (!m_frontBuffer || !m_readyWithTexture) + return nullptr; + + absl::optional<gl::DCLayerOverlayImage> overlayImage = m_frontBuffer->overlayImage(); + if (!overlayImage) { + qWarning("No overlay image."); + return nullptr; + } + + QSGRendererInterface *ri = win->rendererInterface(); + + HRESULT status = S_OK; + HANDLE sharedHandle = nullptr; + IDXGIResource1 *resource = nullptr; + if (!overlayImage->nv12_texture()) { + qWarning("No D3D texture."); + return nullptr; + } + status = overlayImage->nv12_texture()->QueryInterface(__uuidof(IDXGIResource1), + (void **)&resource); + Q_ASSERT(status == S_OK); + status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle); + Q_ASSERT(status == S_OK); + Q_ASSERT(sharedHandle); + + // Pass texture between two D3D devices: + ID3D11Device1 *device = static_cast<ID3D11Device1 *>( + ri->getResource(win, QSGRendererInterface::DeviceResource)); + + ID3D11Texture2D *qtTexture; + status = device->OpenSharedResource1(sharedHandle, __uuidof(ID3D11Texture2D), + (void **)&qtTexture); + if (status != S_OK) { + qWarning("Failed to share D3D11 texture (%s). This will result in failed rendering. Report " + "the bug, and try restarting with QTWEBENGINE_CHROMIUM_FLAGS=--disble-gpu", + qPrintable(QSystemError::windowsComString(status))); + ::CloseHandle(sharedHandle); + return nullptr; + } + + Q_ASSERT(qtTexture); + QQuickWindow::CreateTextureOptions texOpts(textureOptions); + QSGTexture *texture = + QNativeInterface::QSGD3D11Texture::fromNative(qtTexture, win, size(), texOpts); + + m_frontBuffer->textureCleanupCallback = [qtTexture, sharedHandle]() { + qtTexture->Release(); + ::CloseHandle(sharedHandle); + }; + + return texture; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/native_skia_output_device_direct3d11.h b/src/core/compositor/native_skia_output_device_direct3d11.h new file mode 100644 index 000000000..33cf1bcd6 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_direct3d11.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef NATIVE_SKIA_OUTPUT_DEVICE_DIRECT3D11_H +#define NATIVE_SKIA_OUTPUT_DEVICE_DIRECT3D11_H + +#include "native_skia_output_device.h" + +namespace QtWebEngineCore { + +class NativeSkiaOutputDeviceDirect3D11 final : public NativeSkiaOutputDevice +{ +public: + NativeSkiaOutputDeviceDirect3D11( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); + ~NativeSkiaOutputDeviceDirect3D11() override; + + // Overridden from Compositor: + QSGTexture *texture(QQuickWindow *win, uint32_t textureOptions) override; +}; + +} // namespace QtWebEngineCore + +#endif // NATIVE_SKIA_OUTPUT_DEVICE_DIRECT3D11_H diff --git a/src/core/compositor/native_skia_output_device_mac.mm b/src/core/compositor/native_skia_output_device_mac.mm new file mode 100644 index 000000000..bf21ef8d7 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_mac.mm @@ -0,0 +1,97 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// This is a workaround to be able to include Qt headers without +// "redefinition of 'NSString' as different kind of symbol" errors. +// TODO: Remove this when namespace ambiguity issues are fixed properly, +// see get_forward_declaration_macro() in cmake/Functions.cmake +#undef Q_FORWARD_DECLARE_OBJC_CLASS + +#import <AppKit/AppKit.h> +#import <IOSurface/IOSurface.h> +#import <Metal/Metal.h> + +#include <QtGui/qtguiglobal.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qsgrendererinterface.h> +#include <QtQuick/qsgtexture.h> + +#if QT_CONFIG(opengl) +#include <OpenGL/OpenGL.h> +#include <QtGui/qopenglcontext.h> +#include <QtGui/qopenglextrafunctions.h> +#include <QtOpenGL/qopengltextureblitter.h> +#include <QtOpenGL/qopenglframebufferobject.h> +#endif + +namespace QtWebEngineCore { + +QSGTexture *makeMetalTexture(QQuickWindow *win, IOSurfaceRef ioSurface, uint ioSurfacePlane, + const QSize &size, QQuickWindow::CreateTextureOptions texOpts) +{ + auto desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:size.width() + height:size.height() + mipmapped:false]; + + QSGRendererInterface *ri = win->rendererInterface(); + auto device = (__bridge id<MTLDevice>)(ri->getResource(win, QSGRendererInterface::DeviceResource)); + id<MTLTexture> texture = [device newTextureWithDescriptor:desc + iosurface:ioSurface + plane:ioSurfacePlane]; + return QNativeInterface::QSGMetalTexture::fromNative(texture, win, size, texOpts); +} + +void releaseMetalTexture(void *texture) +{ + [static_cast<id<MTLTexture>>(texture) release]; +} + +#if QT_CONFIG(opengl) +uint32_t makeCGLTexture(QQuickWindow *win, IOSurfaceRef ioSurface, const QSize &size) +{ + const int width = size.width(); + const int height = size.height(); + + auto glContext = QOpenGLContext::currentContext(); + auto glFun = glContext->extraFunctions(); + auto nscontext = glContext->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext(); + CGLContextObj cglContext = [nscontext CGLContextObj]; + + win->beginExternalCommands(); + // Bind the IO surface to a texture + GLuint glTexture; + glFun->glGenTextures(1, &glTexture); + glFun->glBindTexture(GL_TEXTURE_RECTANGLE_ARB, glTexture); + CGLTexImageIOSurface2D(cglContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, width, height, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, ioSurface, 0); + glFun->glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + glFun->glViewport(0, 0, width, height); + + // The bound IO surface is a weird dynamic bind, so take a snapshot of it to a normal texture + { + QOpenGLFramebufferObject fbo(width, height, GL_TEXTURE_2D); + auto success = fbo.bind(); + Q_ASSERT(success); + + QOpenGLTextureBlitter blitter; + success = blitter.create(); + Q_ASSERT(success); + glFun->glDisable(GL_BLEND); + glFun->glDisable(GL_SCISSOR_TEST); + blitter.bind(GL_TEXTURE_RECTANGLE_ARB); + blitter.blit(glTexture, {}, QOpenGLTextureBlitter::OriginBottomLeft); + blitter.release(); + blitter.destroy(); + + glFun->glDeleteTextures(1, &glTexture); + glTexture = fbo.takeTexture(); + fbo.release(); + } + win->endExternalCommands(); + + return glTexture; +} +#endif // QT_CONFIG(opengl) + +} // namespace diff --git a/src/core/compositor/native_skia_output_device_metal.cpp b/src/core/compositor/native_skia_output_device_metal.cpp new file mode 100644 index 000000000..a9d6e4fd5 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_metal.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "native_skia_output_device_metal.h" + +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qsgtexture.h> + +namespace QtWebEngineCore { + +NativeSkiaOutputDeviceMetal::NativeSkiaOutputDeviceMetal( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) + : NativeSkiaOutputDevice(contextState, requiresAlpha, memoryTracker, dependency, + shared_image_factory, shared_image_representation_factory, + didSwapBufferCompleteCallback) +{ + SkColorType skColorType = kRGBA_8888_SkColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRX_8888)] = skColorType; +} + +NativeSkiaOutputDeviceMetal::~NativeSkiaOutputDeviceMetal() { } + +QSGTexture *makeMetalTexture(QQuickWindow *win, IOSurfaceRef ioSurface, uint ioSurfacePlane, + const QSize &size, QQuickWindow::CreateTextureOptions texOpts); +void releaseMetalTexture(void *texture); + +QSGTexture *NativeSkiaOutputDeviceMetal::texture(QQuickWindow *win, uint32_t textureOptions) +{ + if (!m_frontBuffer || !m_readyWithTexture) + return nullptr; + + gfx::ScopedIOSurface ioSurface = m_frontBuffer->ioSurface(); + if (!ioSurface) { + qWarning("No IOSurface."); + return nullptr; + } + + // This is a workaround to not to release metal texture too early. + // In RHI, QMetalTexture wraps MTLTexture. QMetalTexture seems to be only destructed after the + // next MTLTexture is imported. The "old" MTLTexture can be still pontentially used by RHI + // while QMetalTexture is not destructed. Metal Validation Layer also warns about it. + // Delay releasing MTLTexture after the next one is presented. + if (m_currentMetalTexture) { + m_frontBuffer->textureCleanupCallback = [texture = m_currentMetalTexture]() { + releaseMetalTexture(texture); + }; + m_currentMetalTexture = nullptr; + } + + QQuickWindow::CreateTextureOptions texOpts(textureOptions); + QSGTexture *qsgTexture = makeMetalTexture(win, ioSurface.get(), /* plane */ 0, size(), texOpts); + + auto ni = qsgTexture->nativeInterface<QNativeInterface::QSGMetalTexture>(); + m_currentMetalTexture = ni->nativeTexture(); + + return qsgTexture; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/native_skia_output_device_metal.h b/src/core/compositor/native_skia_output_device_metal.h new file mode 100644 index 000000000..8e8d0fab8 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_metal.h @@ -0,0 +1,31 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef NATIVE_SKIA_OUTPUT_DEVICE_METAL_H +#define NATIVE_SKIA_OUTPUT_DEVICE_METAL_H + +#include "native_skia_output_device.h" + +namespace QtWebEngineCore { + +class NativeSkiaOutputDeviceMetal final : public NativeSkiaOutputDevice +{ +public: + NativeSkiaOutputDeviceMetal( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); + ~NativeSkiaOutputDeviceMetal() override; + + // Overridden from Compositor: + QSGTexture *texture(QQuickWindow *win, uint32_t textureOptions) override; + +private: + void *m_currentMetalTexture = nullptr; +}; + +} // namespace QtWebEngineCore + +#endif // NATIVE_SKIA_OUTPUT_DEVICE_METAL_H diff --git a/src/core/compositor/native_skia_output_device_opengl.cpp b/src/core/compositor/native_skia_output_device_opengl.cpp new file mode 100644 index 000000000..058573b9e --- /dev/null +++ b/src/core/compositor/native_skia_output_device_opengl.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "native_skia_output_device_opengl.h" + +#include <QtGui/qopenglcontext.h> +#include <QtGui/qopenglextrafunctions.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qsgtexture.h> + +namespace QtWebEngineCore { + +NativeSkiaOutputDeviceOpenGL::NativeSkiaOutputDeviceOpenGL( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) + : NativeSkiaOutputDevice(contextState, requiresAlpha, memoryTracker, dependency, + shared_image_factory, shared_image_representation_factory, + didSwapBufferCompleteCallback) +{ + SkColorType skColorType = kRGBA_8888_SkColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRX_8888)] = skColorType; +} + +NativeSkiaOutputDeviceOpenGL::~NativeSkiaOutputDeviceOpenGL() { } + +#if defined(Q_OS_MACOS) +uint32_t makeCGLTexture(QQuickWindow *win, IOSurfaceRef ioSurface, const QSize &size); +#endif + +QSGTexture *NativeSkiaOutputDeviceOpenGL::texture(QQuickWindow *win, uint32_t textureOptions) +{ + if (!m_frontBuffer || !m_readyWithTexture) + return nullptr; + +#if defined(USE_OZONE) + scoped_refptr<gfx::NativePixmap> nativePixmap = m_frontBuffer->nativePixmap(); + if (!nativePixmap) { + qWarning("No native pixmap."); + return nullptr; + } +#elif defined(Q_OS_WIN) + auto overlayImage = m_frontBuffer->overlayImage(); + if (!overlayImage) { + qWarning("No overlay image."); + return nullptr; + } +#elif defined(Q_OS_MACOS) + gfx::ScopedIOSurface ioSurface = m_frontBuffer->ioSurface(); + if (!ioSurface) { + qWarning("No IOSurface."); + return nullptr; + } +#endif + + QQuickWindow::CreateTextureOptions texOpts(textureOptions); + QSGTexture *texture = nullptr; + +#if defined(USE_OZONE) + // TODO(QTBUG-112281): Add ANGLE support to Linux. + QT_NOT_YET_IMPLEMENTED +#elif defined(Q_OS_WIN) + // TODO: Add WGL support over ANGLE. + QT_NOT_YET_IMPLEMENTED +#elif defined(Q_OS_MACOS) + uint32_t glTexture = makeCGLTexture(win, ioSurface.get(), size()); + texture = QNativeInterface::QSGOpenGLTexture::fromNative(glTexture, win, size(), texOpts); + + m_frontBuffer->textureCleanupCallback = [glTexture]() { + auto *glContext = QOpenGLContext::currentContext(); + if (!glContext) + return; + auto glFun = glContext->functions(); + glFun->glDeleteTextures(1, &glTexture); + }; +#endif + + return texture; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/native_skia_output_device_opengl.h b/src/core/compositor/native_skia_output_device_opengl.h new file mode 100644 index 000000000..233f51df9 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_opengl.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef NATIVE_SKIA_OUTPUT_DEVICE_OPENGL_H +#define NATIVE_SKIA_OUTPUT_DEVICE_OPENGL_H + +#include "native_skia_output_device.h" + +namespace QtWebEngineCore { + +class NativeSkiaOutputDeviceOpenGL final : public NativeSkiaOutputDevice +{ +public: + NativeSkiaOutputDeviceOpenGL( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); + ~NativeSkiaOutputDeviceOpenGL() override; + + // Overridden from Compositor: + QSGTexture *texture(QQuickWindow *win, uint32_t textureOptions) override; +}; + +} // namespace QtWebEngineCore + +#endif // NATIVE_SKIA_OUTPUT_DEVICE_OPENGL_H diff --git a/src/core/compositor/native_skia_output_device_vulkan.cpp b/src/core/compositor/native_skia_output_device_vulkan.cpp new file mode 100644 index 000000000..c2ad7a382 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_vulkan.cpp @@ -0,0 +1,306 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "native_skia_output_device_vulkan.h" + +#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h" + +#include <QtGui/qvulkaninstance.h> +#include <QtGui/qvulkanfunctions.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qsgtexture.h> + +#if defined(USE_OZONE) +#include "ui/ozone/buildflags.h" +#if BUILDFLAG(OZONE_PLATFORM_X11) +// We need to define USE_VULKAN_XCB for proper vulkan function pointers. +// Avoiding it may lead to call wrong vulkan functions. +// This is originally defined in chromium/gpu/vulkan/BUILD.gn. +#define USE_VULKAN_XCB +#endif // BUILDFLAG(OZONE_PLATFORM_X11) +#include "gpu/vulkan/vulkan_function_pointers.h" + +#include "components/viz/common/gpu/vulkan_context_provider.h" +#include "gpu/vulkan/vulkan_device_queue.h" +#endif // defined(USE_OZONE) + +namespace QtWebEngineCore { + +NativeSkiaOutputDeviceVulkan::NativeSkiaOutputDeviceVulkan( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) + : NativeSkiaOutputDevice(contextState, requiresAlpha, memoryTracker, dependency, + shared_image_factory, shared_image_representation_factory, + didSwapBufferCompleteCallback) +{ + SkColorType skColorType = kRGBA_8888_SkColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] = skColorType; + capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRX_8888)] = skColorType; +} + +NativeSkiaOutputDeviceVulkan::~NativeSkiaOutputDeviceVulkan() { } + +QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t textureOptions) +{ + if (!m_frontBuffer || !m_readyWithTexture) + return nullptr; + +#if defined(USE_OZONE) + Q_ASSERT(m_contextState->gr_context_type() == gpu::GrContextType::kVulkan); + + GrVkImageInfo vkImageInfo; + scoped_refptr<gfx::NativePixmap> nativePixmap = m_frontBuffer->nativePixmap(); + if (!nativePixmap) { + if (m_isNativeBufferSupported) { + qWarning("VULKAN: No NativePixmap."); + return nullptr; + } + + sk_sp<SkImage> skImage = m_frontBuffer->skImage(); + if (!skImage) { + qWarning("VULKAN: No SkImage."); + return nullptr; + } + + if (!skImage->isTextureBacked()) { + qWarning("VULKAN: SkImage is not backed by GPU texture."); + return nullptr; + } + + GrBackendTexture backendTexture; + bool success = SkImages::GetBackendTextureFromImage(skImage, &backendTexture, false); + if (!success || !backendTexture.isValid()) { + qWarning("VULKAN: Failed to retrieve backend texture from SkImage."); + return nullptr; + } + + if (backendTexture.backend() != GrBackendApi::kVulkan) { + qWarning("VULKAN: Backend texture is not a Vulkan texture."); + return nullptr; + } + + backendTexture.getVkImageInfo(&vkImageInfo); + if (vkImageInfo.fAlloc.fMemory == VK_NULL_HANDLE) { + qWarning("VULKAN: Unable to access Vulkan memory."); + return nullptr; + } + } +#elif defined(Q_OS_WIN) + Q_ASSERT(m_contextState->gr_context_type() == gpu::GrContextType::kGL); + + absl::optional<gl::DCLayerOverlayImage> overlayImage = m_frontBuffer->overlayImage(); + if (!overlayImage) { + qWarning("No overlay image."); + return nullptr; + } +#endif + + QSGRendererInterface *ri = win->rendererInterface(); + VkDevice qtVulkanDevice = + *static_cast<VkDevice *>(ri->getResource(win, QSGRendererInterface::DeviceResource)); + VkPhysicalDevice qtPhysicalDevice = *static_cast<VkPhysicalDevice *>( + ri->getResource(win, QSGRendererInterface::PhysicalDeviceResource)); + QVulkanFunctions *f = win->vulkanInstance()->functions(); + QVulkanDeviceFunctions *df = win->vulkanInstance()->deviceFunctions(qtVulkanDevice); + + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkPhysicalDeviceProperties deviceProperties; + f->vkGetPhysicalDeviceProperties(qtPhysicalDevice, &deviceProperties); + if (deviceProperties.vendorID == 0x10DE) { + // FIXME: This is a workaround for Nvidia driver. + // The imported image is empty if the initialLayout is not + // VK_IMAGE_LAYOUT_PREINITIALIZED. + imageLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + } + + VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = { + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR + }; +#if defined(USE_OZONE) + VkSubresourceLayout planeLayout = {}; + VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = { + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT + }; + base::ScopedFD scopedFd; + + if (nativePixmap) { + gfx::NativePixmapHandle nativePixmapHandle = nativePixmap->ExportHandle(); + if (nativePixmapHandle.planes.size() != 1) + qFatal("VULKAN: Multiple planes are not supported."); + + planeLayout.offset = nativePixmapHandle.planes[0].offset; + planeLayout.size = 0; + planeLayout.rowPitch = nativePixmapHandle.planes[0].stride; + planeLayout.arrayPitch = 0; + planeLayout.depthPitch = 0; + + modifierInfo.drmFormatModifier = nativePixmapHandle.modifier; + modifierInfo.drmFormatModifierPlaneCount = 1; + modifierInfo.pPlaneLayouts = &planeLayout; + + externalMemoryImageCreateInfo.pNext = &modifierInfo; + externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + scopedFd = std::move(nativePixmapHandle.planes[0].fd); + } else { + externalMemoryImageCreateInfo.pNext = nullptr; + externalMemoryImageCreateInfo.handleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + VkMemoryGetFdInfoKHR exportInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR }; + exportInfo.pNext = nullptr; + exportInfo.memory = vkImageInfo.fAlloc.fMemory; + exportInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + gpu::VulkanFunctionPointers *vfp = gpu::GetVulkanFunctionPointers(); + gpu::VulkanDeviceQueue *vulkanDeviceQueue = + m_contextState->vk_context_provider()->GetDeviceQueue(); + VkDevice vulkanDevice = vulkanDeviceQueue->GetVulkanDevice(); + + int fd = -1; + if (vfp->vkGetMemoryFdKHR(vulkanDevice, &exportInfo, &fd) != VK_SUCCESS) + qFatal("VULKAN: Unable to extract file descriptor out of external VkImage."); + + scopedFd.reset(fd); + } + + if (!scopedFd.is_valid()) + qFatal("VULKAN: Unable to extract file descriptor."); +#elif defined(Q_OS_WIN) + externalMemoryImageCreateInfo.pNext = nullptr; + externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT; + + HRESULT status = S_OK; + HANDLE sharedHandle = nullptr; + IDXGIResource1 *resource = nullptr; + if (!overlayImage->nv12_texture()) { + qWarning("VULKAN: No D3D texture."); + return nullptr; + } + status = overlayImage->nv12_texture()->QueryInterface(__uuidof(IDXGIResource1), + (void **)&resource); + Q_ASSERT(status == S_OK); + status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle); + Q_ASSERT(status == S_OK); + + if (!sharedHandle) + qFatal("VULKAN: Unable to extract shared handle."); +#endif + + constexpr VkImageUsageFlags kUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + VkImageCreateInfo importedImageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + importedImageCreateInfo.pNext = &externalMemoryImageCreateInfo; + importedImageCreateInfo.flags = 0; + importedImageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + importedImageCreateInfo.format = gpu::ToVkFormat(m_frontBuffer->sharedImageFormat()); + importedImageCreateInfo.extent.width = static_cast<uint32_t>(size().width()); + importedImageCreateInfo.extent.height = static_cast<uint32_t>(size().height()); + importedImageCreateInfo.extent.depth = 1; + importedImageCreateInfo.mipLevels = 1; + importedImageCreateInfo.arrayLayers = 1; + importedImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + importedImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + importedImageCreateInfo.usage = kUsage; + importedImageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + importedImageCreateInfo.queueFamilyIndexCount = 0; + importedImageCreateInfo.pQueueFamilyIndices = nullptr; + importedImageCreateInfo.initialLayout = imageLayout; + +#if defined(USE_OZONE) + if (nativePixmap) + importedImageCreateInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + else + importedImageCreateInfo.tiling = vkImageInfo.fImageTiling; +#endif + + VkResult result; + VkImage importedImage = VK_NULL_HANDLE; + result = df->vkCreateImage(qtVulkanDevice, &importedImageCreateInfo, nullptr /* pAllocator */, + &importedImage); + if (result != VK_SUCCESS) + qFatal() << "VULKAN: vkCreateImage failed result:" << result; + +#if defined(USE_OZONE) + VkImportMemoryFdInfoKHR importMemoryHandleInfo = { + VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR + }; + importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + importMemoryHandleInfo.fd = scopedFd.release(); + + if (nativePixmap) + importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; +#elif defined(Q_OS_WIN) + VkImportMemoryWin32HandleInfoKHR importMemoryHandleInfo = { + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR + }; + importMemoryHandleInfo.pNext = nullptr; + importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT; + importMemoryHandleInfo.handle = sharedHandle; +#endif + + VkMemoryDedicatedAllocateInfoKHR dedicatedMemoryInfo = { + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR + }; + dedicatedMemoryInfo.pNext = &importMemoryHandleInfo; + dedicatedMemoryInfo.image = importedImage; + + VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + memoryAllocateInfo.pNext = &dedicatedMemoryInfo; + + VkMemoryRequirements requirements; + df->vkGetImageMemoryRequirements(qtVulkanDevice, importedImage, &requirements); + if (!requirements.memoryTypeBits) + qFatal("VULKAN: vkGetImageMemoryRequirements failed."); + + VkPhysicalDeviceMemoryProperties memoryProperties; + f->vkGetPhysicalDeviceMemoryProperties(qtPhysicalDevice, &memoryProperties); + constexpr VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + constexpr uint32_t kMaxIndex = 31; + uint32_t memoryTypeIndex = kMaxIndex + 1; + for (uint32_t i = 0; i <= kMaxIndex; i++) { + if (((1u << i) & requirements.memoryTypeBits) == 0) + continue; + if ((memoryProperties.memoryTypes[i].propertyFlags & flags) != flags) + continue; + memoryTypeIndex = i; + break; + } + + if (memoryTypeIndex > kMaxIndex) + qFatal("VULKAN: Cannot find valid memory type index."); + + memoryAllocateInfo.allocationSize = requirements.size; + memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex; + + VkDeviceMemory importedImageMemory = VK_NULL_HANDLE; + result = df->vkAllocateMemory(qtVulkanDevice, &memoryAllocateInfo, nullptr /* pAllocator */, + &importedImageMemory); + if (result != VK_SUCCESS) + qFatal() << "VULKAN: vkAllocateMemory failed result:" << result; + + df->vkBindImageMemory(qtVulkanDevice, importedImage, importedImageMemory, 0); + + QQuickWindow::CreateTextureOptions texOpts(textureOptions); + QSGTexture *texture = QNativeInterface::QSGVulkanTexture::fromNative(importedImage, imageLayout, + win, size(), texOpts); + + m_frontBuffer->textureCleanupCallback = [=]() { + df->vkDestroyImage(qtVulkanDevice, importedImage, nullptr); + df->vkFreeMemory(qtVulkanDevice, importedImageMemory, nullptr); +#if defined(Q_OS_WIN) + ::CloseHandle(sharedHandle); +#endif + }; + + return texture; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/native_skia_output_device_vulkan.h b/src/core/compositor/native_skia_output_device_vulkan.h new file mode 100644 index 000000000..bead0cc11 --- /dev/null +++ b/src/core/compositor/native_skia_output_device_vulkan.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef NATIVE_SKIA_OUTPUT_DEVICE_VULKAN_H +#define NATIVE_SKIA_OUTPUT_DEVICE_VULKAN_H + +#include "native_skia_output_device.h" + +namespace QtWebEngineCore { + +class NativeSkiaOutputDeviceVulkan final : public NativeSkiaOutputDevice +{ +public: + NativeSkiaOutputDeviceVulkan( + scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha, + gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, + gpu::SharedImageFactory *shared_image_factory, + gpu::SharedImageRepresentationFactory *shared_image_representation_factory, + DidSwapBufferCompleteCallback didSwapBufferCompleteCallback); + ~NativeSkiaOutputDeviceVulkan() override; + + // Overridden from Compositor: + QSGTexture *texture(QQuickWindow *win, uint32_t textureOptions) override; +}; + +} // namespace QtWebEngineCore + +#endif // NATIVE_SKIA_OUTPUT_DEVICE_VULKAN_H diff --git a/src/core/compositor/vulkan_implementation_qt.cpp b/src/core/compositor/vulkan_implementation_qt.cpp new file mode 100644 index 000000000..2f2259666 --- /dev/null +++ b/src/core/compositor/vulkan_implementation_qt.cpp @@ -0,0 +1,162 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "vulkan_implementation_qt.h" + +#include "base/environment.h" +#include "base/logging.h" +#include "gpu/vulkan/vulkan_image.h" +#include "gpu/vulkan/vulkan_surface.h" +#include "gpu/vulkan/vulkan_util.h" +#include "ui/gfx/gpu_fence.h" + +#include <vulkan/vulkan.h> + +namespace gpu { + +VulkanImplementationQt::VulkanImplementationQt() : VulkanImplementation(false) { } + +VulkanImplementationQt::~VulkanImplementationQt() = default; + +bool VulkanImplementationQt::InitializeVulkanInstance(bool /*using_surface*/) +{ + std::vector<const char *> required_extensions = { + VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + }; + + auto env = base::Environment::Create(); + std::string vulkan_path; + if (!env->GetVar("QT_VULKAN_LIB", &vulkan_path)) { +#if BUILDFLAG(IS_WIN) + vulkan_path = "vulkan-1.dll"; +#else + vulkan_path = "libvulkan.so.1"; +#endif + } + + if (!vulkan_instance_.Initialize(base::FilePath::FromUTF8Unsafe(vulkan_path), + required_extensions, {})) { + LOG(ERROR) << "Failed to initialize vulkan instance"; + return false; + } + + return true; +} + +VulkanInstance *VulkanImplementationQt::GetVulkanInstance() +{ + return &vulkan_instance_; +} + +std::unique_ptr<VulkanSurface> +VulkanImplementationQt::CreateViewSurface(gfx::AcceleratedWidget /*window*/) +{ + NOTREACHED(); + return nullptr; +} + +bool VulkanImplementationQt::GetPhysicalDevicePresentationSupport( + VkPhysicalDevice /*device*/, + const std::vector<VkQueueFamilyProperties> & /*queue_family_properties*/, + uint32_t /*queue_family_index*/) +{ + NOTREACHED(); + return true; +} + +std::vector<const char *> VulkanImplementationQt::GetRequiredDeviceExtensions() +{ + return { + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, +#if BUILDFLAG(IS_WIN) + VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, +#else + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, +#endif + }; +} + +std::vector<const char *> VulkanImplementationQt::GetOptionalDeviceExtensions() +{ + return { + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, +#if BUILDFLAG(IS_WIN) + VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, +#else + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, +#endif + }; +} + +VkFence VulkanImplementationQt::CreateVkFenceForGpuFence(VkDevice /*vk_device*/) +{ + NOTREACHED(); + return VK_NULL_HANDLE; +} + +std::unique_ptr<gfx::GpuFence> +VulkanImplementationQt::ExportVkFenceToGpuFence(VkDevice /*vk_device*/, VkFence /*vk_fence*/) +{ + NOTREACHED(); + return nullptr; +} + +VkSemaphore VulkanImplementationQt::ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle sync_handle) +{ + return ImportVkSemaphoreHandle(vk_device, std::move(sync_handle)); +} + +VkExternalSemaphoreHandleTypeFlagBits VulkanImplementationQt::GetExternalSemaphoreHandleType() +{ +#if BUILDFLAG(IS_WIN) + return VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT; +#else + return VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; +#endif +} + +bool VulkanImplementationQt::CanImportGpuMemoryBuffer( + VulkanDeviceQueue *device_queue, + gfx::GpuMemoryBufferType memory_buffer_type) +{ +#if BUILDFLAG(IS_LINUX) + const auto &enabled_extensions = device_queue->enabled_extensions(); + return gfx::HasExtension(enabled_extensions, + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME) && + gfx::HasExtension(enabled_extensions, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME) && + memory_buffer_type == gfx::GpuMemoryBufferType::NATIVE_PIXMAP; +#else + return false; +#endif +} + +std::unique_ptr<VulkanImage> VulkanImplementationQt::CreateImageFromGpuMemoryHandle(VulkanDeviceQueue *device_queue, + gfx::GpuMemoryBufferHandle gmb_handle, + gfx::Size size, + VkFormat vk_format, + const gfx::ColorSpace &) +{ +#if BUILDFLAG(IS_LINUX) + constexpr auto kUsage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + auto tiling = gmb_handle.native_pixmap_handle.modifier == + gfx::NativePixmapHandle::kNoModifier + ? VK_IMAGE_TILING_OPTIMAL + : VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + return gpu::VulkanImage::CreateFromGpuMemoryBufferHandle( + device_queue, std::move(gmb_handle), size, vk_format, kUsage, /*flags=*/0, + tiling, VK_QUEUE_FAMILY_EXTERNAL); +#else + NOTIMPLEMENTED(); + return nullptr; +#endif +} + +} // namespace gpu diff --git a/src/core/compositor/vulkan_implementation_qt.h b/src/core/compositor/vulkan_implementation_qt.h new file mode 100644 index 000000000..88983331f --- /dev/null +++ b/src/core/compositor/vulkan_implementation_qt.h @@ -0,0 +1,46 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef VULKAN_IMPLEMENTATION_QT_H +#define VULKAN_IMPLEMENTATION_QT_H + +#include "gpu/vulkan/vulkan_implementation.h" +#include "gpu/vulkan/vulkan_instance.h" + +namespace gpu { + +class VulkanImplementationQt : public VulkanImplementation +{ +public: + VulkanImplementationQt(); + ~VulkanImplementationQt() override; + + // Overridden from VulkanImplementation. + bool InitializeVulkanInstance(bool using_surface) override; + VulkanInstance *GetVulkanInstance() override; + std::unique_ptr<VulkanSurface> CreateViewSurface(gfx::AcceleratedWidget window) override; + bool GetPhysicalDevicePresentationSupport( + VkPhysicalDevice device, + const std::vector<VkQueueFamilyProperties> &queue_family_properties, + uint32_t queue_family_index) override; + std::vector<const char *> GetRequiredDeviceExtensions() override; + std::vector<const char *> GetOptionalDeviceExtensions() override; + VkFence CreateVkFenceForGpuFence(VkDevice vk_device) override; + std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence(VkDevice vk_device, + VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, SemaphoreHandle handle) override; + VkExternalSemaphoreHandleTypeFlagBits GetExternalSemaphoreHandleType() override; + bool CanImportGpuMemoryBuffer(VulkanDeviceQueue* device_queue, + gfx::GpuMemoryBufferType memory_buffer_type) override; + std::unique_ptr<VulkanImage> CreateImageFromGpuMemoryHandle(VulkanDeviceQueue *device_queue, + gfx::GpuMemoryBufferHandle gmb_handle, + gfx::Size size, VkFormat vk_format, + const gfx::ColorSpace &color_space) override; + +private: + VulkanInstance vulkan_instance_; +}; + +} // namespace gpu + +#endif // VULKAN_IMPLEMENTATION_QT_H |