wayland/screencopy: pin XRGB alpha to 1 in vulkan mode

While EGL handles this internally, vulkan's alpha channel behavior is
undefined when rendering depending on the driver. Notably intel does
not treat it as 1.0.
This commit is contained in:
reakjra 2026-02-23 18:28:01 +01:00 committed by outfoxxed
parent 2cf57f43d5
commit c3c3e2ca25
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
2 changed files with 55 additions and 12 deletions

View file

@ -12,7 +12,7 @@ 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::WaylandClient Qt::WaylandClientPrivate wayland-client
Qt::Quick Qt::QuickPrivate Qt::GuiPrivate Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
PkgConfig::dmabuf-deps
wlp-linux-dmabuf
Vulkan::Headers

View file

@ -15,6 +15,7 @@
#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>
@ -73,6 +74,19 @@ VkFormat drmFormatToVkFormat(uint32_t drmFormat) {
// 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) {
@ -106,25 +120,27 @@ GbmDeviceHandle::~GbmDeviceHandle() {
}
}
// This will definitely backfire later
// 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.
void LinuxDmabufFormatSelection::ensureSorted() {
if (this->sorted) return;
auto beginIter = this->formats.begin();
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);
++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);
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);
this->sorted = true;
}
@ -946,6 +962,33 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) co
{}
);
// 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,