mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-03-10 19:30:38 +01:00
Merge branch 'test-and-bench' into 'master'
Using tests and benchmarks to improve wlr_scene_node_at See merge request wlroots/wlroots!5277
This commit is contained in:
commit
8a70fd74c9
14 changed files with 362 additions and 14 deletions
|
|
@ -35,6 +35,9 @@ tasks:
|
|||
cd wlroots
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- test: |
|
||||
cd wlroots
|
||||
meson test -C build --verbose
|
||||
- build-features-disabled: |
|
||||
cd wlroots
|
||||
meson setup build --reconfigure -Dauto_features=disabled
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ tasks:
|
|||
- clang: |
|
||||
cd wlroots/build-clang
|
||||
ninja
|
||||
- test: |
|
||||
cd wlroots/build-gcc
|
||||
meson test --verbose
|
||||
meson test --benchmark --verbose
|
||||
- smoke-test: |
|
||||
cd wlroots/build-gcc/tinywl
|
||||
sudo modprobe vkms
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ tasks:
|
|||
meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dallocators=gbm
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- test: |
|
||||
cd wlroots
|
||||
meson test -C build --verbose
|
||||
- tinywl: |
|
||||
cd wlroots/tinywl
|
||||
make
|
||||
|
|
|
|||
|
|
@ -107,6 +107,13 @@ void wlr_fbox_transform(struct wlr_fbox *dest, const struct wlr_fbox *box,
|
|||
|
||||
#ifdef WLR_USE_UNSTABLE
|
||||
|
||||
/**
|
||||
* Checks whether two boxes intersect.
|
||||
*
|
||||
* Returns false if either box is empty.
|
||||
*/
|
||||
bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b);
|
||||
|
||||
/**
|
||||
* Returns true if the two boxes are equal, false otherwise.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -178,6 +178,10 @@ if get_option('examples')
|
|||
subdir('tinywl')
|
||||
endif
|
||||
|
||||
if get_option('tests')
|
||||
subdir('test')
|
||||
endif
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
pkgconfig.generate(
|
||||
lib_wlr,
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@ option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], v
|
|||
option('allocators', type: 'array', choices: ['auto', 'gbm', 'udmabuf'], value: ['auto'],
|
||||
description: 'Select built-in allocators')
|
||||
option('session', type: 'feature', value: 'auto', description: 'Enable session support')
|
||||
option('tests', type: 'boolean', value: true, description: 'Build tests and benchmarks')
|
||||
option('color-management', type: 'feature', value: 'auto', description: 'Enable support for color management')
|
||||
option('libliftoff', type: 'feature', value: 'auto', description: 'Enable support for libliftoff')
|
||||
|
|
|
|||
154
test/bench_scene.c
Normal file
154
test/bench_scene.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
|
||||
struct tree_spec {
|
||||
// Parameters for the tree we'll construct
|
||||
int depth;
|
||||
int branching;
|
||||
int rect_size;
|
||||
int spread;
|
||||
|
||||
// Stats around the tree we built
|
||||
int tree_count;
|
||||
int rect_count;
|
||||
int max_x;
|
||||
int max_y;
|
||||
};
|
||||
|
||||
static int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static double timespec_diff_msec(struct timespec *start, struct timespec *end) {
|
||||
return (double)(end->tv_sec - start->tv_sec) * 1e3 +
|
||||
(double)(end->tv_nsec - start->tv_nsec) / 1e6;
|
||||
}
|
||||
|
||||
static bool build_tree(struct wlr_scene_tree *parent, struct tree_spec *spec,
|
||||
int depth, int x, int y) {
|
||||
|
||||
if (depth == spec->depth) {
|
||||
float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
struct wlr_scene_rect *rect =
|
||||
wlr_scene_rect_create(parent, spec->rect_size, spec->rect_size, color);
|
||||
if (rect == NULL) {
|
||||
fprintf(stderr, "wlr_scene_rect_create failed\n");
|
||||
return false;
|
||||
}
|
||||
wlr_scene_node_set_position(&rect->node, x, y);
|
||||
spec->max_x = max(spec->max_x, x + spec->rect_size);
|
||||
spec->max_y = max(spec->max_y, y + spec->rect_size);
|
||||
spec->rect_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < spec->branching; i++) {
|
||||
struct wlr_scene_tree *child = wlr_scene_tree_create(parent);
|
||||
if (child == NULL) {
|
||||
fprintf(stderr, "wlr_scene_tree_create failed\n");
|
||||
return false;
|
||||
}
|
||||
spec->tree_count++;
|
||||
int offset = i * spec->spread;
|
||||
wlr_scene_node_set_position(&child->node, offset, offset);
|
||||
if (!build_tree(child, spec, depth + 1, x + offset, y + offset)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bench_create_tree(struct wlr_scene *scene, struct tree_spec *spec) {
|
||||
struct timespec start, end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
if (!build_tree(&scene->tree, spec, 0, 0, 0)) {
|
||||
fprintf(stderr, "build_tree failed\n");
|
||||
return false;
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
|
||||
printf("Built tree with %d tree nodes, %d rect nodes\n\n",
|
||||
spec->tree_count, spec->rect_count);
|
||||
|
||||
double elapsed = timespec_diff_msec(&start, &end);
|
||||
int nodes = spec->tree_count + spec->rect_count;
|
||||
printf("create test tree: %d nodes, %.3f ms, %.0f nodes/ms\n",
|
||||
nodes, elapsed, nodes / elapsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bench_scene_node_at(struct wlr_scene *scene, struct tree_spec *spec) {
|
||||
struct timespec start, end;
|
||||
int iters = 10000;
|
||||
int hits = 0;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
for (int i = 0; i < iters; i++) {
|
||||
// Spread lookups across the tree extent
|
||||
double lx = (double)(i * 97 % spec->max_x);
|
||||
double ly = (double)(i * 53 % spec->max_y);
|
||||
double nx, ny;
|
||||
struct wlr_scene_node *node =
|
||||
wlr_scene_node_at(&scene->tree.node, lx, ly, &nx, &ny);
|
||||
if (node != NULL) {
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
|
||||
double elapsed = timespec_diff_msec(&start, &end);
|
||||
int nodes = (spec->tree_count + spec->rect_count) * iters;
|
||||
printf("wlr_scene_node_at: %d iters, %.3f ms, %.0f nodes/ms (hits: %d/%d)\n",
|
||||
iters, elapsed, nodes / elapsed, hits, iters);
|
||||
}
|
||||
|
||||
static void noop_iterator(struct wlr_scene_buffer *buffer,
|
||||
int sx, int sy, void *user_data) {
|
||||
(void)buffer;
|
||||
(void)sx;
|
||||
(void)sy;
|
||||
int *cnt = user_data;
|
||||
(*cnt)++;
|
||||
}
|
||||
|
||||
static void bench_scene_node_for_each_buffer(struct wlr_scene *scene, struct tree_spec *spec) {
|
||||
struct timespec start, end;
|
||||
int iters = 10000;
|
||||
int hits = 0;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
for (int i = 0; i < iters; i++) {
|
||||
wlr_scene_node_for_each_buffer(&scene->tree.node,
|
||||
noop_iterator, &hits);
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
|
||||
double elapsed = timespec_diff_msec(&start, &end);
|
||||
int nodes = (spec->tree_count + spec->rect_count) * iters;
|
||||
printf("wlr_scene_node_for_each_buffer: %d iters, %.3f ms, %.0f nodes/ms (hits: %d/%d)\n",
|
||||
iters, elapsed, nodes / elapsed, hits, iters);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct wlr_scene *scene = wlr_scene_create();
|
||||
if (scene == NULL) {
|
||||
fprintf(stderr, "wlr_scene_create failed\n");
|
||||
return 99;
|
||||
}
|
||||
|
||||
struct tree_spec spec = {
|
||||
.depth = 5,
|
||||
.branching = 5,
|
||||
.rect_size = 10,
|
||||
.spread = 100,
|
||||
};
|
||||
if (!bench_create_tree(scene, &spec)) {
|
||||
return 99;
|
||||
}
|
||||
bench_scene_node_at(scene, &spec);
|
||||
bench_scene_node_for_each_buffer(scene, &spec);
|
||||
|
||||
wlr_scene_node_destroy(&scene->tree.node);
|
||||
return 0;
|
||||
}
|
||||
10
test/meson.build
Normal file
10
test/meson.build
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
test(
|
||||
'box',
|
||||
executable('test-box', 'test_box.c', dependencies: wlroots),
|
||||
)
|
||||
|
||||
benchmark(
|
||||
'scene',
|
||||
executable('bench-scene', 'bench_scene.c', dependencies: wlroots),
|
||||
timeout: 30,
|
||||
)
|
||||
149
test/test_box.c
Normal file
149
test/test_box.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <wlr/util/box.h>
|
||||
|
||||
static void test_box_empty(void) {
|
||||
// NULL is empty
|
||||
assert(wlr_box_empty(NULL));
|
||||
|
||||
// Zero width/height
|
||||
struct wlr_box box = { .x = 0, .y = 0, .width = 0, .height = 10 };
|
||||
assert(wlr_box_empty(&box));
|
||||
box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = 0 };
|
||||
assert(wlr_box_empty(&box));
|
||||
|
||||
// Negative width/height
|
||||
box = (struct wlr_box){ .x = 0, .y = 0, .width = -1, .height = 10 };
|
||||
assert(wlr_box_empty(&box));
|
||||
box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = -1 };
|
||||
assert(wlr_box_empty(&box));
|
||||
|
||||
// Valid box
|
||||
box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = 10 };
|
||||
assert(!wlr_box_empty(&box));
|
||||
}
|
||||
|
||||
static void test_box_intersection(void) {
|
||||
struct wlr_box dest;
|
||||
|
||||
// Overlapping
|
||||
struct wlr_box a = { .x = 0, .y = 0, .width = 100, .height = 100 };
|
||||
struct wlr_box b = { .x = 50, .y = 50, .width = 100, .height = 100 };
|
||||
assert(wlr_box_intersection(&dest, &a, &b));
|
||||
assert(dest.x == 50 && dest.y == 50 &&
|
||||
dest.width == 50 && dest.height == 50);
|
||||
|
||||
// Non-overlapping
|
||||
b = (struct wlr_box){ .x = 200, .y = 200, .width = 50, .height = 50 };
|
||||
assert(!wlr_box_intersection(&dest, &a, &b));
|
||||
assert(dest.width == 0 && dest.height == 0);
|
||||
|
||||
// Touching edges
|
||||
b = (struct wlr_box){ .x = 100, .y = 0, .width = 50, .height = 50 };
|
||||
assert(!wlr_box_intersection(&dest, &a, &b));
|
||||
|
||||
// Self-intersection
|
||||
assert(wlr_box_intersection(&dest, &a, &a));
|
||||
assert(dest.x == a.x && dest.y == a.y &&
|
||||
dest.width == a.width && dest.height == a.height);
|
||||
|
||||
// Empty input
|
||||
struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 };
|
||||
assert(!wlr_box_intersection(&dest, &a, &empty));
|
||||
|
||||
// NULL input
|
||||
assert(!wlr_box_intersection(&dest, &a, NULL));
|
||||
assert(!wlr_box_intersection(&dest, NULL, &a));
|
||||
}
|
||||
|
||||
static void test_box_intersects_box(void) {
|
||||
// Overlapping
|
||||
struct wlr_box a = { .x = 0, .y = 0, .width = 100, .height = 100 };
|
||||
struct wlr_box b = { .x = 50, .y = 50, .width = 100, .height = 100 };
|
||||
assert(wlr_box_intersects(&a, &b));
|
||||
|
||||
// Non-overlapping
|
||||
b = (struct wlr_box){ .x = 200, .y = 200, .width = 50, .height = 50 };
|
||||
assert(!wlr_box_intersects(&a, &b));
|
||||
|
||||
// Touching edges
|
||||
b = (struct wlr_box){ .x = 100, .y = 0, .width = 50, .height = 50 };
|
||||
assert(!wlr_box_intersects(&a, &b));
|
||||
|
||||
// Self-intersection
|
||||
assert(wlr_box_intersects(&a, &a));
|
||||
|
||||
// Empty input
|
||||
struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 };
|
||||
assert(!wlr_box_intersects(&a, &empty));
|
||||
|
||||
// NULL input
|
||||
assert(!wlr_box_intersects(&a, NULL));
|
||||
assert(!wlr_box_intersects(NULL, &a));
|
||||
}
|
||||
|
||||
static void test_box_contains_point(void) {
|
||||
struct wlr_box box = { .x = 10, .y = 20, .width = 100, .height = 50 };
|
||||
|
||||
// Interior point
|
||||
assert(wlr_box_contains_point(&box, 50, 40));
|
||||
|
||||
// Inclusive lower bound
|
||||
assert(wlr_box_contains_point(&box, 10, 20));
|
||||
|
||||
// Exclusive upper bound
|
||||
assert(!wlr_box_contains_point(&box, 110, 70));
|
||||
assert(!wlr_box_contains_point(&box, 110, 40));
|
||||
assert(!wlr_box_contains_point(&box, 50, 70));
|
||||
|
||||
// Outside
|
||||
assert(!wlr_box_contains_point(&box, 5, 40));
|
||||
assert(!wlr_box_contains_point(&box, 50, 15));
|
||||
|
||||
// Empty box
|
||||
struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 };
|
||||
assert(!wlr_box_contains_point(&empty, 0, 0));
|
||||
|
||||
// NULL
|
||||
assert(!wlr_box_contains_point(NULL, 0, 0));
|
||||
}
|
||||
|
||||
static void test_box_contains_box(void) {
|
||||
struct wlr_box outer = { .x = 0, .y = 0, .width = 100, .height = 100 };
|
||||
|
||||
// Fully contained
|
||||
struct wlr_box inner = { .x = 10, .y = 10, .width = 50, .height = 50 };
|
||||
assert(wlr_box_contains_box(&outer, &inner));
|
||||
|
||||
// Self-containment
|
||||
assert(wlr_box_contains_box(&outer, &outer));
|
||||
|
||||
// Partial overlap — not contained
|
||||
struct wlr_box partial = { .x = 50, .y = 50, .width = 100, .height = 100 };
|
||||
assert(!wlr_box_contains_box(&outer, &partial));
|
||||
|
||||
// Empty inner
|
||||
struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 };
|
||||
assert(!wlr_box_contains_box(&outer, &empty));
|
||||
|
||||
// Empty outer
|
||||
assert(!wlr_box_contains_box(&empty, &inner));
|
||||
|
||||
// NULL
|
||||
assert(!wlr_box_contains_box(&outer, NULL));
|
||||
assert(!wlr_box_contains_box(NULL, &outer));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
#ifdef NDEBUG
|
||||
fprintf(stderr, "NDEBUG must be disabled for tests\n");
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
test_box_empty();
|
||||
test_box_intersection();
|
||||
test_box_intersects_box();
|
||||
test_box_contains_point();
|
||||
test_box_contains_box();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -159,9 +159,8 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) {
|
|||
struct wlr_box cursor_box;
|
||||
output_cursor_get_box(cursor, &cursor_box);
|
||||
|
||||
struct wlr_box intersection;
|
||||
cursor->visible =
|
||||
wlr_box_intersection(&intersection, &output_box, &cursor_box);
|
||||
wlr_box_intersects(&output_box, &cursor_box);
|
||||
}
|
||||
|
||||
static bool output_pick_cursor_format(struct wlr_output *output,
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@ static bool output_basic_test(struct wlr_output *output,
|
|||
};
|
||||
struct wlr_box dst_box;
|
||||
output_state_get_buffer_dst_box(state, &dst_box);
|
||||
if (!wlr_box_intersection(&output_box, &output_box, &dst_box)) {
|
||||
if (!wlr_box_intersects(&output_box, &dst_box)) {
|
||||
wlr_log(WLR_ERROR, "Primary buffer is entirely off-screen or 0-sized");
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box
|
|||
struct wlr_box node_box = { .x = lx, .y = ly };
|
||||
scene_node_get_size(node, &node_box.width, &node_box.height);
|
||||
|
||||
if (wlr_box_intersection(&node_box, &node_box, box) &&
|
||||
if (wlr_box_intersects(&node_box, box) &&
|
||||
iterator(node, lx, ly, user_data)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2673,8 +2673,7 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box,
|
|||
struct wlr_box node_box = { .x = lx, .y = ly };
|
||||
scene_node_get_size(node, &node_box.width, &node_box.height);
|
||||
|
||||
struct wlr_box intersection;
|
||||
if (wlr_box_intersection(&intersection, output_box, &node_box)) {
|
||||
if (wlr_box_intersects(output_box, &node_box)) {
|
||||
struct wlr_scene_buffer *scene_buffer =
|
||||
wlr_scene_buffer_from_node(node);
|
||||
user_iterator(scene_buffer, lx, ly, user_data);
|
||||
|
|
|
|||
|
|
@ -260,14 +260,12 @@ bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
|
|||
|
||||
bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
|
||||
struct wlr_output *reference, const struct wlr_box *target_lbox) {
|
||||
struct wlr_box out_box;
|
||||
|
||||
if (reference == NULL) {
|
||||
struct wlr_output_layout_output *l_output;
|
||||
wl_list_for_each(l_output, &layout->outputs, link) {
|
||||
struct wlr_box output_box;
|
||||
output_layout_output_get_box(l_output, &output_box);
|
||||
if (wlr_box_intersection(&out_box, &output_box, target_lbox)) {
|
||||
if (wlr_box_intersects(&output_box, target_lbox)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -281,7 +279,7 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
|
|||
|
||||
struct wlr_box output_box;
|
||||
output_layout_output_get_box(l_output, &output_box);
|
||||
return wlr_box_intersection(&out_box, &output_box, target_lbox);
|
||||
return wlr_box_intersects(&output_box, target_lbox);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
25
util/box.c
25
util/box.c
|
|
@ -4,6 +4,14 @@
|
|||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
static int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static int min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
|
||||
double *dest_x, double *dest_y) {
|
||||
// if box is empty, then it contains no points, so no closest point either
|
||||
|
|
@ -56,10 +64,10 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a,
|
|||
return false;
|
||||
}
|
||||
|
||||
int x1 = fmax(box_a->x, box_b->x);
|
||||
int y1 = fmax(box_a->y, box_b->y);
|
||||
int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width);
|
||||
int y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height);
|
||||
int x1 = max(box_a->x, box_b->x);
|
||||
int y1 = max(box_a->y, box_b->y);
|
||||
int x2 = min(box_a->x + box_a->width, box_b->x + box_b->width);
|
||||
int y2 = min(box_a->y + box_a->height, box_b->y + box_b->height);
|
||||
|
||||
dest->x = x1;
|
||||
dest->y = y1;
|
||||
|
|
@ -94,6 +102,15 @@ bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *sm
|
|||
smaller->y + smaller->height <= bigger->y + bigger->height;
|
||||
}
|
||||
|
||||
bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b) {
|
||||
if (wlr_box_empty(a) || wlr_box_empty(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return a->x < b->x + b->width && b->x < a->x + a->width &&
|
||||
a->y < b->y + b->height && b->y < a->y + a->height;
|
||||
}
|
||||
|
||||
void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box,
|
||||
enum wl_output_transform transform, int width, int height) {
|
||||
struct wlr_box src = {0};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue