Compare commits

..

No commits in common. "8e8f27a22aa3fe8a0470dd19ebbd87b1b9d72ed0" and "5eb6f51f4a2a84d3f0f3f7352253780730beee1b" have entirely different histories.

25 changed files with 43 additions and 546 deletions

View file

@ -50,7 +50,6 @@ jobs:
wayland-protocols \
wayland \
libdrm \
vulkan-headers \
libxcb \
libpipewire \
cli11 \

View file

@ -146,7 +146,6 @@ To disable: `-DSCREENCOPY=OFF`
Dependencies:
- `libdrm`
- `libgbm`
- `vulkan-headers` (build-time)
Specific protocols can also be disabled:
- `DSCREENCOPY_ICC=OFF` - Disable screencopy via [ext-image-copy-capture-v1]

View file

@ -24,8 +24,6 @@ set shell id.
- Added support for grabbing focus from popup windows.
- Added support for IPC signal listeners.
- Added Quickshell version checking and version gated preprocessing.
- Added a way to detect if an icon is from the system icon theme or not.
- Added vulkan support to screencopy.
## Other Changes
@ -37,7 +35,6 @@ set shell id.
- Fixed volume control breaking with pipewire pro audio mode.
- Fixed volume control breaking with bluez streams and potentially others.
- Fixed volume control breaking for devices without route definitions.
- Fixed escape sequence handling in desktop entries.
- Fixed volumes not initializing if a pipewire device was already loaded before its node.
- Fixed hyprland active toplevel not resetting after window closes.
@ -46,10 +43,7 @@ set shell id.
- Fixed asynchronous loaders not working after reload.
- Fixed asynchronous loaders not working before window creation.
- Fixed memory leak in IPC handlers.
- Fixed ClippingRectangle related crashes.
- Fixed crashes when monitors are unplugged.
## Packaging Changes
`glib` and `polkit` have been added as dependencies when compiling with polkit agent support.
`vulkan-headers` has been added as a build-time dependency for screencopy (Vulkan backend support).

View file

@ -19,7 +19,6 @@
xorg,
libdrm,
libgbm ? null,
vulkan-headers,
pipewire,
pam,
polkit,
@ -78,7 +77,7 @@
++ lib.optional withJemalloc jemalloc
++ lib.optional (withWayland && lib.strings.compareVersions qt6.qtbase.version "6.10.0" == -1) qt6.qtwayland
++ lib.optionals withWayland [ wayland wayland-protocols ]
++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm vulkan-headers ]
++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm ]
++ lib.optional withX11 xorg.libxcb
++ lib.optional withPam pam
++ lib.optional withPipewire pipewire

View file

@ -314,8 +314,6 @@ QString QuickshellGlobal::iconPath(const QString& icon, const QString& fallback)
return IconImageProvider::requestString(icon, "", fallback);
}
bool QuickshellGlobal::hasThemeIcon(const QString& icon) { return QIcon::hasThemeIcon(icon); }
bool QuickshellGlobal::hasVersion(qint32 major, qint32 minor, const QStringList& features) {
return qs::scan::env::PreprocEnv::hasVersion(major, minor, features);
}

View file

@ -202,8 +202,6 @@ public:
/// Setting the `fallback` parameter of `iconPath` will attempt to load the fallback
/// icon if the requested one could not be loaded.
Q_INVOKABLE static QString iconPath(const QString& icon, const QString& fallback);
/// Check if specified icon has an available icon in your icon theme
Q_INVOKABLE static bool hasThemeIcon(const QString& icon);
/// Equivalent to `${Quickshell.configDir}/${path}`
Q_INVOKABLE [[nodiscard]] QString shellPath(const QString& path) const;
/// > [!WARNING] Deprecated: Renamed to @@shellPath() for clarity.

View file

@ -118,7 +118,7 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
auto stream = QTextStream(&file);
auto imports = QVector<QString>();
bool inHeader = true;
bool inHeader = false;
auto ifScopes = QVector<bool>();
bool sourceMasked = false;
int lineNum = 0;
@ -177,7 +177,7 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
} else if (!internal && line == "//@ pragma Internal") {
internal = true;
} else if (line.contains('{')) {
inHeader = false;
inHeader = true;
}
}

View file

