diff --git a/www/qt6-webengine/Makefile b/www/qt6-webengine/Makefile index 54707931d1dd..2fcf0c082b57 100644 --- a/www/qt6-webengine/Makefile +++ b/www/qt6-webengine/Makefile @@ -1,195 +1,195 @@ # QtWebEngine itself is a very thin layer of Qt code on top of a large part of # Chromium (everything up to the content/ layer). As such, most of the work in # this port revolves around taming Chromium and getting it to build on FreeBSD. # While it does build at the moment, there are several items that should be # investigated or improved: # - We are using several stub files, especially in Chromium's base/ and net/ # layers. We should look at implementing the missing bits instead. # - We are currently not using any sandboxing mechanism. # - The process of porting QtWebEngine needs to be documented so we can move to # newer releases more easily. # PORTNAME?= webengine DISTVERSION= ${QT6_VERSION} # This is the parent port of print/qt6-pdf. Please always keep 'PORTREVISION?=' # and reset the value to '0' only after increasing QT6_VERSION in Mk/Uses/qt.mk. -PORTREVISION?= 1 +PORTREVISION?= 2 CATEGORIES?= www PKGNAMEPREFIX= qt6- MAINTAINER= kde@FreeBSD.org COMMENT?= Qt 6 library to render web content # 32-bit platforms are not supported by upstream, but we're limping them along. ONLY_FOR_ARCHS= aarch64 amd64 armv7 i386 BUILD_DEPENDS= ${LOCALBASE}/include/vulkan/vulkan.h:graphics/vulkan-headers LIB_DEPENDS= libfreetype.so:print/freetype2 \ libnss3.so:security/nss \ libopenjp2.so:graphics/openjpeg \ libxkbcommon.so:x11/libxkbcommon USES= cmake compiler:c++20-lang gl localbase:ldflags ninja:build \ pkgconfig python:build qt-dist:6,webengine USE_GL= opengl USE_QT= base declarative tools USE_XORG= x11 xcb xcomposite xcursor xdamage xext xfixes xi xkbfile \ xrandr xrender xscrnsaver xshmfence xtst CMAKE_ON= QT_FEATURE_webengine_system_ffmpeg \ QT_FEATURE_webengine_proprietary_codecs USE_LDCONFIG= ${PREFIX}/${QT_LIBDIR_REL} .if defined(BUILD_QTPDF) CMAKE_ON+= QT_FEATURE_qtpdf_build CMAKE_OFF+= QT_FEATURE_qtwebengine_build SYS_LIBS= freetype .else BUILD_DEPENDS+= ${LOCALBASE}/include/linux/videodev2.h:multimedia/v4l_compat \ ${PYTHON_PKGNAMEPREFIX}html5lib>0:www/py-html5lib@${PY_FLAVOR} LIB_DEPENDS+= libabsl_base.so:devel/abseil \ libavcodec.so:multimedia/ffmpeg \ libdbus-1.so:devel/dbus \ libdrm.so:graphics/libdrm \ libepoll-shim.so:devel/libepoll-shim \ libexpat.so:textproc/expat2 \ libfontconfig.so:x11-fonts/fontconfig \ libharfbuzz.so:print/harfbuzz \ liblcms2.so:graphics/lcms2 \ libnspr4.so:devel/nspr \ libopenh264.so:multimedia/openh264 \ libopus.so:audio/opus \ libpci.so:devel/libpci \ libpng.so:graphics/png \ libre2.so:devel/re2 \ libsnappy.so:archivers/snappy \ libtiff.so:graphics/tiff \ libudev.so:devel/libudev-devd \ libvpx.so:multimedia/libvpx \ libwebp.so:graphics/webp USES+= bison gnome gperf jpeg minizip xorg USE_GL+= gbm USE_QT+= positioning quick3d:build webchannel USE_GNOME+= glib20 libxml2 libxslt USE_XORG+= x11 xcb xcomposite xcursor xdamage xext xfixes xi xkbfile \ xrandr xrender xscrnsaver xshmfence xtst CMAKE_ON+= QT_FEATURE_qtwebengine_build CMAKE_OFF+= QT_FEATURE_qtpdf_build CXXFLAGS+= -I${LOCALBASE}/include/libepoll-shim SYS_LIBS= freetype harfbuzz-ng libdrm libpng libxml libxslt openh264 opus .endif # The build system reads the environment variable $NINJA_PATH to decide whether # to boostrap ninja or not (and also to invoke it afterwards). CC and CXX are # read by some Chromium code to determine which compiler to invoke when running # some configuration tests. CONFIGURE_ENV+= NINJAFLAGS="-j${MAKE_JOBS_NUMBER}" \ NINJA_PATH="${LOCALBASE}/bin/ninja" \ PATH=${CONFIGURE_WRKSRC}/bin:${LOCALBASE}/bin:${PATH} MAKE_ENV+= CC="${CC}" CXX="${CXX}" \ C_INCLUDE_PATH=${LOCALBASE}/include \ CPLUS_INCLUDE_PATH=${LOCALBASE}/include \ ${CONFIGURE_ENV} # Avoid running multiple make(1) jobs, but only those. Otherwise the build # fails intermittently due race conditions if multiple ninja instances are # running at the same time (mostly for the targets "WebEngineCore" and # "convert_dict"). # # MAKE_JOBS_UNSAFE is too invasive because it also affects the number of jobs # for ninja(1) and would slow everything down which we don't want. We pass the # real number of make jobs via MAKE_JOBS_NUMBER to ninja(1) to CONFIGURE_ENV. DO_MAKE_BUILD= ${SETENV} ${MAKE_ENV} ${MAKE_CMD} ${MAKE_FLAGS} ${MAKEFILE} -j1 ${MAKE_ARGS:N${DESTDIRNAME}=*} BINARY_ALIAS= python3=${PYTHON_CMD} .if !defined(BUILD_QTPDF) OPTIONS_DEFINE= DRIVER PIPEWIRE OPTIONS_DEFAULT= ALSA DRIVER PIPEWIRE OPTIONS_SINGLE= AUDIO OPTIONS_SINGLE_AUDIO= ALSA PULSEAUDIO SNDIO OPTIONS_SUB= yes AUDIO_DESC= Audio backend # Need the alsa plugins to get sound at runtime, otherwise messages # that the pcm_oss plugin can't be opened. ALSA_LIB_DEPENDS= libasound.so:audio/alsa-lib ALSA_RUN_DEPENDS= alsa-plugins>=0:audio/alsa-plugins ALSA_CMAKE_ON= -DQT_FEATURE_webengine_system_alsa:BOOL=ON ALSA_CMAKE_OFF= -DQT_FEATURE_webengine_system_alsa:BOOL=OFF DRIVER_DESC= Install WebEngineDriver DRIVER_CMAKE_ON= -DQT_FEATURE_webenginedriver:BOOL=ON DRIVER_CMAKE_OFF= -DQT_FEATURE_webenginedriver:BOOL=OFF PIPEWIRE_LIB_DEPENDS= libpipewire-0.3.so:multimedia/pipewire PIPEWIRE_CMAKE_ON= -DQT_FEATURE_webengine_webrtc_pipewire:BOOL=ON PIPEWIRE_CMAKE_OFF= -DQT_FEATURE_webengine_webrtc_pipewire:BOOL=OFF PULSEAUDIO_LIB_DEPENDS= libpulse.so:audio/pulseaudio PULSEAUDIO_CMAKE_ON= -DQT_FEATURE_webengine_system_pulseaudio:BOOL=ON PULSEAUDIO_CMAKE_OFF= -DQT_FEATURE_webengine_system_pulseaudio:BOOL=OFF SNDIO_LIB_DEPENDS= libsndio.so:audio/sndio SNDIO_CMAKE_ON= -DQT_FEATURE_webengine_system_sndio:BOOL=ON SNDIO_CMAKE_OFF= -DQT_FEATURE_webengine_system_sndio:BOOL=OFF .endif .include .if !defined(BUILD_QTPDF) . if ${ARCH:Mi386} USES+= nodejs:build,20 . else USES+= nodejs:build,lts . endif .endif post-extract: @${MKDIR} ${WRKSRC}/src/3rdparty/chromium/media/audio/sndio \ ${WRKSRC}/src/3rdparty/chromium/sandbox/policy/freebsd \ ${WRKSRC}/src/3rdparty/chromium/sandbox/policy/openbsd (cd ${WRKSRC}/src/3rdparty/chromium/third_party/libdrm/src/include && ${CP} drm/drm.h .) post-patch: @${REINPLACE_CMD} -e 's|%%CPPFLAGS%%|${CPPFLAGS}|;s|%%CXXFLAGS%%|${CXXFLAGS}|;s|%%LDFLAGS%%|${LDFLAGS}|' \ ${WRKSRC}/src/host/BUILD.toolchain.gn.in @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|' \ ${WRKSRC}/src/3rdparty/chromium/third_party/pdfium/core/fxge/linux/fx_linux_impl.cpp \ ${WRKSRC}/src/3rdparty/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc pre-configure: # We used to remove bundled libraries to be sure that webengine uses # system libraries and not shipped ones. # # Leads to missing header errors: icu, libvpx, libwebp, re2, snappy, zlib # No-Op: flac # # Don't attempt to unbundle libraries that the Pdf module doesn't use. It # causes configuration errors. # # cd ${WRKSRC} && ${PYTHON_CMD} \ # ./build/linux/unbundle/remove_bundled_libraries.py [list of preserved] cd ${WRKSRC}/src/3rdparty/chromium && ${SETENV} ${CONFIGURE_ENV} ${PYTHON_CMD} \ ./build/linux/unbundle/replace_gn_files.py --system-libraries \ ${SYS_LIBS} || ${FALSE} .if !defined(BUILD_QTPDF) post-install: # Fix for deskutils/calibre, perhaps others, where this empty directory # is created during build causing a fs-violation. ${MKDIR} ${STAGEDIR}${QT_DATADIR}/resources/locales post-install-DRIVER-on: ${STRIP_CMD} ${STAGEDIR}${QT_TOOLDIR}/webenginedriver .endif .include diff --git a/www/qt6-webengine/files/patch-QTBUG-139335 b/www/qt6-webengine/files/patch-QTBUG-139335 new file mode 100644 index 000000000000..7308d72b013f --- /dev/null +++ b/www/qt6-webengine/files/patch-QTBUG-139335 @@ -0,0 +1,610 @@ +From 4b2f03af22bfb19168e71092d075c649e942604f Mon Sep 17 00:00:00 2001 +From: Peter Varga +Date: Mon, 20 Oct 2025 10:43:49 +0200 +Subject: [PATCH] Move GPU info logging to the GPU thread + +Accessing certain GPU information (eg. GPU feature status values) from +the browser thread requires extra API on top of Chromium, such as +content::GpuChildThread::gpu_channel_manager(). + +This change moves the logging to the GPU thread, allowing use of +existing APIs like content::GpuDataManager::GetFeatureStatus(). + +Task-number: QTBUG-139335 +Fixes: QTBUG-142497 +Fixes: QTBUG-142720 +Change-Id: I4e4471a78258a1502ec0e11683cae3936e170ce1 +Reviewed-by: Moss Heim +(cherry picked from commit 0b65b0754d1534280acc3fe48be61127ce24ac93) +Reviewed-by: Allan Sandfeld Jensen +--- + +diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt +index 64ef09f..eb83a82 100644 +--- src/core/CMakeLists.txt ++++ src/core/CMakeLists.txt +@@ -132,6 +132,7 @@ + file_system_access/file_system_access_permission_request_manager_qt.cpp file_system_access/file_system_access_permission_request_manager_qt.h + find_text_helper.cpp find_text_helper.h + global_descriptors_qt.h ++ gpu/content_gpu_client_qt.cpp gpu/content_gpu_client_qt.h + javascript_dialog_controller.cpp javascript_dialog_controller.h javascript_dialog_controller_p.h + javascript_dialog_manager_qt.cpp javascript_dialog_manager_qt.h + login_delegate_qt.cpp login_delegate_qt.h +diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp +index 4d81e39..ed63980 100644 +--- src/core/content_client_qt.cpp ++++ src/core/content_client_qt.cpp +@@ -4,8 +4,6 @@ + + #include "content_client_qt.h" + +-#include "compositor/compositor.h" +- + #include "base/command_line.h" + #include "base/files/file_util.h" + #include "base/json/json_string_value_serializer.h" +@@ -13,15 +11,11 @@ + #include "base/strings/string_util.h" + #include "base/values.h" + #include "base/version.h" +-#include "content/gpu/gpu_child_thread.h" + #include "content/public/common/cdm_info.h" + #include "content/public/common/content_constants.h" + #include "content/public/common/content_switches.h" + #include "extensions/buildflags/buildflags.h" + #include "extensions/common/constants.h" +-#include "gpu/config/gpu_feature_info.h" +-#include "gpu/config/gpu_preferences.h" +-#include "gpu/ipc/service/gpu_channel_manager.h" + #include "media/base/media_switches.h" + #include "media/base/video_codecs.h" + #include "media/cdm/supported_audio_codecs.h" +@@ -34,10 +28,8 @@ + #include + #include + #include +-#include + #include + #include +-#include + + #if BUILDFLAG(IS_WIN) + #include "ui/gl/gl_utils.h" +@@ -497,165 +489,4 @@ + return origin_trial_policy_.get(); + } + +-void ContentClientQt::SetGpuInfo(const gpu::GPUInfo &gpu_info) +-{ +- base::CommandLine *commandLine = base::CommandLine::ForCurrentProcess(); +- const bool isBrowserProcess = !commandLine->HasSwitch(switches::kProcessType); +- const bool isMainThread = QThread::currentThread() == qApp->thread(); +- +- // Limit this to the main thread of the browser process for now. +- if (!isBrowserProcess || !isMainThread) +- return; +- +- if (!gpu_info.IsInitialized()) { +- // This is probably not an issue but suspicious. +- qWarning("Failed to initialize GPUInfo."); +- return; +- } +- +- const gpu::GPUInfo::GPUDevice &primary = gpu_info.gpu; +- +- // Do not print the info again if the device hasn't been changed. +- // Change of the device is unexpected: we don't support or implement fallback yet. +- // It is suspicious if the info is logged twice. +- if (m_gpuInfo && m_gpuInfo->gpu.device_string == primary.device_string) +- return; +- m_gpuInfo = gpu_info; +- +- auto *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); +- const gpu::GpuFeatureStatus gpuCompositingStatus = +- gpuChannelManager->gpu_feature_info() +- .status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_GL]; +- +-#if BUILDFLAG(IS_OZONE) +- if (gpuCompositingStatus == gpu::kGpuFeatureStatusEnabled) { +- // See entry 3 in //gpu/config/software_rendering_list.json +- QRegularExpression filter(u"software|llvmpipe|softpipe"_s, +- QRegularExpression::CaseInsensitiveOption); +- if (filter.match(QLatin1StringView(gpu_info.gl_renderer)).hasMatch()) { +- qWarning("Hardware rendering is enabled but it is not supported with Mesa software " +- "rasterizer. Expect troubles."); +- +- if (gpuChannelManager->gpu_preferences().ignore_gpu_blocklist) +- qWarning("Rendering may fail because --ignore-gpu-blocklist is set."); +- } +- } +-#endif +- +- if (Q_LIKELY(!lcWebEngineCompositor().isDebugEnabled())) +- return; +- +- auto deviceToString = [](const gpu::GPUInfo::GPUDevice &device) -> QString { +- if (device.vendor_id == 0x0) +- return "Disabled"_L1; +- +- QString log; +- +- // TODO: Factor vendor translation out from QtWebEngineCore::GPUInfo. +- // Only name the most common desktop GPU hardware vendors for now. +- switch (device.vendor_id) { +- case 0x1002: +- log += "AMD"_L1; +- break; +- case 0x10DE: +- log += "Nvidia"_L1; +- break; +- case 0x8086: +- log += "Intel"_L1; +- break; +- default: +- log += "vendor id: 0x"_L1 + QString::number(device.vendor_id, 16); +- } +- +- log += ", device id: 0x"_L1 + QString::number(device.device_id, 16); +- +- if (!device.driver_vendor.empty()) { +- log += ", driver: "_L1 + QLatin1StringView(device.driver_vendor) + u' ' +- + QLatin1StringView(device.driver_version); +- } +- log += ", system device id: 0x"_L1 + QString::number(device.system_device_id, 16); +- +- log += ", preference: "_L1; +- switch (device.gpu_preference) { +- case gl::GpuPreference::kNone: +- log += "None"_L1; +- break; +- case gl::GpuPreference::kDefault: +- log += "Default"_L1; +- break; +- case gl::GpuPreference::kLowPower: +- log += "LowPower"_L1; +- break; +- case gl::GpuPreference::kHighPerformance: +- log += "HighPerformance"_L1; +- break; +- } +- +- log += ", active: "_L1 + (device.active ? "yes"_L1 : "no"_L1); +- return log; +- }; +- +- QString log; +- +- log = "GPU Compositing: "; +- switch (gpuCompositingStatus) { +- case gpu::kGpuFeatureStatusEnabled: +- log += "Enabled"_L1; +- break; +- case gpu::kGpuFeatureStatusBlocklisted: +- log += "Blocklisted"_L1; +- break; +- case gpu::kGpuFeatureStatusDisabled: +- log += "Disabled"_L1; +- break; +- case gpu::kGpuFeatureStatusSoftware: +- log += "Software"_L1; +- break; +- case gpu::kGpuFeatureStatusUndefined: +- log += "Undefined"_L1; +- break; +- case gpu::kGpuFeatureStatusMax: +- log += "Max"_L1; +- break; +- } +- qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(log)); +- +- if (gpu_info.gl_vendor.empty() || gpu_info.gl_vendor == "Disabled") { +- log = "ANGLE is disabled:\n"_L1; +- log += " GL Renderer: "_L1 + QLatin1StringView(gpu_info.gl_renderer) + u'\n'; +- log += " Software Renderer: "_L1 + (primary.IsSoftwareRenderer() ? "yes"_L1 : "no"_L1) +- + u'\n'; +- log += " Primary GPU: "_L1 + deviceToString(primary) + u'\n'; +- } else { +- log = QLatin1StringView(gpu_info.display_type) + " display is initialized:\n"_L1; +- log += " GL Renderer: "_L1 + QLatin1StringView(gpu_info.gl_renderer) + u'\n'; +- log += " "_L1 + QString::number(gpu_info.GpuCount()) + " GPU(s) detected:\n"_L1; +- log += " "_L1 + deviceToString(primary) + u'\n'; +- for (auto &secondary : gpu_info.secondary_gpus) +- log += " "_L1 + deviceToString(secondary) + u'\n'; +- +- log += " NVIDIA Optimus: "_L1 + (gpu_info.optimus ? "enabled"_L1 : "disabled"_L1) + u'\n'; +- log += " AMD Switchable: "_L1 + (gpu_info.amd_switchable ? "enabled"_L1 : "disabled"_L1); +- } +- +- qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(log)); +- +-#if BUILDFLAG(IS_WIN) +- log = "Windows specific driver information:\n"_L1; +- +- log += " Direct Composition: "_L1; +- if (gpu_info.overlay_info.direct_composition) +- log += "enabled\n"_L1; +- else if (gl::GetGlWorkarounds().disable_direct_composition) +- log += "disabled by workaround\n"_L1; +- else +- log += "disabled\n"_L1; +- +- log += " Supports Overlays: "_L1 +- + (gpu_info.overlay_info.supports_overlays ? "yes"_L1 : "no"_L1) + u'\n'; +- log += " Supports D3D Shared Images: "_L1 + (gpu_info.shared_image_d3d ? "yes"_L1 : "no"_L1); +- qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(log)); +-#endif +-} +- + } // namespace QtWebEngineCore +diff --git a/src/core/content_client_qt.h b/src/core/content_client_qt.h +index 2936c23..d5f9e71 100644 +--- src/core/content_client_qt.h ++++ src/core/content_client_qt.h +@@ -10,11 +10,9 @@ + #include "base/synchronization/lock.h" + #include "components/embedder_support/origin_trials/origin_trial_policy_impl.h" + #include "content/public/common/content_client.h" +-#include "gpu/config/gpu_info.h" + #include "ui/base/layout.h" + + #include +-#include + + namespace QtWebEngineCore { + +@@ -32,13 +30,11 @@ + gfx::Image &GetNativeImageNamed(int resource_id) override; + std::u16string GetLocalizedString(int message_id) override; + blink::OriginTrialPolicy *GetOriginTrialPolicy() override; +- void SetGpuInfo(const gpu::GPUInfo &gpu_info) override; + + private: + // Used to lock when |origin_trial_policy_| is initialized. + base::Lock origin_trial_policy_lock_; + std::unique_ptr origin_trial_policy_; +- std::optional m_gpuInfo; + }; + + } // namespace QtWebEngineCore +diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp +index 6c61c7a..1512f9a 100644 +--- src/core/content_main_delegate_qt.cpp ++++ src/core/content_main_delegate_qt.cpp +@@ -194,6 +194,12 @@ + return m_browserClient.get(); + } + ++content::ContentGpuClient *ContentMainDelegateQt::CreateContentGpuClient() ++{ ++ m_gpuClient.reset(new ContentGpuClientQt); ++ return m_gpuClient.get(); ++} ++ + content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient() + { + #if BUILDFLAG(IS_LINUX) +diff --git a/src/core/content_main_delegate_qt.h b/src/core/content_main_delegate_qt.h +index cb320ec..b725d88 100644 +--- src/core/content_main_delegate_qt.h ++++ src/core/content_main_delegate_qt.h +@@ -10,6 +10,7 @@ + #include "content_browser_client_qt.h" + #include "content_client_qt.h" + #include "content_utility_client_qt.h" ++#include "gpu/content_gpu_client_qt.h" + + namespace QtWebEngineCore { + +@@ -23,6 +24,7 @@ + + content::ContentClient *CreateContentClient() override; + content::ContentBrowserClient* CreateContentBrowserClient() override; ++ content::ContentGpuClient* CreateContentGpuClient() override; + content::ContentRendererClient* CreateContentRendererClient() override; + content::ContentUtilityClient* CreateContentUtilityClient() override; + std::optional BasicStartupComplete() override; +@@ -30,6 +32,7 @@ + private: + ContentClientQt m_contentClient; + std::unique_ptr m_browserClient; ++ std::unique_ptr m_gpuClient; + std::unique_ptr m_utilityClient; + }; + +diff --git a/src/core/gpu/content_gpu_client_qt.cpp b/src/core/gpu/content_gpu_client_qt.cpp +new file mode 100644 +index 0000000..3b0b3e9 +--- /dev/null ++++ src/core/gpu/content_gpu_client_qt.cpp +@@ -0,0 +1,238 @@ ++// Copyright (C) 2025 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 ++// Qt-Security score:significant reason:default ++ ++#include "content_gpu_client_qt.h" ++ ++#include "compositor/compositor.h" ++ ++#include "content/public/browser/browser_thread.h" ++#include "content/public/browser/gpu_data_manager.h" ++#include "content/public/browser/gpu_data_manager_observer.h" ++#include "gpu/config/gpu_driver_bug_workarounds.h" ++#include "gpu/config/gpu_info.h" ++#include "mojo/public/cpp/bindings/binder_map.h" ++ ++#include ++#include ++ ++#include ++#include ++ ++#if BUILDFLAG(IS_WIN) ++#include "ui/gl/gl_utils.h" ++#endif ++ ++using namespace Qt::StringLiterals; ++ ++namespace QtWebEngineCore { ++ ++namespace { ++static inline bool isSameDevice(const gpu::GPUInfo::GPUDevice &d1, ++ const gpu::GPUInfo::GPUDevice &d2) ++{ ++ return std::tie(d1.vendor_id, d1.device_id, d1.system_device_id, d1.vendor_string, ++ d1.device_string, d1.driver_vendor, d1.driver_version) ++ == std::tie(d2.vendor_id, d2.device_id, d2.system_device_id, d2.vendor_string, ++ d2.device_string, d2.driver_vendor, d2.driver_version); ++} ++ ++static QString gpuDeviceToString(const gpu::GPUInfo::GPUDevice &device) ++{ ++ if (device.vendor_id == 0x0) ++ return "Disabled"_L1; ++ ++ QString deviceString; ++ ++ // TODO: Factor vendor translation out from QtWebEngineCore::GPUInfo. ++ // Only name the most common desktop GPU hardware vendors for now. ++ switch (device.vendor_id) { ++ case 0x1002: ++ deviceString += "AMD"_L1; ++ break; ++ case 0x10DE: ++ deviceString += "Nvidia"_L1; ++ break; ++ case 0x8086: ++ deviceString += "Intel"_L1; ++ break; ++ default: ++ deviceString += "vendor id: 0x"_L1 + QString::number(device.vendor_id, 16); ++ } ++ ++ deviceString += ", device id: 0x"_L1 + QString::number(device.device_id, 16); ++ ++ if (!device.driver_vendor.empty()) { ++ deviceString += ", driver: "_L1 + QLatin1StringView(device.driver_vendor) + u' ' ++ + QLatin1StringView(device.driver_version); ++ } ++ deviceString += ", system device id: 0x"_L1 + QString::number(device.system_device_id, 16); ++ ++ deviceString += ", preference: "_L1; ++ switch (device.gpu_preference) { ++ case gl::GpuPreference::kNone: ++ deviceString += "None"_L1; ++ break; ++ case gl::GpuPreference::kDefault: ++ deviceString += "Default"_L1; ++ break; ++ case gl::GpuPreference::kLowPower: ++ deviceString += "LowPower"_L1; ++ break; ++ case gl::GpuPreference::kHighPerformance: ++ deviceString += "HighPerformance"_L1; ++ break; ++ } ++ ++ deviceString += ", active: "_L1 + (device.active ? "yes"_L1 : "no"_L1); ++ return deviceString; ++} ++ ++static inline const char *gpuFeatureStatusToString(const gpu::GpuFeatureStatus &status) ++{ ++ switch (status) { ++ case gpu::kGpuFeatureStatusEnabled: ++ return "Enabled"; ++ case gpu::kGpuFeatureStatusBlocklisted: ++ return "Blocklisted"; ++ case gpu::kGpuFeatureStatusDisabled: ++ return "Disabled"; ++ case gpu::kGpuFeatureStatusSoftware: ++ return "Software"; ++ case gpu::kGpuFeatureStatusUndefined: ++ return "Undefined"; ++ case gpu::kGpuFeatureStatusMax: ++ return "Max"; ++ } ++} ++ ++static QString angleInfo(const gpu::GPUInfo &gpuInfo) ++{ ++ QString info; ++ ++ if (gpuInfo.gl_vendor.empty() || gpuInfo.gl_vendor == "Disabled") { ++ info = "ANGLE is disabled:\n"_L1; ++ info += " GL Renderer: "_L1 + QLatin1StringView(gpuInfo.gl_renderer) + u'\n'; ++ info += " Software Renderer: "_L1 + (gpuInfo.gpu.IsSoftwareRenderer() ? "yes"_L1 : "no"_L1) ++ + u'\n'; ++ info += " Primary GPU: "_L1 + gpuDeviceToString(gpuInfo.gpu) + u'\n'; ++ return info; ++ } ++ ++ info = QLatin1StringView(gpuInfo.display_type) + " display is initialized:\n"_L1; ++ info += " GL Renderer: "_L1 + QLatin1StringView(gpuInfo.gl_renderer) + u'\n'; ++ info += " "_L1 + QString::number(gpuInfo.GpuCount()) + " GPU(s) detected:\n"_L1; ++ info += " "_L1 + gpuDeviceToString(gpuInfo.gpu) + u'\n'; ++ for (auto &secondary : gpuInfo.secondary_gpus) ++ info += " "_L1 + gpuDeviceToString(secondary) + u'\n'; ++ ++ info += " NVIDIA Optimus: "_L1 + (gpuInfo.optimus ? "enabled"_L1 : "disabled"_L1) + u'\n'; ++ info += " AMD Switchable: "_L1 + (gpuInfo.amd_switchable ? "enabled"_L1 : "disabled"_L1); ++ ++ return info; ++} ++ ++#if BUILDFLAG(IS_WIN) ++static QString windowsInfo(const gpu::GPUInfo &gpuInfo) ++{ ++ QString info; ++ info = "Windows specific driver information:\n"_L1; ++ ++ info += " Direct Composition: "_L1; ++ if (gpuInfo.overlay_info.direct_composition) ++ info += "enabled\n"_L1; ++ else if (gl::GetGlWorkarounds().disable_direct_composition) ++ info += "disabled by workaround\n"_L1; ++ else ++ info += "disabled\n"_L1; ++ ++ info += " Supports Overlays: "_L1 ++ + (gpuInfo.overlay_info.supports_overlays ? "yes"_L1 : "no"_L1) + u'\n'; ++ info += " Supports D3D Shared Images: "_L1 + (gpuInfo.shared_image_d3d ? "yes"_L1 : "no"_L1); ++ return info; ++} ++#endif ++} // namespace ++ ++class GpuObserver : public content::GpuDataManagerObserver ++{ ++public: ++ GpuObserver(ContentGpuClientQt *client) : m_client(client) ++ { ++ content::GpuDataManager *manager = content::GpuDataManager::GetInstance(); ++ if (manager->IsEssentialGpuInfoAvailable()) ++ OnGpuInfoUpdate(); ++ } ++ ++ ~GpuObserver() { content::GpuDataManager::GetInstance()->RemoveObserver(this); } ++ ++ void OnGpuInfoUpdate() override ++ { ++ Q_ASSERT(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); ++ ++ content::GpuDataManager *manager = content::GpuDataManager::GetInstance(); ++ Q_ASSERT(manager->IsEssentialGpuInfoAvailable()); ++ ++ const gpu::GPUInfo &gpuInfo = manager->GetGPUInfo(); ++ Q_ASSERT(gpuInfo.IsInitialized()); ++ ++ // Avoid logging the info again if the device hasn't changed. ++ // A change in the device is unexpected, as we currently don't support or implement ++ // fallback. Logging the info multiple times may indicate a problem. ++ if (m_gpuInfo && isSameDevice(m_gpuInfo->gpu, gpuInfo.gpu)) ++ return; ++ m_gpuInfo = gpuInfo; ++ ++ const gpu::GpuFeatureStatus gpuCompositingStatus = ++ manager->GetFeatureStatus(gpu::GPU_FEATURE_TYPE_ACCELERATED_GL); ++ qCDebug(lcWebEngineCompositor, "GPU Compositing: %s", ++ gpuFeatureStatusToString(gpuCompositingStatus)); ++ ++#if BUILDFLAG(IS_OZONE) ++ if (gpuCompositingStatus == gpu::kGpuFeatureStatusEnabled) { ++ // See entry 3 in //gpu/config/software_rendering_list.json ++ QRegularExpression filter(u"software|llvmpipe|softpipe"_s, ++ QRegularExpression::CaseInsensitiveOption); ++ if (filter.match(QLatin1StringView(gpuInfo.gl_renderer)).hasMatch()) { ++ qWarning("Hardware rendering is enabled but it is not supported with Mesa software " ++ "rasterizer. Expect troubles."); ++ ++ if (m_client->gpuPreferences().ignore_gpu_blocklist) ++ qWarning("Rendering may fail because --ignore-gpu-blocklist is set."); ++ } ++ } ++#endif ++ ++ qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(angleInfo(gpuInfo))); ++#if BUILDFLAG(IS_WIN) ++ qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(windowsInfo(gpuInfo))); ++#endif ++ } ++ ++private: ++ ContentGpuClientQt *m_client; ++ std::optional m_gpuInfo; ++}; ++ ++ContentGpuClientQt::ContentGpuClientQt() = default; ++ContentGpuClientQt::~ContentGpuClientQt() = default; ++ ++void ContentGpuClientQt::GpuServiceInitialized() ++{ ++ // This is expected to be called on the GPU thread. ++ Q_ASSERT(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); ++ ++ m_gpuObserver.reset(new GpuObserver(this)); ++ content::GpuDataManager::GetInstance()->AddObserver(m_gpuObserver.get()); ++} ++ ++void ContentGpuClientQt::ExposeInterfacesToBrowser( ++ const gpu::GpuPreferences &gpu_preferences, ++ const gpu::GpuDriverBugWorkarounds &gpu_workarounds, mojo::BinderMap *binders) ++{ ++ Q_UNUSED(gpu_workarounds); ++ Q_UNUSED(binders); ++ m_gpuPreferences = gpu_preferences; ++} ++ ++} // namespace QtWebEngineCore +diff --git a/src/core/gpu/content_gpu_client_qt.h b/src/core/gpu/content_gpu_client_qt.h +new file mode 100644 +index 0000000..51b3a0d +--- /dev/null ++++ src/core/gpu/content_gpu_client_qt.h +@@ -0,0 +1,46 @@ ++// Copyright (C) 2025 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 ++// Qt-Security score:significant reason:default ++ ++#ifndef CONTENT_GPU_CLIENT_QT_H ++#define CONTENT_GPU_CLIENT_QT_H ++ ++#include "content/public/gpu/content_gpu_client.h" ++#include "gpu/config/gpu_preferences.h" ++ ++#include ++ ++namespace gpu { ++class GpuDriverBugWorkarounds; ++} ++ ++namespace mojo { ++class BinderMap; ++} ++ ++namespace QtWebEngineCore { ++ ++class GpuObserver; ++ ++class ContentGpuClientQt : public content::ContentGpuClient ++{ ++public: ++ ContentGpuClientQt(); ++ ~ContentGpuClientQt(); ++ ++ gpu::GpuPreferences gpuPreferences() const { return m_gpuPreferences; } ++ ++ // Overridden from content::ContentGpuClient: ++ void GpuServiceInitialized() override; ++ void ExposeInterfacesToBrowser(const gpu::GpuPreferences &gpu_preferences, ++ const gpu::GpuDriverBugWorkarounds &gpu_workarounds, ++ mojo::BinderMap *binders) override; ++ ++private: ++ QScopedPointer m_gpuObserver; ++ gpu::GpuPreferences m_gpuPreferences; ++}; ++ ++} // namespace QtWebEngineCore ++ ++#endif // CONTENT_GPU_CLIENT_QT_H