Skip to content

Commit 9f9594d

Browse files
committed
GPU Wayland Display: Support pixel-precise rendering
1 parent fbc170d commit 9f9594d

4 files changed

Lines changed: 70 additions & 14 deletions

File tree

‎GPU/GPUTracking/display/CMakeLists.txt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ if(GPUCA_EVENT_DISPLAY_VULKAN)
7878
if(GPUCA_EVENT_DISPLAY_WAYLAND)
7979
set(WAYLAND_PROTOCOL_HEADERS)
8080
set(WAYLAND_PROTOCOL_SOURCES)
81-
foreach(xml stable/xdg-shell/xdg-shell.xml unstable/xdg-decoration/xdg-decoration-unstable-v1.xml)
81+
foreach(xml stable/xdg-shell/xdg-shell.xml stable/viewporter/viewporter.xml unstable/xdg-decoration/xdg-decoration-unstable-v1.xml staging/fractional-scale/fractional-scale-v1.xml)
8282
get_filename_component(name ${xml} NAME_WE)
83-
string(REPLACE "-unstable-v1" "" name ${name})
83+
string(REGEX REPLACE "(-unstable)?-v[0-9]+$" "" name ${name})
8484
set(header ${CMAKE_CURRENT_BINARY_DIR}/xdg-gen/${name}-client-protocol.h)
8585
set(source ${CMAKE_CURRENT_BINARY_DIR}/xdg-gen/${name}-protocol.c)
8686
add_custom_command(

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx‎

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <cerrno>
2727
#include <chrono>
2828
#include <algorithm>
29+
#include <cmath>
2930

3031
#include <vulkan/vulkan.h>
3132
#include <vulkan/vulkan_wayland.h>
@@ -37,6 +38,7 @@
3738
#include <wayland-client.h>
3839
#include "xdg-shell-client-protocol.h"
3940
#include "xdg-decoration-client-protocol.h"
41+
#include "viewporter-client-protocol.h"
4042
#include <xkbcommon/xkbcommon.h>
4143
#include <linux/input-event-codes.h>
4244

@@ -353,6 +355,10 @@ int32_t GPUDisplayFrontendWayland::FrontendMain()
353355
wl_seat_add_listener(me->mSeat, &me->mSeatListener, me);
354356
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
355357
me->mDecManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1);
358+
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
359+
me->mViewporter = (wp_viewporter*)wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
360+
} else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
361+
me->mFractionalScaleManager = (wp_fractional_scale_manager_v1*)wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1);
356362
}
357363
};
358364
auto registry_global_remove = [](void* data, wl_registry* registry, uint32_t name) {
@@ -416,6 +422,18 @@ int32_t GPUDisplayFrontendWayland::FrontendMain()
416422
};
417423
wl_surface_add_listener(mSurface, &mSurfaceListener, this);
418424