@ -1,7 +1,6 @@
#include "scanenv.hpp"
#include <qcontainerfwd.h>
#include <qtenvironmentvariables.h>
#include "build.hpp"
@ -20,12 +19,4 @@ bool PreprocEnv::hasVersion(int major, int minor, const QStringList& features) {
return QS_VERSION_MAJOR == major && QS_VERSION_MINOR == minor;
}
QString PreprocEnv::env(const QString& variable) {
return qEnvironmentVariable(variable.toStdString().c_str());
}
bool PreprocEnv::isEnvSet(const QString& variable) {
return qEnvironmentVariableIsSet(variable.toStdString().c_str());
}
} // namespace qs::scan::env

View file

@ -12,9 +12,6 @@ class PreprocEnv: public QObject {
public:
Q_INVOKABLE static bool
hasVersion(int major, int minor, const QStringList& features = QStringList());
Q_INVOKABLE static QString env(const QString& variable);
Q_INVOKABLE static bool isEnvSet(const QString& variable);
};
} // namespace qs::scan::env

View file

@ -61,7 +61,7 @@ IpcServerConnection::IpcServerConnection(QLocalSocket* socket, IpcServer* server
void IpcServerConnection::onDisconnected() {
qCInfo(logIpc) << "IPC connection disconnected" << this;
this->deleteLater();
delete this;
}
void IpcServerConnection::onReadyRead() {
@ -88,7 +88,7 @@ void IpcServerConnection::onReadyRead() {
// async connections reparent
if (dynamic_cast<IpcServer*>(this->parent()) != nullptr) {
this->deleteLater();
delete this;
}
}

View file

@ -141,10 +141,6 @@ bool PwDevice::tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps
return true;
}
bool PwDevice::hasRouteDevice(qint32 routeDevice) const {
return this->routeDeviceIndexes.contains(routeDevice);
}
void PwDevice::polled() {
// It is far more likely that the list content has not come in yet than it having no entries,
// and there isn't a way to check in the case that there *aren't* actually any entries.

View file

@ -12,15 +12,13 @@
#include <spa/pod/builder.h>
#include "core.hpp"
#include "node.hpp"
#include "registry.hpp"
namespace qs::service::pipewire {
class PwDevice;
// Forward declare to avoid circular dependency with node.hpp
struct PwVolumeProps;
class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> {
Q_OBJECT;
@ -35,7 +33,6 @@ public:
[[nodiscard]] bool waitingForDevice() const;
[[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps);
[[nodiscard]] bool hasRouteDevice(qint32 routeDevice) const;
signals:
void deviceReady();

View file

@ -429,10 +429,6 @@ void PwNodeBoundAudio::setMuted(bool muted) {
}
float PwNodeBoundAudio::averageVolume() const {
if (this->mVolumes.isEmpty()) {
return 0.0f;
}
float total = 0;
for (auto volume: this->mVolumes) {
@ -576,8 +572,9 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute);
const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep);
if (volumesProp) {
const auto* volumes = reinterpret_cast<const spa_pod_array*>(&volumesProp->value);
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
spa_pod* iter = nullptr;
SPA_POD_ARRAY_FOREACH(volumes, iter) {
// Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly.
@ -585,19 +582,12 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
auto visual = std::cbrt(linear);
props.volumes.push_back(visual);
}
}
if (channelsProp) {
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
spa_pod* iter = nullptr;
SPA_POD_ARRAY_FOREACH(channels, iter) {
props.channels.push_back(*reinterpret_cast<PwAudioChannel::Enum*>(iter));
}
}
if (muteProp) {
spa_pod_get_bool(&muteProp->value, &props.mute);
}
if (volumeStepProp) {
spa_pod_get_float(&volumeStepProp->value, &props.volumeStep);

View file

@ -15,7 +15,6 @@
#include <spa/pod/pod.h>
#include "core.hpp"
#include "device.hpp"
#include "registry.hpp"
namespace qs::service::pipewire {
@ -250,9 +249,7 @@ public:
bool proAudio = false;
[[nodiscard]] bool shouldUseDevice() const {
if (!this->device || this->proAudio || this->routeDevice == -1) return false;
// Only use device control if the device actually has route indexes for this routeDevice
return this->device->hasRouteDevice(this->routeDevice);
return this->device && !this->proAudio && this->routeDevice != -1;
}
signals:

View file

@ -1,8 +1,6 @@
find_package(PkgConfig REQUIRED)
pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl)
find_package(VulkanHeaders REQUIRED)
qt_add_library(quickshell-wayland-buffer STATIC
manager.cpp
dmabuf.cpp
@ -12,10 +10,9 @@ qt_add_library(quickshell-wayland-buffer STATIC
wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf")
target_link_libraries(quickshell-wayland-buffer PRIVATE
Qt::Quick Qt::QuickPrivate Qt::GuiPrivate Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
PkgConfig::dmabuf-deps
wlp-linux-dmabuf
Vulkan::Headers
)
qs_pch(quickshell-wayland-buffer SET large)

View file

@ -14,8 +14,6 @@
#include <fcntl.h>
#include <gbm.h>
#include <libdrm/drm_fourcc.h>
#include <private/qquickwindow_p.h>
#include <private/qrhivulkan_p.h>
#include <qcontainerfwd.h>
#include <qdebug.h>
#include <qlist.h>
@ -26,17 +24,12 @@
#include <qpair.h>
#include <qquickwindow.h>
#include <qscopedpointer.h>
#include <qsgrendererinterface.h>
#include <qsgtexture_platform.h>
#include <qtypes.h>
#include <qvulkanfunctions.h>
#include <qvulkaninstance.h>
#include <qwayland-linux-dmabuf-v1.h>
#include <qwaylandclientextension.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <vulkan/vulkan_core.h>
#include <wayland-client-protocol.h>
#include <wayland-linux-dmabuf-v1-client-protocol.h>
#include <wayland-util.h>
@ -55,36 +48,6 @@ QS_LOGGING_CATEGORY(logDmabuf, "quickshell.wayland.buffer.dmabuf", QtWarningMsg)
LinuxDmabufManager* MANAGER = nullptr; // NOLINT
VkFormat drmFormatToVkFormat(uint32_t drmFormat) {
// NOLINTBEGIN(bugprone-branch-clone): XRGB/ARGB intentionally map to the same VK format
switch (drmFormat) {
case DRM_FORMAT_ARGB8888: return VK_FORMAT_B8G8R8A8_UNORM;
case DRM_FORMAT_XRGB8888: return VK_FORMAT_B8G8R8A8_UNORM;
case DRM_FORMAT_ABGR8888: return VK_FORMAT_R8G8B8A8_UNORM;
case DRM_FORMAT_XBGR8888: return VK_FORMAT_R8G8B8A8_UNORM;
case DRM_FORMAT_ARGB2101010: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
case DRM_FORMAT_XRGB2101010: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
case DRM_FORMAT_ABGR2101010: return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
case DRM_FORMAT_XBGR2101010: return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
case DRM_FORMAT_ABGR16161616F: return VK_FORMAT_R16G16B16A16_SFLOAT;
case DRM_FORMAT_RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16;
case DRM_FORMAT_BGR565: return VK_FORMAT_B5G6R5_UNORM_PACK16;
default: return VK_FORMAT_UNDEFINED;
}
// NOLINTEND(bugprone-branch-clone)
}
bool drmFormatHasAlpha(uint32_t drmFormat) {
switch (drmFormat) {
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_ABGR16161616F: return true;
default: return false;
}
}
} // namespace
QDebug& operator<<(QDebug& debug, const FourCCStr& fourcc) {
@ -118,27 +81,25 @@ GbmDeviceHandle::~GbmDeviceHandle() {
}
}
// Prefer ARGB over XRGB: XRGB has undefined alpha bytes which cause
// transparency artifacts on Vulkan (notably Intel GPUs) since Vulkan
// doesn't auto-fill alpha=1.0 for X formats like EGL does.
// This will definitely backfire later
void LinuxDmabufFormatSelection::ensureSorted() {
if (this->sorted) return;
auto beginIter = this->formats.begin();
auto argbIter = std::ranges::find_if(this->formats, [](const auto& format) {
return format.first == DRM_FORMAT_ARGB8888;
});
if (argbIter != this->formats.end()) {
std::swap(*beginIter, *argbIter);
++beginIter;
}
auto xrgbIter = std::ranges::find_if(this->formats, [](const auto& format) {
return format.first == DRM_FORMAT_XRGB8888;
});
if (xrgbIter != this->formats.end()) std::swap(*beginIter, *xrgbIter);
if (xrgbIter != this->formats.end()) {
std::swap(*beginIter, *xrgbIter);
++beginIter;
}
auto argbIter = std::ranges::find_if(this->formats, [](const auto& format) {
return format.first == DRM_FORMAT_ARGB8888;
});
if (argbIter != this->formats.end()) std::swap(*beginIter, *argbIter);
this->sorted = true;
}
@ -571,15 +532,6 @@ bool WlDmaBuffer::isCompatible(const WlBufferRequest& request) const {
}
WlBufferQSGTexture* WlDmaBuffer::createQsgTexture(QQuickWindow* window) const {
auto* ri = window->rendererInterface();
if (ri && ri->graphicsApi() == QSGRendererInterface::Vulkan) {
return this->createQsgTextureVulkan(window);
}
return this->createQsgTextureGl(window);
}
WlBufferQSGTexture* WlDmaBuffer::createQsgTextureGl(QQuickWindow* window) const {
static auto* glEGLImageTargetTexture2DOES = []() {
auto* fn = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(
eglGetProcAddress("glEGLImageTargetTexture2DOES")
@ -710,313 +662,6 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTextureGl(QQuickWindow* window) const
return tex;
}
WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) const {
auto* ri = window->rendererInterface();
auto* vkInst = window->vulkanInstance();
if (!vkInst) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: no QVulkanInstance.";
return nullptr;
}
auto* vkDevicePtr =
static_cast<VkDevice*>(ri->getResource(window, QSGRendererInterface::DeviceResource));
auto* vkPhysDevicePtr = static_cast<VkPhysicalDevice*>(
ri->getResource(window, QSGRendererInterface::PhysicalDeviceResource)
);
if (!vkDevicePtr || !vkPhysDevicePtr) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: could not get Vulkan device.";
return nullptr;
}
VkDevice device = *vkDevicePtr;
VkPhysicalDevice physDevice = *vkPhysDevicePtr;
auto* devFuncs = vkInst->deviceFunctions(device);
auto* instFuncs = vkInst->functions();
if (!devFuncs || !instFuncs) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: "
"could not get Vulkan functions.";
return nullptr;
}
auto getMemoryFdPropertiesKHR = reinterpret_cast<PFN_vkGetMemoryFdPropertiesKHR>(
instFuncs->vkGetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR")
);
if (!getMemoryFdPropertiesKHR) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: "
"vkGetMemoryFdPropertiesKHR not available. "
"Missing VK_KHR_external_memory_fd extension.";
return nullptr;
}
const VkFormat vkFormat = drmFormatToVkFormat(this->format);
if (vkFormat == VK_FORMAT_UNDEFINED) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: unsupported DRM format"
<< FourCCStr(this->format);
return nullptr;
}
if (this->planeCount > 4) {
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: too many planes"
<< this->planeCount;
return nullptr;
}
std::array<VkSubresourceLayout, 4> planeLayouts = {};
for (int i = 0; i < this->planeCount; ++i) {
planeLayouts[i].offset = this->planes[i].offset; // NOLINT
planeLayouts[i].rowPitch = this->planes[i].stride; // NOLINT
planeLayouts[i].size = 0;
planeLayouts[i].arrayPitch = 0;
planeLayouts[i].depthPitch = 0;
}
const bool useModifier = this->modifier != DRM_FORMAT_MOD_INVALID;
VkExternalMemoryImageCreateInfo externalInfo = {};
externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = {};
modifierInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
modifierInfo.drmFormatModifier = this->modifier;
modifierInfo.drmFormatModifierPlaneCount = static_cast<uint32_t>(this->planeCount);
modifierInfo.pPlaneLayouts = planeLayouts.data();
if (useModifier) {
externalInfo.pNext = &modifierInfo;
}
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = &externalInfo;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = vkFormat;
imageInfo.extent = {.width = this->width, .height = this->height, .depth = 1};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = useModifier ? VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT : VK_IMAGE_TILING_LINEAR;
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage image = VK_NULL_HANDLE;
VkResult result = devFuncs->vkCreateImage(device, &imageInfo, nullptr, &image);
if (result != VK_SUCCESS) {
qCWarning(logDmabuf) << "Failed to create VkImage for DMA-BUF import, result:" << result;
return nullptr;
}
VkDeviceMemory memory = VK_NULL_HANDLE;
// dup() is required because vkAllocateMemory with VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
// takes ownership of the fd on succcess. Without dup, WlDmaBuffer would double-close.
const int dupFd =
dup(this->planes[0].fd); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
if (dupFd < 0) {
qCWarning(logDmabuf) << "Failed to dup() fd for DMA-BUF import";
goto cleanup_fail; // NOLINT
}
{
VkMemoryRequirements memReqs = {};
devFuncs->vkGetImageMemoryRequirements(device, image, &memReqs);
VkMemoryFdPropertiesKHR fdProps = {};
fdProps.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
result = getMemoryFdPropertiesKHR(
device,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
dupFd,
&fdProps
);
if (result != VK_SUCCESS) {
close(dupFd);
qCWarning(logDmabuf) << "vkGetMemoryFdPropertiesKHR failed, result:" << result;
goto cleanup_fail; // NOLINT
}
const uint32_t memTypeBits = memReqs.memoryTypeBits & fdProps.memoryTypeBits;
VkPhysicalDeviceMemoryProperties memProps = {};
instFuncs->vkGetPhysicalDeviceMemoryProperties(physDevice, &memProps);
uint32_t memTypeIndex = UINT32_MAX;
for (uint32_t j = 0; j < memProps.memoryTypeCount; ++j) {
if (memTypeBits & (1u << j)) {
memTypeIndex = j;
break;
}
}
if (memTypeIndex == UINT32_MAX) {
close(dupFd);
qCWarning(logDmabuf) << "No compatible memory type for DMA-BUF import";
goto cleanup_fail; // NOLINT
}
VkImportMemoryFdInfoKHR importInfo = {};
importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
importInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
importInfo.fd = dupFd;
VkMemoryDedicatedAllocateInfo dedicatedInfo = {};
dedicatedInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
dedicatedInfo.image = image;
dedicatedInfo.pNext = &importInfo;
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.pNext = &dedicatedInfo;
allocInfo.allocationSize = memReqs.size;
allocInfo.memoryTypeIndex = memTypeIndex;
result = devFuncs->vkAllocateMemory(device, &allocInfo, nullptr, &memory);
if (result != VK_SUCCESS) {
close(dupFd);
qCWarning(logDmabuf) << "vkAllocateMemory failed, result:" << result;
goto cleanup_fail; // NOLINT
}
result = devFuncs->vkBindImageMemory(device, image, memory, 0);
if (result != VK_SUCCESS) {
qCWarning(logDmabuf) << "vkBindImageMemory failed, result:" << result;
goto cleanup_fail; // NOLINT
}
}
{
// acquire the DMA-BUF from the foreign (compositor) queue and transition
// to shader-read layout. oldLayout must be GENERAL (not UNDEFINED) to
// preserve the DMA-BUF contents written by the external producer. Hopefully.
window->beginExternalCommands();
auto* cmdBufPtr = static_cast<VkCommandBuffer*>(
ri->getResource(window, QSGRendererInterface::CommandListResource)
);
if (cmdBufPtr && *cmdBufPtr) {
VkCommandBuffer cmdBuf = *cmdBufPtr;
// find the graphics queue family index for the ownrship transfer.
uint32_t graphicsQueueFamily = 0;
uint32_t queueFamilyCount = 0;
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(
physDevice,
&queueFamilyCount,
queueFamilies.data()
);
for (uint32_t i = 0; i < queueFamilyCount; ++i) {
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsQueueFamily = i;
break;
}
}
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
barrier.dstQueueFamilyIndex = graphicsQueueFamily;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
devFuncs->vkCmdPipelineBarrier(
cmdBuf,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&barrier
);
}
window->endExternalCommands();
auto* qsgTexture = QQuickWindowPrivate::get(window)->createTextureFromNativeTexture(
reinterpret_cast<quint64>(image),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
static_cast<uint>(vkFormat),
QSize(static_cast<int>(this->width), static_cast<int>(this->height)),
{}
);
// For opaque DRM formats (XRGB, XBGR, etc.), the alpha bytes are underfined.
// EGL silently forces alpha=1.0 for these, but Vulkan doesn't. Replace Qt's
// default identity-swizzle VkImageView with one that maps alpha to ONE.
if (!drmFormatHasAlpha(this->format)) {
auto* vkTexture = static_cast<QVkTexture*>(qsgTexture->rhiTexture()); // NOLINT
devFuncs->vkDestroyImageView(device, vkTexture->imageView, nullptr);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vkFormat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.layerCount = 1;
result = devFuncs->vkCreateImageView(device, &viewInfo, nullptr, &vkTexture->imageView);
if (result != VK_SUCCESS) {
qCWarning(logDmabuf) << "Failed to create alpha-swizzled VkImageView, result:" << result;
}
}
auto* tex = new WlDmaBufferVulkanQSGTexture(devFuncs, device, image, memory, qsgTexture);
qCDebug(logDmabuf) << "Created WlDmaBufferVulkanQSGTexture" << tex << "from" << this;
return tex;
}
cleanup_fail:
if (image != VK_NULL_HANDLE) {
devFuncs->vkDestroyImage(device, image, nullptr);
}
if (memory != VK_NULL_HANDLE) {
devFuncs->vkFreeMemory(device, memory, nullptr);
}
return nullptr;
}
WlDmaBufferVulkanQSGTexture::~WlDmaBufferVulkanQSGTexture() {
delete this->qsgTexture;
if (this->image != VK_NULL_HANDLE) {
this->devFuncs->vkDestroyImage(this->device, this->image, nullptr);
}
if (this->memory != VK_NULL_HANDLE) {
this->devFuncs->vkFreeMemory(this->device, this->memory, nullptr);
}
qCDebug(logDmabuf) << "WlDmaBufferVulkanQSGTexture" << this << "destroyed.";
}
WlDmaBufferQSGTexture::~WlDmaBufferQSGTexture() {
auto* context = QOpenGLContext::currentContext();
auto* display = context->nativeInterface<QNativeInterface::QEGLContext>()->display();

View file

@ -1,6 +1,5 @@
#pragma once
#include <array>
#include <cstdint>
#include <EGL/egl.h>
@ -13,11 +12,9 @@
#include <qsize.h>
#include <qtclasshelpermacros.h>
#include <qtypes.h>
#include <qvulkanfunctions.h>
#include <qwayland-linux-dmabuf-v1.h>
#include <qwaylandclientextension.h>
#include <sys/types.h>
#include <vulkan/vulkan_core.h>
#include <wayland-linux-dmabuf-v1-client-protocol.h>
#include <wayland-util.h>
#include <xf86drm.h>
@ -117,36 +114,6 @@ private:
friend class WlDmaBuffer;
};
class WlDmaBufferVulkanQSGTexture: public WlBufferQSGTexture {
public:
~WlDmaBufferVulkanQSGTexture() override;
Q_DISABLE_COPY_MOVE(WlDmaBufferVulkanQSGTexture);
[[nodiscard]] QSGTexture* texture() const override { return this->qsgTexture; }
private:
WlDmaBufferVulkanQSGTexture(
QVulkanDeviceFunctions* devFuncs,
VkDevice device,
VkImage image,
VkDeviceMemory memory,
QSGTexture* qsgTexture
)
: devFuncs(devFuncs)
, device(device)
, image(image)
, memory(memory)
, qsgTexture(qsgTexture) {}
QVulkanDeviceFunctions* devFuncs = nullptr;
VkDevice device = VK_NULL_HANDLE;
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
QSGTexture* qsgTexture = nullptr;
friend class WlDmaBuffer;
};
class WlDmaBuffer: public WlBuffer {
public:
~WlDmaBuffer() override;
@ -184,9 +151,6 @@ private:
friend class LinuxDmabufManager;
friend QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer);
[[nodiscard]] WlBufferQSGTexture* createQsgTextureGl(QQuickWindow* window) const;
[[nodiscard]] WlBufferQSGTexture* createQsgTextureVulkan(QQuickWindow* window) const;
};
QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer);

