Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions providers/base/data/vrr_rectangles_test.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/* This file is part of Checkbox.

Copyright 2026 Canonical Ltd.
Written by:
Zhongning Li <zhongning.li@canonical.com>

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 <http://www.gnu.org/licenses/>.
*/

import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2

Comment thread
tomli380576 marked this conversation as resolved.
Window {
id: root
width: 1280
height: 720
visible: true
title: "Dynamic Refresh Rate Demo"
color: "#1a1a1a"

property int targetFps: 60
property int rectCount: 5

property int minFps: 10
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
Item {
anchors.fill: parent
focus: true
Keys.onPressed: {
if (event.key === Qt.Key_Escape) {
root.close()
}
}
}

Timer {
interval: 1000 / targetFps
running: true
repeat: true
onTriggered: {
// 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, 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?.()
// method existence also must be checked
// since it doesn't exist on the very 1st frame
if (rect.updatePosition) {
rect.updatePosition();
}
}
}
}

// Container for the rectangles
Item {
id: rectContainer
anchors.fill: parent

Repeater {
model: rectCount
Rectangle {
width: Screen.width * 0.05
height: Screen.width * 0.05
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

Component.onCompleted: {
x = Math.random() * (root.width - width);
y = Math.random() * (root.height - height);
}

// Custom function called by the Timer
function updatePosition() {
// normalize position change w.r.t. fps
x += vx / targetFps;
y += vy / targetFps;

// bounce at the walls
if (x <= 0 || x >= root.width - width) {
vx *= -1;
}
if (y <= 0 || y >= root.height - height) {
vy *= -1
}
}
}
}
}

// Control Panel
Rectangle {
id: panel
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: contentColumn.implicitWidth + 50
height: contentColumn.implicitHeight + 50
color: "#cc000000"
radius: 12
border.color: "white"
anchors.margins: 10

Column {
id: contentColumn
anchors.centerIn: parent
spacing: 5

Text {
text: "Press Esc to quit"
color: "white"
font.bold: true
anchors.left: parent.left
}
Comment thread
tomli380576 marked this conversation as resolved.

Text {
text: "This test should be run at fullscreen"
color: "grey"
anchors.left: parent.left
}
Comment thread
tomli380576 marked this conversation as resolved.

Text {
text: "Set GALLIUM_HUD=fps vblank_mode=3 MESA_VK_WSI_PRESENT_MODE=relaxed"
color: "grey"
anchors.left: parent.left
}

Text {
text: "Look for tearing ONLY. Any stutter or fps mismatch is OK."
color: "red"
font.bold: true
anchors.left: parent.left
}

Text {
text: "Requested Refresh Rate: " + targetFps + " FPS"
color: "white"
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
}

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 {
text: "Number of rectangles: " + rectCount
color: "white"
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
}

RowLayout {
Layout.minimumHeight: 10
Layout.fillWidth: true
anchors.horizontalCenter: parent.horizontalCenter
Button {
text: '-'
Layout.fillHeight: true
Layout.fillWidth: true
onClicked: {
rectCount = Math.max(minRectCount, 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(maxRectCount, rectCount + 1)
}
}
}
}
}
}
6 changes: 6 additions & 0 deletions providers/base/units/graphics/test-plan.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,9 @@ include:
bootstrap_include:
graphics_card
executable

id: vrr-test-plan
unit: test plan
_name: Variable-Refresh-Rate Test Plan
include:
graphics/variable-refresh-rate.*
Comment on lines +413 to +417

Copilot AI Apr 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says this “adds coverage … in 26.04”, but this new VRR job is only referenced by this standalone “Sample Variable-Refresh-Rate Test Plan” and is not included in the existing 26.04 graphics plans (e.g. graphic-base-26-04-manual). If the intent is to make VRR part of the 26.04 graphics coverage, consider including graphics/variable-refresh-rate in the appropriate existing test plan / nested part (or clarify in the description that it’s intentionally opt-in via this sample plan).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(leaving this open for visibility before merge)
The test plan will be nested by the 26.04 desktop test plan in the final put-it-all-together PR once the resource job #2465 is landed

39 changes: 39 additions & 0 deletions providers/base/units/graphics/vrr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
unit: job
plugin: user-interact-verify
category_id: com.canonical.plainbox::graphics
id: graphics/variable-refresh-rate-manual
flags:
- also-after-suspend
imports:
- from com.canonical.plainbox import manifest
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/
# 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 "$PLAINBOX_PROVIDER_DATA"/vrr_rectangles_test.qml --fullscreen
Comment thread
tomli380576 marked this conversation as resolved.
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
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 amount.
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 (VRR)?
Comment thread
tomli380576 marked this conversation as resolved.
value-type: bool
Comment thread
tomli380576 marked this conversation as resolved.
Loading