425+
if (mViewporter && mFractionalScaleManager) {
426+
auto fractional_scale_preferred_scale = [](void* data, wp_fractional_scale_v1* fractional_scale, uint32_t scale) {
427+
GPUDisplayFrontendWayland* me = (GPUDisplayFrontendWayland*)data;
428+
me->mFractionalScale120 = scale;
429+
me->updateScale();
430+
};
431+
mFractionalScaleListener = wp_fractional_scale_v1_listener{.preferred_scale = fractional_scale_preferred_scale};
432+
mViewport = wp_viewporter_get_viewport(mViewporter, mSurface);
433+
mFractionalScale = wp_fractional_scale_manager_v1_get_fractional_scale(mFractionalScaleManager, mSurface);
434+
wp_fractional_scale_v1_add_listener(mFractionalScale, &mFractionalScaleListener, this);
435+
}
436+
419437
mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
420438
if (mXdgSurface == nullptr) {
421439
throw std::runtime_error("Error creating wayland xdg surface");
@@ -594,9 +612,25 @@ int32_t GPUDisplayFrontendWayland::FrontendMain()
594612
zxdg_toplevel_decoration_v1_destroy(mXdgDecoration);
595613
mXdgDecoration = nullptr;
596614
}
615+
if (mFractionalScale) {
616+
wp_fractional_scale_v1_destroy(mFractionalScale);
617+
mFractionalScale = nullptr;
618+
}
619+
if (mViewport) {
620+
wp_viewport_destroy(mViewport);
621+
mViewport = nullptr;
622+
}
597623
xdg_toplevel_destroy(mXdgToplevel);
598624
xdg_surface_destroy(mXdgSurface);
599625
wl_surface_destroy(mSurface);
626+
if (mFractionalScaleManager) {
627+
wp_fractional_scale_manager_v1_destroy(mFractionalScaleManager);
628+
mFractionalScaleManager = nullptr;
629+
}
630+
if (mViewporter) {
631+
wp_viewporter_destroy(mViewporter);
632+
mViewporter = nullptr;
633+
}
600634
if (mDecManager) {
601635
zxdg_decoration_manager_v1_destroy(mDecManager);
602636
mDecManager = nullptr;
@@ -665,25 +699,37 @@ GPUDisplayFrontendWayland::OutputInfo* GPUDisplayFrontendWayland::findOutput(wl_
665699

666700
void GPUDisplayFrontendWayland::updateScale()
667701
{
668-
int32_t scale = mPreferredScale;
669-
if (scale == 0) {
670-
scale = 1;
671-
for (auto* output : mSurfaceOutputs) {
672-
if (auto* o = findOutput(output)) {
673-
scale = std::max(scale, o->scale);
702+
double scale;
703+
if (mFractionalScale) {
704+
scale = mFractionalScale120 ? mFractionalScale120 / 120. : 1.;
705+
} else {
706+
int32_t intScale = mPreferredScale;
707+
if (intScale == 0) {
708+
intScale = 1;
709+
for (auto* output : mSurfaceOutputs) {
710+
if (auto* o = findOutput(output)) {
711+
intScale = std::max(intScale, o->scale);
712+
}
674713
}
675714
}
715+
scale = intScale;
676716
}
677717
if (scale != mScale && mSurface) {
678718
mScale = scale;
679-
wl_surface_set_buffer_scale(mSurface, mScale); // Applied with the next commit, i.e. the next present, which happens after the swapchain was recreated with the new size by ResizeScene
719+
if (!mFractionalScale) {
720+
wl_surface_set_buffer_scale(mSurface, (int32_t)mScale); // Applied with the next commit, i.e. the next present, which happens after the swapchain was recreated with the new size by ResizeScene
721+
}
680722
updateSize();
681723
}
682724
}
683725

684726
void GPUDisplayFrontendWayland::updateSize()
685727
{
686-
const int32_t width = mWidthLogical * mScale, height = mHeightLogical * mScale;
728+
if (mViewport) {
729+
wp_viewport_set_destination(mViewport, mWidthLogical, mHeightLogical); // Surface size in logical coordinates, independent of the buffer size, applied with the next present
730+
}
731+
const int32_t width = mFractionalScale ? (int32_t)std::lround(mWidthLogical * mScale) : (mWidthLogical * (int32_t)mScale);
732+
const int32_t height = mFractionalScale ? (int32_t)std::lround(mHeightLogical * mScale) : (mHeightLogical * (int32_t)mScale);
687733
if (width != mDisplayWidth || height != mDisplayHeight) {
688734
if (mDisplayRunning) {
689735
ResizeScene(width, height); // Swapchain is recreated by the backend, Vulkan WSI commits the new buffer

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
#include <pthread.h>
2020
#include <wayland-client.h>
2121
#include "xdg-shell-client-protocol.h"
22+
#include "fractional-scale-client-protocol.h"
2223
#include <vector>
2324
#include <string>
2425

2526
struct zxdg_decoration_manager_v1;
27+
struct wp_viewporter;
28+
struct wp_viewport;
2629
struct zxdg_toplevel_decoration_v1;
2730
struct xkb_context;
2831
struct xkb_keymap;
@@ -49,7 +52,7 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend
4952
private:
5053
int32_t FrontendMain() override;
5154
int32_t GetKey(uint32_t key, uint32_t state);
52-
void updateScale(); // Buffer scale from wl_surface.preferred_buffer_scale, or max scale of the outputs the surface is on (older compositors)
55+
void updateScale(); // Scale from wp_fractional_scale_v1, else integer wl_surface.preferred_buffer_scale, or max scale of the outputs the surface is on (older compositors)
5356
void updateSize(); // Physical size = logical size * scale, resizes the scene if changed
5457
void reportError(const char* where); // Print the Wayland display error (protocol error with interface and object, or errno) after a call returned -1
5558
void handleKeyRepeat(); // Call HandleKey for the held key according to wl_keyboard.repeat_info
@@ -88,6 +91,7 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend
8891
xdg_wm_base_listener mXdgWmBaseListener{};
8992
xdg_surface_listener mXdgSurfaceListener{};
9093
xdg_toplevel_listener mXdgToplevelListener{};
94+
wp_fractional_scale_v1_listener mFractionalScaleListener{};
9195

9296
std::vector<OutputInfo> mOutputs;
9397
std::vector<wl_output*> mSurfaceOutputs; // Outputs the surface is on, most recently entered last
@@ -100,6 +104,11 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend
100104
zxdg_decoration_manager_v1* mDecManager = nullptr;
101105
zxdg_toplevel_decoration_v1* mXdgDecoration = nullptr;
102106

107+
wp_viewporter* mViewporter = nullptr; // Fractional scaling (if both are available): buffer scale 1, buffer at physical size, viewport destination = logical size
108+
wp_fractional_scale_manager_v1* mFractionalScaleManager = nullptr;
109+
wp_viewport* mViewport = nullptr;
110+
wp_fractional_scale_v1* mFractionalScale = nullptr;
111+
103112
int32_t mWidthRequested = 0;
104113
int32_t mHeightRequested = 0;
105114
int32_t mWidthLogical = INIT_WIDTH;
@@ -109,8 +118,9 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend
109118
bool mStateFullScreenOrMaximized = false;
110119
int32_t mBoundsWidth = 0;
111120
int32_t mBoundsHeight = 0;
112-
int32_t mScale = 1;
113-
int32_t mPreferredScale = 0; // 0 = not sent by compositor
121+
double mScale = 1; // Physical pixels per logical pixel
122+
int32_t mPreferredScale = 0; // Integer scale from wl_surface.preferred_buffer_scale, 0 = not sent by compositor
123+
uint32_t mFractionalScale120 = 0; // Scale * 120 from wp_fractional_scale_v1, 0 = not sent yet
114124
bool mSuspended = false;
115125
bool mCanFullscreen = true;
116126
bool mCanMaximize = true;

‎dependencies/FindO2GPUWayland.cmake‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ find_program(${PKGNAME}_SCANNER wayland-scanner)
2525
find_package(PkgConfig)
2626
if(PkgConfig_FOUND)
2727
pkg_get_variable(${PKGNAME}_PROTOCOLS_DIR wayland-protocols pkgdatadir)
28-
if(NOT ${PKGNAME}_PROTOCOLS_DIR OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml")
28+
if(NOT ${PKGNAME}_PROTOCOLS_DIR OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/stable/viewporter/viewporter.xml" OR NOT EXISTS "${${PKGNAME}_PROTOCOLS_DIR}/staging/fractional-scale/fractional-scale-v1.xml")
2929
message(STATUS "wayland xdg protocol missing in ${${PKGNAME}_PROTOCOLS_DIR}")
3030
unset(${PKGNAME}_PROTOCOLS_DIR)
3131
endif()

0 commit comments

Comments
 (0)