View file

@ -9,7 +9,6 @@
#include <qqmlcomponent.h>
#include <qqmlengine.h>
#include <qqmllist.h>
#include <qquickgraphicsconfiguration.h>
#include <qquickitem.h>
#include <qquickwindow.h>
#include <qscreen.h>
@ -217,15 +216,6 @@ void WlSessionLockSurface::onReload(QObject* oldInstance) {
if (this->window == nullptr) {
this->window = new QQuickWindow();
// needed for vulkan dmabuf import, qt ignores these if not applicable
auto graphicsConfig = this->window->graphicsConfiguration();
graphicsConfig.setDeviceExtensions({
"VK_KHR_external_memory_fd",
"VK_EXT_external_memory_dma_buf",
"VK_EXT_image_drm_format_modifier",
});
this->window->setGraphicsConfiguration(graphicsConfig);
}
this->mContentItem->setParentItem(this->window->contentItem());

View file

@ -28,7 +28,7 @@ QSWaylandSessionLockSurface::QSWaylandSessionLockSurface(QtWaylandClient::QWayla
wl_output* output = nullptr; // NOLINT (include)
auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
if (waylandScreen != nullptr && !waylandScreen->isPlaceholder() && waylandScreen->output()) {
if (waylandScreen != nullptr) {
output = waylandScreen->output();
} else {
qFatal() << "Session lock screen does not corrospond to a real screen. Force closing window";

View file

@ -143,11 +143,11 @@ LayerSurface::LayerSurface(LayerShellIntegration* shell, QtWaylandClient::QWayla
auto* waylandScreen =
dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
if (waylandScreen != nullptr && !waylandScreen->isPlaceholder() && waylandScreen->output()) {
if (waylandScreen != nullptr) {
output = waylandScreen->output();
} else {
qWarning()
<< "Layershell screen does not correspond to a real screen. Letting the compositor pick.";
<< "Layershell screen does not corrospond to a real screen. Letting the compositor pick.";
}
}

View file

@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import QtQuick
///! Rectangle capable of clipping content inside its border.
@ -74,12 +72,6 @@ Item {
}
}
ShaderEffectSource {
id: shaderSource
hideSource: true
sourceItem: contentItemContainer
}
ShaderEffect {
id: shader
anchors.fill: root
@ -87,6 +79,10 @@ Item {
property Rectangle rect: rectangle
property color backgroundColor: "white"
property color borderColor: root.border.color
property ShaderEffectSource content: shaderSource
property ShaderEffectSource content: ShaderEffectSource {
hideSource: true
sourceItem: contentItemContainer
}
}
}

View file

@ -13,7 +13,6 @@
#include <qqmlengine.h>
#include <qqmlinfo.h>
#include <qqmllist.h>
#include <qquickgraphicsconfiguration.h>
#include <qquickitem.h>
#include <qquickwindow.h>
#include <qregion.h>
@ -148,15 +147,6 @@ void ProxyWindowBase::ensureQWindow() {
this->window = nullptr; // createQQuickWindow may indirectly reference this->window
this->window = this->createQQuickWindow();
this->window->setFormat(format);
// needed for vulkan dmabuf import, qt ignores these if not applicable
auto graphicsConfig = this->window->graphicsConfiguration();
graphicsConfig.setDeviceExtensions({
"VK_KHR_external_memory_fd",
"VK_EXT_external_memory_dma_buf",
"VK_EXT_image_drm_format_modifier",
});
this->window->setGraphicsConfiguration(graphicsConfig);
}
void ProxyWindowBase::createWindow() {
@ -223,7 +213,6 @@ void ProxyWindowBase::completeWindow() {
this->trySetHeight(this->implicitHeight());
this->setColor(this->mColor);
this->updateMask();
QQuickWindowPrivate::get(this->window)->updatesEnabled = this->mUpdatesEnabled;
// notify initial / post-connection geometry
emit this->xChanged();
@ -299,18 +288,12 @@ void ProxyWindowBase::setVisibleDirect(bool visible) {
this->bBackerVisibility = false;
this->deleteWindow();
}
} else {
if (visible && this->window == nullptr) {
this->createWindow();
}
if (this->window != nullptr) {
} else if (this->window != nullptr) {
if (visible) this->polishItems();
this->window->setVisible(visible);
this->bBackerVisibility = visible;
}
}
}
void ProxyWindowBase::schedulePolish() {
if (this->isVisibleDirect()) {
@ -480,19 +463,6 @@ void ProxyWindowBase::setSurfaceFormat(QsSurfaceFormat format) {
emit this->surfaceFormatChanged();
}
bool ProxyWindowBase::updatesEnabled() const { return this->mUpdatesEnabled; }
void ProxyWindowBase::setUpdatesEnabled(bool updatesEnabled) {
if (updatesEnabled == this->mUpdatesEnabled) return;
this->mUpdatesEnabled = updatesEnabled;
if (this->window != nullptr) {
QQuickWindowPrivate::get(this->window)->updatesEnabled = updatesEnabled;
}
emit this->updatesEnabledChanged();
}
qreal ProxyWindowBase::devicePixelRatio() const {
if (this->window != nullptr) return this->window->devicePixelRatio();
if (this->mScreen != nullptr) return this->mScreen->devicePixelRatio();

View file

@ -57,7 +57,6 @@ class ProxyWindowBase: public Reloadable {
Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged);
Q_PROPERTY(bool backingWindowVisible READ isVisibleDirect NOTIFY backerVisibilityChanged);
Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
Q_PROPERTY(bool updatesEnabled READ updatesEnabled WRITE setUpdatesEnabled NOTIFY updatesEnabledChanged);
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
// clang-format on
Q_CLASSINFO("DefaultProperty", "data");
@ -141,9 +140,6 @@ public:
[[nodiscard]] QsSurfaceFormat surfaceFormat() const { return this->qsSurfaceFormat; }
void setSurfaceFormat(QsSurfaceFormat format);
[[nodiscard]] bool updatesEnabled() const;
void setUpdatesEnabled(bool updatesEnabled);
[[nodiscard]] QObject* windowTransform() const { return nullptr; } // NOLINT
[[nodiscard]] QQmlListProperty<QObject> data();
@ -167,7 +163,6 @@ signals:
void colorChanged();
void maskChanged();
void surfaceFormatChanged();
void updatesEnabledChanged();
void polished();
protected slots:
@ -192,7 +187,6 @@ protected:
ProxyWindowContentItem* mContentItem = nullptr;
bool reloadComplete = false;
bool ranLints = false;
bool mUpdatesEnabled = true;
QsSurfaceFormat qsSurfaceFormat;
QSurfaceFormat mSurfaceFormat;

View file

@ -127,9 +127,6 @@ void WindowInterface::setMask(PendingRegion* mask) const { this->proxyWindow()->
QsSurfaceFormat WindowInterface::surfaceFormat() const { return this->proxyWindow()->surfaceFormat(); };
void WindowInterface::setSurfaceFormat(QsSurfaceFormat format) const { this->proxyWindow()->setSurfaceFormat(format); };
bool WindowInterface::updatesEnabled() const { return this->proxyWindow()->updatesEnabled(); };
void WindowInterface::setUpdatesEnabled(bool updatesEnabled) const { this->proxyWindow()->setUpdatesEnabled(updatesEnabled); };
QQmlListProperty<QObject> WindowInterface::data() const { return this->proxyWindow()->data(); };
// clang-format on
@ -151,7 +148,6 @@ void WindowInterface::connectSignals() const {
QObject::connect(window, &ProxyWindowBase::colorChanged, this, &WindowInterface::colorChanged);
QObject::connect(window, &ProxyWindowBase::maskChanged, this, &WindowInterface::maskChanged);
QObject::connect(window, &ProxyWindowBase::surfaceFormatChanged, this, &WindowInterface::surfaceFormatChanged);
QObject::connect(window, &ProxyWindowBase::updatesEnabledChanged, this, &WindowInterface::updatesEnabledChanged);
// clang-format on
}

View file

@ -143,12 +143,6 @@ class WindowInterface: public Reloadable {
///
/// > [!NOTE] The surface format cannot be changed after the window is created.
Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
/// If the window should receive render updates. Defaults to true.
///
/// When set to false, the window will not re-render in response to animations
/// or other visual updates from other windows. This is useful for static windows
/// such as wallpapers that do not need to update frequently, saving GPU cycles.
Q_PROPERTY(bool updatesEnabled READ updatesEnabled WRITE setUpdatesEnabled NOTIFY updatesEnabledChanged);
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
// clang-format on
Q_CLASSINFO("DefaultProperty", "data");
@ -237,9 +231,6 @@ public:
[[nodiscard]] QsSurfaceFormat surfaceFormat() const;
void setSurfaceFormat(QsSurfaceFormat format) const;
[[nodiscard]] bool updatesEnabled() const;
void setUpdatesEnabled(bool updatesEnabled) const;
[[nodiscard]] QQmlListProperty<QObject> data() const;
static QsWindowAttached* qmlAttachedProperties(QObject* object);
@ -267,7 +258,6 @@ signals:
void colorChanged();
void maskChanged();
void surfaceFormatChanged();
void updatesEnabledChanged();
protected:
void connectSignals() const;