From 3bb7ec638187168e739a721a947bedc97f6d326b Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:03:50 +0800 Subject: [PATCH 01/18] feat: basic scene with slider --- providers/base/data/vrr_rectangles_test.qml | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 providers/base/data/vrr_rectangles_test.qml diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml new file mode 100644 index 0000000000..e54d6bd610 --- /dev/null +++ b/providers/base/data/vrr_rectangles_test.qml @@ -0,0 +1,96 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Controls 1.4 + +Window { + id: root + width: 800 + height: 600 + visible: true + title: "Dynamic Refresh Rate Demo" + color: "#1a1a1a" + + property int targetFps: 10 + property int rectCount: 10 + + Timer { + interval: 1000 / targetFps + running: true + repeat: true + onTriggered: { + for (let i = 0; i < rectContainer.children.length; i++) { + let rect = rectContainer.children[i]; + if (rect.updatePosition) { + rect.updatePosition(); + } + } + } + } + + // Container for the rectangles + Item { + id: rectContainer + anchors.fill: parent + + Repeater { + model: rectCount + Rectangle { + width: 30; height: 30 + color: Qt.hsla(Math.random(), 0.6, 0.6, 0.9) + radius: 4 + + // State variables for manual movement + property real vx: (Math.random() - 0.5) * 10 + property real vy: (Math.random() - 0.5) * 10 + + Component.onCompleted: { + x = Math.random() * (root.width - width); + y = Math.random() * (root.height - height); + } + + // Custom function called by the Timer + function updatePosition() { + x += vx; + y += vy; + + // Bounce logic + if (x <= 0 || x >= root.width - width) vx *= -1; + if (y <= 0 || y >= root.height - height) vy *= -1; + } + } + } + } + + // Control Panel + Rectangle { + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: 320; height: 80 + color: "#cc000000" + radius: 10 + border.color: "white" + anchors.margins: 20 + + Column { + anchors.centerIn: parent + spacing: 5 + + Text { + text: "Refresh Rate: " + targetFps + " FPS" + color: "white" + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + } + + Slider { + minimumValue: 48 + maximumValue: 120 + value: 60 + // onMoved: targetFps = value + onValueChanged: { + targetFps = Math.floor(value) + } + } + } + } +} \ No newline at end of file From 93eab0fe9bef2a56ee3ae72652b0d5b9412eb372 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:16:09 +0800 Subject: [PATCH 02/18] fix: add quit button --- providers/base/data/vrr_rectangles_test.qml | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index e54d6bd610..8543c2e67b 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -13,6 +13,18 @@ Window { property int targetFps: 10 property int rectCount: 10 + // old QT workaround, there's no 'Shortcuts' property on Window + // in new QTs don't do this + Item { + anchors.fill: parent + focus: true + Keys.onPressed: { + if (event.key === Qt.Key_Escape) { + root.close() + } + } + } + Timer { interval: 1000 / targetFps running: true @@ -63,9 +75,11 @@ Window { // Control Panel Rectangle { + id: "panel" anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - width: 320; height: 80 + width: 400 + height: 180 color: "#cc000000" radius: 10 border.color: "white" @@ -74,7 +88,14 @@ Window { Column { anchors.centerIn: parent spacing: 5 + // anchors.margins: 5 + Text { + text: "Press Esc to quit" + color: "grey" + anchors.horizontalCenter: parent.horizontalCenter + } + Text { text: "Refresh Rate: " + targetFps + " FPS" color: "white" From 7e144e64399154ec1027a3cafa6b0a1db18cbefc Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 14:16:10 +0800 Subject: [PATCH 03/18] fix: normalize rect speed against fps --- providers/base/data/vrr_rectangles_test.qml | 37 +++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 8543c2e67b..397066333c 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -10,8 +10,8 @@ Window { title: "Dynamic Refresh Rate Demo" color: "#1a1a1a" - property int targetFps: 10 - property int rectCount: 10 + property int targetFps: 60 + property int rectCount: 5 // old QT workaround, there's no 'Shortcuts' property on Window // in new QTs don't do this @@ -47,13 +47,12 @@ Window { Repeater { model: rectCount Rectangle { - width: 30; height: 30 + width: 50; height: 50 color: Qt.hsla(Math.random(), 0.6, 0.6, 0.9) radius: 4 - // State variables for manual movement - property real vx: (Math.random() - 0.5) * 10 - property real vy: (Math.random() - 0.5) * 10 + property real vx: (Math.random() - 0.5) * 500 + property real vy: (Math.random() - 0.5) * 500 Component.onCompleted: { x = Math.random() * (root.width - width); @@ -62,8 +61,8 @@ Window { // Custom function called by the Timer function updatePosition() { - x += vx; - y += vy; + x += vx/targetFps; + y += vy/targetFps; // Bounce logic if (x <= 0 || x >= root.width - width) vx *= -1; @@ -78,8 +77,8 @@ Window { id: "panel" anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - width: 400 - height: 180 + width: 600 + height: 300 color: "#cc000000" radius: 10 border.color: "white" @@ -88,7 +87,6 @@ Window { Column { anchors.centerIn: parent spacing: 5 - // anchors.margins: 5 Text { text: "Press Esc to quit" @@ -96,6 +94,18 @@ Window { anchors.horizontalCenter: parent.horizontalCenter } + Text { + text: "This test should be run at fullscreen" + color: "grey" + anchors.horizontalCenter: parent.horizontalCenter + } + + Text { + text: "Set GALLIUM_HUD=fps vblank_mode=0" + color: "grey" + anchors.horizontalCenter: parent.horizontalCenter + } + Text { text: "Refresh Rate: " + targetFps + " FPS" color: "white" @@ -104,10 +114,9 @@ Window { } Slider { - minimumValue: 48 - maximumValue: 120 + minimumValue: 20 + maximumValue: 200 value: 60 - // onMoved: targetFps = value onValueChanged: { targetFps = Math.floor(value) } From 9ee32372912cee2bc871f80b6911f4598e2d5769 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 14:30:47 +0800 Subject: [PATCH 04/18] style: better text box, add a num rect slider --- providers/base/data/vrr_rectangles_test.qml | 48 ++++++++++++++++----- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 397066333c..1ac324c556 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -47,7 +47,8 @@ Window { Repeater { model: rectCount Rectangle { - width: 50; height: 50 + width: Screen.width * 0.05 + height: Screen.width * 0.05 color: Qt.hsla(Math.random(), 0.6, 0.6, 0.9) radius: 4 @@ -61,8 +62,8 @@ Window { // Custom function called by the Timer function updatePosition() { - x += vx/targetFps; - y += vy/targetFps; + x += vx / targetFps; + y += vy / targetFps; // Bounce logic if (x <= 0 || x >= root.width - width) vx *= -1; @@ -77,33 +78,42 @@ Window { id: "panel" anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - width: 600 - height: 300 + width: contentColumn.implicitWidth + 50 + height: contentColumn.implicitHeight + 50 color: "#cc000000" - radius: 10 + radius: 12 border.color: "white" anchors.margins: 20 Column { + id: contentColumn anchors.centerIn: parent spacing: 5 Text { text: "Press Esc to quit" - color: "grey" - anchors.horizontalCenter: parent.horizontalCenter + color: "white" + font.bold: true + anchors.horizontalCenter: parent.horizontalLeft } Text { text: "This test should be run at fullscreen" color: "grey" - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent.horizontalLeft } Text { text: "Set GALLIUM_HUD=fps vblank_mode=0" color: "grey" - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent.horizontalLeft + } + + Text { + text: "Look for tearing ONLY. Any stutter or fps mismatch is OK." + color: "red" + font.bold: true + anchors.horizontalCenter: parent.horizontalLeft } Text { @@ -120,6 +130,24 @@ Window { onValueChanged: { targetFps = Math.floor(value) } + anchors.horizontalCenter: parent.horizontalCenter + } + + Text { + text: "Number of rectangles: " + rectCount + color: "white" + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + } + + Slider { + minimumValue: 1 + maximumValue: 10 + value: rectCount + onValueChanged: { + rectCount = value + } + anchors.horizontalCenter: parent.horizontalCenter } } } From a138db968d633a3ad730cf4dd03f202a10074132 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:34:17 +0800 Subject: [PATCH 05/18] feat: simple test plan --- providers/base/units/graphics/test-plan.pxu | 6 +++++ providers/base/units/graphics/vrr.yaml | 28 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 providers/base/units/graphics/vrr.yaml diff --git a/providers/base/units/graphics/test-plan.pxu b/providers/base/units/graphics/test-plan.pxu index 6976dc2232..9be9b564d3 100644 --- a/providers/base/units/graphics/test-plan.pxu +++ b/providers/base/units/graphics/test-plan.pxu @@ -409,3 +409,9 @@ include: bootstrap_include: graphics_card executable + +id: vrr-test-plan +unit: test plan +_name: Sample Variable-Refresh-Rate Test Plan +include: + graphics/variable-refresh-rate* diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml new file mode 100644 index 0000000000..bf60475c95 --- /dev/null +++ b/providers/base/units/graphics/vrr.yaml @@ -0,0 +1,28 @@ +unit: job +plugin: user-interact-verify +category_id: com.canonical.plainbox::graphics +id: graphics/variable-refresh-rate +user: root +flags: + - also-after-suspend +imports: + - from com.canonical.plainbox import manifest +requires: + - executable.name == 'qmlscene' + - manifest.has_vrr_support == 'True' +command: GALLIUM_HUD=fps vblank_mode=0 qmlscene vrr_rectangles_test.qml --fullscreen +estimated_duration: "3600s" +summary: This test checks if Variable-Refresh-Rate (VRR) is working on the built-in display +steps: | + 1. Press Enter to start the test program + 2. Inside the test program, you will see multiple rectangles bouncing inside the window. + Adjust the FPS slider and look for screen tearing. + It's OK for the fps meter to not match the requested FPS. + 3. If the DUT appears to be struggling to render all the rectangles, + use the bottom slider to adjust the number of squares. + 4. Mark as pass if no tearing was observed, fail otherwise. +--- +unit: manifest entry +id: has_vrr_support +name: Does the device have a built-in display with Variable-Refresh-Rate? +value-type: bool From e3e71200293154d102bc1b06fd6522d62c3a6228 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:34:44 +0800 Subject: [PATCH 06/18] fix: duration --- providers/base/units/graphics/vrr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index bf60475c95..dc7760dca4 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -11,7 +11,7 @@ requires: - executable.name == 'qmlscene' - manifest.has_vrr_support == 'True' command: GALLIUM_HUD=fps vblank_mode=0 qmlscene vrr_rectangles_test.qml --fullscreen -estimated_duration: "3600s" +estimated_duration: "20s" summary: This test checks if Variable-Refresh-Rate (VRR) is working on the built-in display steps: | 1. Press Enter to start the test program From 1819cc8e9eecf5965126f5fc9347520e71aaac22 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:52:45 +0800 Subject: [PATCH 07/18] style: description changes --- providers/base/units/graphics/vrr.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index dc7760dca4..19d8b1fda6 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -17,12 +17,13 @@ steps: | 1. Press Enter to start the test program 2. Inside the test program, you will see multiple rectangles bouncing inside the window. Adjust the FPS slider and look for screen tearing. - It's OK for the fps meter to not match the requested FPS. + It's OK for the FPS meter to not match the requested FPS. 3. If the DUT appears to be struggling to render all the rectangles, use the bottom slider to adjust the number of squares. - 4. Mark as pass if no tearing was observed, fail otherwise. + 4. Press Esc to quit the program + 5. Mark as pass if no tearing was observed, fail otherwise. --- unit: manifest entry id: has_vrr_support -name: Does the device have a built-in display with Variable-Refresh-Rate? +name: Does the device have a built-in display with Variable Refresh Rate (VRR)? value-type: bool From 81fe257f1f9a7d66a4a037aa03f3bb05073716ad Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:58:36 +0800 Subject: [PATCH 08/18] fix: remove root --- providers/base/units/graphics/vrr.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index 19d8b1fda6..38c7e17e99 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -2,7 +2,6 @@ unit: job plugin: user-interact-verify category_id: com.canonical.plainbox::graphics id: graphics/variable-refresh-rate -user: root flags: - also-after-suspend imports: @@ -19,7 +18,7 @@ steps: | Adjust the FPS slider and look for screen tearing. It's OK for the FPS meter to not match the requested FPS. 3. If the DUT appears to be struggling to render all the rectangles, - use the bottom slider to adjust the number of squares. + use the bottom slider to adjust the amount. 4. Press Esc to quit the program 5. Mark as pass if no tearing was observed, fail otherwise. --- From 17b4488913f33943054d6bfd0b2522362afe08b9 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:27:53 +0800 Subject: [PATCH 09/18] fix: add vulkan vsync var in test job --- providers/base/data/vrr_rectangles_test.qml | 2 +- providers/base/units/graphics/vrr.yaml | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 1ac324c556..34b114d262 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -104,7 +104,7 @@ Window { } Text { - text: "Set GALLIUM_HUD=fps vblank_mode=0" + text: "Set GALLIUM_HUD=fps vblank_mode=0 MESA_VK_WSI_PRESENT_MODE=immediate" color: "grey" anchors.horizontalCenter: parent.horizontalLeft } diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index 38c7e17e99..25c62b213a 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -9,7 +9,13 @@ imports: requires: - executable.name == 'qmlscene' - manifest.has_vrr_support == 'True' -command: GALLIUM_HUD=fps vblank_mode=0 qmlscene vrr_rectangles_test.qml --fullscreen +# the hud shows an fps graph over time +# vblank turns off vsync for opengl +# https://dri.freedesktop.org/wiki/ConfigurationOptions/ +# MESA_VK_WSI_PRESENT_MODE turns off vsync for vulkan +# https://docs.mesa3d.org/envvars.html#envvar-MESA_VK_WSI_PRESENT_MODE +# vblank is ignored by vulkan and the MESA_VK var is ignored by opengl => they don't conflict each other +command: GALLIUM_HUD=fps MESA_VK_WSI_PRESENT_MODE=immediate vblank_mode=0 qmlscene vrr_rectangles_test.qml --fullscreen estimated_duration: "20s" summary: This test checks if Variable-Refresh-Rate (VRR) is working on the built-in display steps: | From d44836a663b6458ff25f16cbadfdf9b21f46fce4 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:32:13 +0800 Subject: [PATCH 10/18] style: description --- providers/base/data/vrr_rectangles_test.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 34b114d262..8429ff74db 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -77,7 +77,7 @@ Window { Rectangle { id: "panel" anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent.horizontalLeft width: contentColumn.implicitWidth + 50 height: contentColumn.implicitHeight + 50 color: "#cc000000" @@ -117,7 +117,7 @@ Window { } Text { - text: "Refresh Rate: " + targetFps + " FPS" + text: "Requested Refresh Rate: " + targetFps + " FPS" color: "white" font.bold: true anchors.horizontalCenter: parent.horizontalCenter From 10e3c8501eca00fbb5a0e7ba6b97ff9811d58e66 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:46:27 +0800 Subject: [PATCH 11/18] fix: enable vsync --- providers/base/data/vrr_rectangles_test.qml | 22 ++++++++++++++++----- providers/base/units/graphics/vrr.yaml | 6 +++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 8429ff74db..4f99594eba 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -30,8 +30,14 @@ Window { running: true repeat: true onTriggered: { + // manually update the positions + // this also implicitly changes the framerate to the requested one for (let i = 0; i < rectContainer.children.length; i++) { let rect = rectContainer.children[i]; + // this QT version doesn't support optional chaining + // don't use rect?.updatePosition?.() + // this also must be checked + // since it doesn't exist on the very 1st frame if (rect.updatePosition) { rect.updatePosition(); } @@ -52,6 +58,7 @@ Window { color: Qt.hsla(Math.random(), 0.6, 0.6, 0.9) radius: 4 + // velocity x and y property real vx: (Math.random() - 0.5) * 500 property real vy: (Math.random() - 0.5) * 500 @@ -62,12 +69,17 @@ Window { // Custom function called by the Timer function updatePosition() { + // normalize position change w.r.t. fps x += vx / targetFps; y += vy / targetFps; - // Bounce logic - if (x <= 0 || x >= root.width - width) vx *= -1; - if (y <= 0 || y >= root.height - height) vy *= -1; + // bounce at the walls + if (x <= 0 || x >= root.width - width) { + vx *= -1; + } + if (y <= 0 || y >= root.height - height) { + vy *= -1 + } } } } @@ -104,7 +116,7 @@ Window { } Text { - text: "Set GALLIUM_HUD=fps vblank_mode=0 MESA_VK_WSI_PRESENT_MODE=immediate" + text: "Set GALLIUM_HUD=fps vblank_mode=3 MESA_VK_WSI_PRESENT_MODE=relaxed" color: "grey" anchors.horizontalCenter: parent.horizontalLeft } @@ -124,7 +136,7 @@ Window { } Slider { - minimumValue: 20 + minimumValue: 10 maximumValue: 200 value: 60 onValueChanged: { diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index 25c62b213a..77fbc0dc5f 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -10,12 +10,12 @@ requires: - executable.name == 'qmlscene' - manifest.has_vrr_support == 'True' # the hud shows an fps graph over time -# vblank turns off vsync for opengl +# vblank=3 forces vsync for opengl # https://dri.freedesktop.org/wiki/ConfigurationOptions/ -# MESA_VK_WSI_PRESENT_MODE turns off vsync for vulkan +# MESA_VK_WSI_PRESENT_MODE=relaxed is the relaxed vsync for vulkan # https://docs.mesa3d.org/envvars.html#envvar-MESA_VK_WSI_PRESENT_MODE # vblank is ignored by vulkan and the MESA_VK var is ignored by opengl => they don't conflict each other -command: GALLIUM_HUD=fps MESA_VK_WSI_PRESENT_MODE=immediate vblank_mode=0 qmlscene vrr_rectangles_test.qml --fullscreen +command: GALLIUM_HUD=fps MESA_VK_WSI_PRESENT_MODE=relaxed vblank_mode=3 qmlscene vrr_rectangles_test.qml --fullscreen estimated_duration: "20s" summary: This test checks if Variable-Refresh-Rate (VRR) is working on the built-in display steps: | From 91bcc2463d472300789ee900eeb6331233529eb5 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 11:49:09 +0800 Subject: [PATCH 12/18] fix: copilot suggestions --- providers/base/data/vrr_rectangles_test.qml | 20 ++++++++++---------- providers/base/units/graphics/vrr.yaml | 8 ++++++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 4f99594eba..4aa5899a3c 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -4,8 +4,8 @@ import QtQuick.Controls 1.4 Window { id: root - width: 800 - height: 600 + width: 1280 + height: 720 visible: true title: "Dynamic Refresh Rate Demo" color: "#1a1a1a" @@ -87,15 +87,15 @@ Window { // Control Panel Rectangle { - id: "panel" + id: panel anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalLeft + anchors.horizontalCenter: parent.horizontalCenter width: contentColumn.implicitWidth + 50 height: contentColumn.implicitHeight + 50 color: "#cc000000" radius: 12 border.color: "white" - anchors.margins: 20 + anchors.margins: 10 Column { id: contentColumn @@ -106,26 +106,26 @@ Window { text: "Press Esc to quit" color: "white" font.bold: true - anchors.horizontalCenter: parent.horizontalLeft + anchors.left: parent.left } Text { text: "This test should be run at fullscreen" color: "grey" - anchors.horizontalCenter: parent.horizontalLeft + anchors.left: parent.left } Text { text: "Set GALLIUM_HUD=fps vblank_mode=3 MESA_VK_WSI_PRESENT_MODE=relaxed" color: "grey" - anchors.horizontalCenter: parent.horizontalLeft + anchors.left: parent.left } Text { text: "Look for tearing ONLY. Any stutter or fps mismatch is OK." color: "red" font.bold: true - anchors.horizontalCenter: parent.horizontalLeft + anchors.left: parent.left } Text { @@ -157,7 +157,7 @@ Window { maximumValue: 10 value: rectCount onValueChanged: { - rectCount = value + rectCount = Math.floor(value) } anchors.horizontalCenter: parent.horizontalCenter } diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index 77fbc0dc5f..db3e5ef205 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -10,12 +10,16 @@ requires: - executable.name == 'qmlscene' - manifest.has_vrr_support == 'True' # the hud shows an fps graph over time -# vblank=3 forces vsync for opengl +# vblank_mode=3 forces vsync for opengl # https://dri.freedesktop.org/wiki/ConfigurationOptions/ # MESA_VK_WSI_PRESENT_MODE=relaxed is the relaxed vsync for vulkan # https://docs.mesa3d.org/envvars.html#envvar-MESA_VK_WSI_PRESENT_MODE # vblank is ignored by vulkan and the MESA_VK var is ignored by opengl => they don't conflict each other -command: GALLIUM_HUD=fps MESA_VK_WSI_PRESENT_MODE=relaxed vblank_mode=3 qmlscene vrr_rectangles_test.qml --fullscreen +command: >- + GALLIUM_HUD=fps + MESA_VK_WSI_PRESENT_MODE=relaxed + vblank_mode=3 + qmlscene "$PLAINBOX_PROVIDER_DATA"/vrr_rectangles_test.qml --fullscreen estimated_duration: "20s" summary: This test checks if Variable-Refresh-Rate (VRR) is working on the built-in display steps: | From 5e5cf2630003d5cef0d6ebf1ce905e761b33aba2 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 11:59:16 +0800 Subject: [PATCH 13/18] feat: add buttons --- providers/base/data/vrr_rectangles_test.qml | 73 ++++++++++++++++----- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 4aa5899a3c..2f36196d22 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 Window { id: root @@ -12,6 +13,8 @@ Window { property int targetFps: 60 property int rectCount: 5 + property int minFps: 10 + property int maxFps: 200 // old QT workaround, there's no 'Shortcuts' property on Window // in new QTs don't do this @@ -95,7 +98,7 @@ Window { color: "#cc000000" radius: 12 border.color: "white" - anchors.margins: 10 + anchors.margins: 10 Column { id: contentColumn @@ -135,14 +138,34 @@ Window { anchors.horizontalCenter: parent.horizontalCenter } - Slider { - minimumValue: 10 - maximumValue: 200 - value: 60 - onValueChanged: { - targetFps = Math.floor(value) - } + RowLayout { + Layout.minimumHeight: 10 + Layout.fillWidth: true anchors.horizontalCenter: parent.horizontalCenter + Button { + text: '-' + Layout.fillHeight: true + Layout.fillWidth: true + onClicked: { + targetFps = Math.max(minFps, targetFps - 1) + } + } + Slider { + minimumValue: minFps + maximumValue: maxFps + value: targetFps + onValueChanged: { + targetFps = Math.floor(value) + } + } + Button { + text: '+' + Layout.fillHeight: true + Layout.fillWidth: true + onClicked: { + targetFps = Math.min(maxFps, targetFps + 1) + } + } } Text { @@ -152,14 +175,34 @@ Window { anchors.horizontalCenter: parent.horizontalCenter } - Slider { - minimumValue: 1 - maximumValue: 10 - value: rectCount - onValueChanged: { - rectCount = Math.floor(value) - } + RowLayout { + Layout.minimumHeight: 10 + Layout.fillWidth: true anchors.horizontalCenter: parent.horizontalCenter + Button { + text: '-' + Layout.fillHeight: true + Layout.fillWidth: true + onClicked: { + rectCount = Math.max(1, rectCount - 1) + } + } + Slider { + minimumValue: 1 + maximumValue: 10 + value: rectCount + onValueChanged: { + rectCount = Math.floor(value) + } + } + Button { + text: '+' + Layout.fillHeight: true + Layout.fillWidth: true + onClicked: { + rectCount = Math.min(10, rectCount + 1) + } + } } } } From b9b6333f3db073303477da067b484ce3367cfdcf Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 13:57:36 +0800 Subject: [PATCH 14/18] fix: bad regex --- providers/base/data/vrr_rectangles_test.qml | 19 +++++++++++++++++++ providers/base/units/graphics/test-plan.pxu | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 2f36196d22..dfc4371b2d 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -1,3 +1,22 @@ +/* This file is part of Checkbox. + + Copyright 2026 Canonical Ltd. + Written by: + Zhongning Li + + Checkbox is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, + as published by the Free Software Foundation. + + Checkbox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Checkbox. If not, see . +*/ + import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 1.4 diff --git a/providers/base/units/graphics/test-plan.pxu b/providers/base/units/graphics/test-plan.pxu index 9be9b564d3..3d747d8186 100644 --- a/providers/base/units/graphics/test-plan.pxu +++ b/providers/base/units/graphics/test-plan.pxu @@ -414,4 +414,4 @@ id: vrr-test-plan unit: test plan _name: Sample Variable-Refresh-Rate Test Plan include: - graphics/variable-refresh-rate* + graphics/variable-refresh-rate.* From f53df3de309ff9922e11ce140d004e396164bd94 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:41:52 +0800 Subject: [PATCH 15/18] fix: use var not let --- providers/base/data/vrr_rectangles_test.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index dfc4371b2d..39db3afc20 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -54,8 +54,10 @@ Window { onTriggered: { // manually update the positions // this also implicitly changes the framerate to the requested one - for (let i = 0; i < rectContainer.children.length; i++) { - let rect = rectContainer.children[i]; + for (var i = 0; i < rectContainer.children.length; i++) { + // must use var here, even if it's the root of all evils in js + // otherwise older QT won't understand it + var rect = rectContainer.children[i]; // this QT version doesn't support optional chaining // don't use rect?.updatePosition?.() // this also must be checked From c52b87b769534898cf3bc8ac8ed0c745cf3e7718 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:59:28 +0800 Subject: [PATCH 16/18] refactor: move min max rect count to property --- providers/base/data/vrr_rectangles_test.qml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/providers/base/data/vrr_rectangles_test.qml b/providers/base/data/vrr_rectangles_test.qml index 39db3afc20..5033975ca9 100644 --- a/providers/base/data/vrr_rectangles_test.qml +++ b/providers/base/data/vrr_rectangles_test.qml @@ -32,8 +32,12 @@ Window { property int targetFps: 60 property int rectCount: 5 + property int minFps: 10 - property int maxFps: 200 + property int maxFps: 200 + + property int minRectCount: 1 + property int maxRectCount: 10 // old QT workaround, there's no 'Shortcuts' property on Window // in new QTs don't do this @@ -55,12 +59,12 @@ Window { // manually update the positions // this also implicitly changes the framerate to the requested one for (var i = 0; i < rectContainer.children.length; i++) { - // must use var here, even if it's the root of all evils in js + // must use 'var' here, not let/const like modern js // otherwise older QT won't understand it var rect = rectContainer.children[i]; // this QT version doesn't support optional chaining // don't use rect?.updatePosition?.() - // this also must be checked + // method existence also must be checked // since it doesn't exist on the very 1st frame if (rect.updatePosition) { rect.updatePosition(); @@ -205,7 +209,7 @@ Window { Layout.fillHeight: true Layout.fillWidth: true onClicked: { - rectCount = Math.max(1, rectCount - 1) + rectCount = Math.max(minRectCount, rectCount - 1) } } Slider { @@ -221,7 +225,7 @@ Window { Layout.fillHeight: true Layout.fillWidth: true onClicked: { - rectCount = Math.min(10, rectCount + 1) + rectCount = Math.min(maxRectCount, rectCount + 1) } } } From ab5a953b3b0397ddbe67dea02ce78dc26af44287 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:06:21 +0800 Subject: [PATCH 17/18] fix: name, restrict to laptop only --- providers/base/units/graphics/vrr.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/base/units/graphics/vrr.yaml b/providers/base/units/graphics/vrr.yaml index db3e5ef205..fc5e3b4379 100644 --- a/providers/base/units/graphics/vrr.yaml +++ b/providers/base/units/graphics/vrr.yaml @@ -1,7 +1,7 @@ unit: job plugin: user-interact-verify category_id: com.canonical.plainbox::graphics -id: graphics/variable-refresh-rate +id: graphics/variable-refresh-rate-manual flags: - also-after-suspend imports: @@ -9,6 +9,7 @@ imports: requires: - executable.name == 'qmlscene' - manifest.has_vrr_support == 'True' + - dmi.sane_product == 'portable' # don't run this if there's no built-in screen # the hud shows an fps graph over time # vblank_mode=3 forces vsync for opengl # https://dri.freedesktop.org/wiki/ConfigurationOptions/ From 3fb90f61327e7d31688b64716cd2ea59b2b5903a Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:08:06 +0800 Subject: [PATCH 18/18] fix: wording --- providers/base/units/graphics/test-plan.pxu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/base/units/graphics/test-plan.pxu b/providers/base/units/graphics/test-plan.pxu index 3d747d8186..fce3e0f70d 100644 --- a/providers/base/units/graphics/test-plan.pxu +++ b/providers/base/units/graphics/test-plan.pxu @@ -412,6 +412,6 @@ bootstrap_include: id: vrr-test-plan unit: test plan -_name: Sample Variable-Refresh-Rate Test Plan +_name: Variable-Refresh-Rate Test Plan include: graphics/variable-refresh-rate.*