2019-09-13 09:33:49 +02:00
|
|
|
#include <assert.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <libliftoff.h>
|
2019-09-13 18:47:00 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2019-09-13 09:33:49 +02:00
|
|
|
#include "libdrm_mock.h"
|
|
|
|
|
|
|
|
static struct liftoff_layer *add_layer(struct liftoff_output *output,
|
|
|
|
int x, int y, int width, int height)
|
|
|
|
{
|
|
|
|
uint32_t fb_id;
|
|
|
|
struct liftoff_layer *layer;
|
|
|
|
|
|
|
|
layer = liftoff_layer_create(output);
|
|
|
|
fb_id = liftoff_mock_drm_create_fb(layer);
|
|
|
|
liftoff_layer_set_property(layer, "FB_ID", fb_id);
|
|
|
|
liftoff_layer_set_property(layer, "CRTC_X", x);
|
|
|
|
liftoff_layer_set_property(layer, "CRTC_Y", y);
|
|
|
|
liftoff_layer_set_property(layer, "CRTC_W", width);
|
|
|
|
liftoff_layer_set_property(layer, "CRTC_H", height);
|
|
|
|
liftoff_layer_set_property(layer, "SRC_X", 0);
|
|
|
|
liftoff_layer_set_property(layer, "SRC_Y", 0);
|
|
|
|
liftoff_layer_set_property(layer, "SRC_W", width << 16);
|
|
|
|
liftoff_layer_set_property(layer, "SRC_H", height << 16);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
struct test_plane {
|
|
|
|
int type;
|
|
|
|
};
|
|
|
|
|
2021-06-24 19:05:01 +02:00
|
|
|
/* This structure describes a layer in a test case. The first block of fields
|
|
|
|
* describe the layer properties: geometry, vertical ordering, etc. The `compat`
|
|
|
|
* field describes which hardware planes the layer is compatible with. The
|
|
|
|
* `result` field describes the expected hardware plane returned by libliftoff.
|
|
|
|
*/
|
2019-09-13 18:47:00 +02:00
|
|
|
struct test_layer {
|
|
|
|
int x, y, width, height;
|
2019-09-13 21:53:15 +02:00
|
|
|
int zpos; /* zero means unset */
|
2019-10-11 12:03:30 +02:00
|
|
|
bool composition;
|
2019-12-13 11:36:09 +01:00
|
|
|
bool force_composited;
|
2021-06-24 19:05:01 +02:00
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
struct test_plane *compat[64];
|
|
|
|
struct test_plane *result;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct test_case {
|
|
|
|
const char *name;
|
|
|
|
struct test_layer layers[64];
|
|
|
|
};
|
|
|
|
|
2021-06-24 19:05:01 +02:00
|
|
|
/* This array describes the hardware we're going to perform the tests with. Our
|
|
|
|
* hardware has one primary plane at the bottom position, two overlay planes
|
|
|
|
* at the middle position (with undefined ordering between themselves), and one
|
|
|
|
* cursor plane at the top.
|
|
|
|
*/
|
2019-09-13 18:47:00 +02:00
|
|
|
static struct test_plane test_setup[] = {
|
2019-09-14 11:59:37 +02:00
|
|
|
{ .type = DRM_PLANE_TYPE_PRIMARY }, /* zpos = 0 */
|
|
|
|
{ .type = DRM_PLANE_TYPE_CURSOR }, /* zpos = 2 */
|
|
|
|
{ .type = DRM_PLANE_TYPE_OVERLAY }, /* zpos = 1 */
|
|
|
|
{ .type = DRM_PLANE_TYPE_OVERLAY }, /* zpos = 1 */
|
2019-09-13 18:47:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const size_t test_setup_len = sizeof(test_setup) / sizeof(test_setup[0]);
|
|
|
|
|
2019-09-14 11:59:37 +02:00
|
|
|
#define PRIMARY_PLANE &test_setup[0]
|
|
|
|
#define CURSOR_PLANE &test_setup[1]
|
|
|
|
#define OVERLAY_PLANE &test_setup[2]
|
|
|
|
|
2019-09-15 15:56:24 +02:00
|
|
|
/* non-primary planes */
|
|
|
|
#define FIRST_2_SECONDARY_PLANES { &test_setup[1], &test_setup[2] }
|
|
|
|
#define FIRST_3_SECONDARY_PLANES { &test_setup[1], &test_setup[2], \
|
|
|
|
&test_setup[3] }
|
2019-09-13 21:53:15 +02:00
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
static struct test_case tests[] = {
|
2019-11-29 10:57:24 +01:00
|
|
|
{
|
|
|
|
.name = "empty",
|
|
|
|
},
|
2019-09-13 18:47:00 +02:00
|
|
|
{
|
2019-09-13 21:35:23 +02:00
|
|
|
.name = "simple-1x-fail",
|
2019-09-13 18:47:00 +02:00
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.compat = { NULL },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2019-09-13 21:35:23 +02:00
|
|
|
.name = "simple-1x",
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
2019-09-13 21:35:23 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "simple-3x",
|
2019-09-13 18:47:00 +02:00
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
2019-09-13 18:47:00 +02:00
|
|
|
},
|
2019-09-13 21:35:23 +02:00
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { CURSOR_PLANE },
|
|
|
|
.result = CURSOR_PLANE,
|
2019-09-13 21:35:23 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { OVERLAY_PLANE },
|
|
|
|
.result = OVERLAY_PLANE,
|
2019-09-13 21:35:23 +02:00
|
|
|
},
|
2019-09-13 18:47:00 +02:00
|
|
|
},
|
|
|
|
},
|
2019-09-13 21:53:15 +02:00
|
|
|
{
|
|
|
|
.name = "zpos-3x",
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
2019-09-14 11:59:37 +02:00
|
|
|
.result = OVERLAY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
2019-09-14 11:59:37 +02:00
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "zpos-3x-intersect-fail",
|
|
|
|
/* Layer 1 is over layer 2 but falls back to composition. Since
|
|
|
|
* they intersect, layer 2 needs to be composited too. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { NULL },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
2019-09-14 11:59:37 +02:00
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "zpos-3x-intersect-partial",
|
|
|
|
/* Layer 1 is only compatible with the cursor plane. Layer 2 is
|
|
|
|
* only compatible with the overlay plane. Layer 2 is over layer
|
|
|
|
* 1, but the cursor plane is over the overlay plane. There is a
|
|
|
|
* zpos conflict, only one of these two layers can be mapped to
|
|
|
|
* a plane. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = { CURSOR_PLANE },
|
2019-09-14 20:00:14 +02:00
|
|
|
.result = NULL,
|
2019-09-14 11:59:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { OVERLAY_PLANE },
|
2019-09-14 20:00:14 +02:00
|
|
|
.result = OVERLAY_PLANE,
|
2019-09-14 11:59:37 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "zpos-3x-disjoint-partial",
|
|
|
|
/* Layer 1 is over layer 2 and falls back to composition. Since
|
|
|
|
* they don't intersect, layer 2 can be mapped to a plane. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { NULL },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = { CURSOR_PLANE },
|
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "zpos-3x-disjoint",
|
|
|
|
/* Layer 1 is only compatible with the cursor plane. Layer 2 is
|
|
|
|
* only compatible with the overlay plane. Layer 2 is over layer
|
|
|
|
* 1, but the cursor plane is over the overlay plane. There is a
|
|
|
|
* zpos conflict, however since these two layers don't
|
|
|
|
* intersect, we can still map them to planes. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = { CURSOR_PLANE },
|
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { OVERLAY_PLANE },
|
|
|
|
.result = OVERLAY_PLANE,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2019-09-14 11:59:37 +02:00
|
|
|
.name = "zpos-4x-intersect-partial",
|
|
|
|
/* We have 4 layers and 4 planes. However since they all
|
|
|
|
* intersect and the ordering between both overlay planes is
|
|
|
|
* undefined, we can only use 3 planes. */
|
2019-09-13 21:53:15 +02:00
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
2019-09-14 11:59:37 +02:00
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 4,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 11:59:37 +02:00
|
|
|
.result = CURSOR_PLANE,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:00:14 +02:00
|
|
|
.result = NULL,
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:00:14 +02:00
|
|
|
.result = &test_setup[3],
|
2019-09-13 21:53:15 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-09-14 11:59:37 +02:00
|
|
|
{
|
|
|
|
.name = "zpos-4x-disjoint",
|
|
|
|
/* Ordering between the two overlay planes isn't defined,
|
|
|
|
* however layers 2 and 3 don't intersect so they can be mapped
|
|
|
|
* to these planes nonetheless. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 4,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 11:59:37 +02:00
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 22:48:10 +02:00
|
|
|
.compat = { &test_setup[3] },
|
|
|
|
.result = &test_setup[3],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { OVERLAY_PLANE },
|
|
|
|
.result = OVERLAY_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "zpos-4x-disjoint-alt",
|
|
|
|
/* Same as zpos-4x-disjoint, but with the last two layers'
|
|
|
|
* plane swapped. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 4,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-15 22:48:10 +02:00
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = { OVERLAY_PLANE },
|
2019-09-15 21:31:10 +02:00
|
|
|
.result = OVERLAY_PLANE,
|
2019-09-14 11:59:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-15 22:48:10 +02:00
|
|
|
.compat = { &test_setup[3] },
|
2019-09-15 21:31:10 +02:00
|
|
|
.result = &test_setup[3],
|
2019-09-14 11:59:37 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-09-14 20:05:14 +02:00
|
|
|
{
|
|
|
|
.name = "zpos-4x-domino-fail",
|
|
|
|
/* A layer on top falls back to composition. There is a layer at
|
|
|
|
* zpos=2 which doesn't overlap and could be mapped to a plane,
|
|
|
|
* however another layer at zpos=3 overlaps both and prevents
|
|
|
|
* all layers from being mapped to a plane. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 4,
|
|
|
|
.compat = { NULL },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:05:14 +02:00
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 50,
|
|
|
|
.y = 50,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:05:14 +02:00
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-09-14 20:18:20 +02:00
|
|
|
{
|
|
|
|
.name = "zpos-4x-domino-partial",
|
|
|
|
/* A layer on top falls back to composition. A layer at zpos=2
|
|
|
|
* falls back to composition too because it's underneath. A
|
|
|
|
* layer at zpos=3 doesn't intersect with the one at zpos=4 and
|
|
|
|
* is over the one at zpos=2 so it can be mapped to a plane. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 4,
|
|
|
|
.compat = { NULL },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 100,
|
|
|
|
.y = 100,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:18:20 +02:00
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.x = 50,
|
|
|
|
.y = 50,
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
2019-09-15 15:56:24 +02:00
|
|
|
.compat = FIRST_3_SECONDARY_PLANES,
|
2019-09-14 20:18:20 +02:00
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-10-11 12:03:30 +02:00
|
|
|
{
|
|
|
|
.name = "composition-3x",
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.composition = true,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
|
|
|
.result = OVERLAY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "composition-3x-fail",
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.composition = true,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { 0 },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "composition-3x-partial",
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.composition = true,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.compat = { 0 },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { CURSOR_PLANE },
|
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-12-13 11:36:09 +01:00
|
|
|
{
|
|
|
|
.name = "composition-3x-force",
|
|
|
|
/* Layers at zpos=1 and zpos=2 could be put on a plane, but
|
|
|
|
* FB composition is forced on the zpos=2 one. As a result, only
|
|
|
|
* the layer at zpos=3 can be put into a plane. */
|
|
|
|
.layers = {
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.composition = true,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = PRIMARY_PLANE,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 1920,
|
|
|
|
.height = 1080,
|
|
|
|
.zpos = 1,
|
|
|
|
.compat = { PRIMARY_PLANE },
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 2,
|
|
|
|
.force_composited = true,
|
|
|
|
.compat = FIRST_2_SECONDARY_PLANES,
|
|
|
|
.result = NULL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.width = 100,
|
|
|
|
.height = 100,
|
|
|
|
.zpos = 3,
|
|
|
|
.compat = { CURSOR_PLANE },
|
|
|
|
.result = CURSOR_PLANE,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-09-13 18:47:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void run_test(struct test_layer *test_layers)
|
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
ssize_t plane_index_got, plane_index_want;
|
|
|
|
struct liftoff_mock_plane *mock_planes[64];
|
2019-09-13 09:33:49 +02:00
|
|
|
struct liftoff_mock_plane *mock_plane;
|
2019-09-13 18:47:00 +02:00
|
|
|
struct test_layer *test_layer;
|
2019-09-13 09:33:49 +02:00
|
|
|
int drm_fd;
|
2019-11-24 13:07:12 +01:00
|
|
|
struct liftoff_device *device;
|
2019-09-13 09:33:49 +02:00
|
|
|
struct liftoff_output *output;
|
2019-09-13 18:47:00 +02:00
|
|
|
struct liftoff_layer *layers[64];
|
2021-07-01 11:49:36 +02:00
|
|
|
struct liftoff_plane *plane;
|
2019-09-13 09:33:49 +02:00
|
|
|
drmModeAtomicReq *req;
|
|
|
|
bool ok;
|
2020-05-26 18:47:52 +02:00
|
|
|
int ret;
|
2019-09-13 18:47:00 +02:00
|
|
|
uint32_t plane_id;
|
2019-09-13 09:33:49 +02:00
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
for (i = 0; i < test_setup_len; i++) {
|
|
|
|
mock_planes[i] = liftoff_mock_drm_create_plane(test_setup[i].type);
|
|
|
|
}
|
2019-09-13 09:33:49 +02:00
|
|
|
|
|
|
|
drm_fd = liftoff_mock_drm_open();
|
2019-11-24 13:07:12 +01:00
|
|
|
device = liftoff_device_create(drm_fd);
|
|
|
|
assert(device != NULL);
|
2019-09-13 09:33:49 +02:00
|
|
|
|
2021-02-23 18:01:38 +01:00
|
|
|
liftoff_device_register_all_planes(device);
|
|
|
|
|
2019-11-24 13:07:12 +01:00
|
|
|
output = liftoff_output_create(device, liftoff_mock_drm_crtc_id);
|
2019-09-13 18:47:00 +02:00
|
|
|
for (i = 0; test_layers[i].width > 0; i++) {
|
|
|
|
test_layer = &test_layers[i];
|
|
|
|
layers[i] = add_layer(output, test_layer->x, test_layer->y,
|
|
|
|
test_layer->width, test_layer->height);
|
2019-09-13 21:53:15 +02:00
|
|
|
if (test_layer->zpos != 0) {
|
|
|
|
liftoff_layer_set_property(layers[i], "zpos",
|
|
|
|
test_layer->zpos);
|
|
|
|
}
|
2019-10-11 12:03:30 +02:00
|
|
|
if (test_layer->composition) {
|
|
|
|
liftoff_output_set_composition_layer(output, layers[i]);
|
|
|
|
}
|
2019-12-13 11:36:09 +01:00
|
|
|
if (test_layer->force_composited) {
|
|
|
|
liftoff_layer_set_fb_composited(layers[i]);
|
|
|
|
}
|
2019-09-13 18:47:00 +02:00
|
|
|
for (j = 0; test_layer->compat[j] != NULL; j++) {
|
|
|
|
mock_plane = mock_planes[test_layer->compat[j] -
|
|
|
|
test_setup];
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane,
|
|
|
|
layers[i]);
|
|
|
|
}
|
|
|
|
}
|
2019-09-13 09:33:49 +02:00
|
|
|
|
|
|
|
req = drmModeAtomicAlloc();
|
2020-11-10 16:27:55 +01:00
|
|
|
ok = liftoff_output_apply(output, req, 0);
|
2019-09-13 09:33:49 +02:00
|
|
|
assert(ok);
|
2020-05-26 18:47:52 +02:00
|
|
|
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
|
|
|
|
assert(ret == 0);
|
2019-09-13 18:47:00 +02:00
|
|
|
drmModeAtomicFree(req);
|
|
|
|
|
|
|
|
for (i = 0; test_layers[i].width > 0; i++) {
|
2021-07-01 11:49:36 +02:00
|
|
|
plane = liftoff_layer_get_plane(layers[i]);
|
2019-09-13 18:47:00 +02:00
|
|
|
mock_plane = NULL;
|
2021-07-01 11:49:36 +02:00
|
|
|
if (plane != NULL) {
|
|
|
|
plane_id = liftoff_plane_get_id(plane);
|
2019-09-13 18:47:00 +02:00
|
|
|
mock_plane = liftoff_mock_drm_get_plane(plane_id);
|
|
|
|
}
|
|
|
|
plane_index_got = -1;
|
|
|
|
for (j = 0; j < test_setup_len; j++) {
|
|
|
|
if (mock_planes[j] == mock_plane) {
|
|
|
|
plane_index_got = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(mock_plane == NULL || plane_index_got >= 0);
|
|
|
|
|
|
|
|
fprintf(stderr, "layer %zu got assigned to plane %d\n",
|
|
|
|
i, (int)plane_index_got);
|
|
|
|
|
|
|
|
plane_index_want = -1;
|
|
|
|
if (test_layers[i].result != NULL) {
|
|
|
|
plane_index_want = test_layers[i].result - test_setup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plane_index_got != plane_index_want) {
|
|
|
|
fprintf(stderr, " ERROR: want plane %d\n",
|
|
|
|
(int)plane_index_want);
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(ok);
|
|
|
|
|
2019-11-29 10:57:24 +01:00
|
|
|
liftoff_output_destroy(output);
|
2019-11-24 13:07:12 +01:00
|
|
|
liftoff_device_destroy(device);
|
2019-09-13 18:47:00 +02:00
|
|
|
close(drm_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_basic(void)
|
|
|
|
{
|
|
|
|
struct liftoff_mock_plane *mock_plane;
|
|
|
|
int drm_fd;
|
2019-11-24 13:07:12 +01:00
|
|
|
struct liftoff_device *device;
|
2019-09-13 18:47:00 +02:00
|
|
|
struct liftoff_output *output;
|
|
|
|
struct liftoff_layer *layer;
|
|
|
|
drmModeAtomicReq *req;
|
|
|
|
bool ok;
|
2020-05-26 18:47:52 +02:00
|
|
|
int ret;
|
2019-09-13 18:47:00 +02:00
|
|
|
|
|
|
|
mock_plane = liftoff_mock_drm_create_plane(DRM_PLANE_TYPE_PRIMARY);
|
|
|
|
|
|
|
|
drm_fd = liftoff_mock_drm_open();
|
2019-11-24 13:07:12 +01:00
|
|
|
device = liftoff_device_create(drm_fd);
|
|
|
|
assert(device != NULL);
|
2019-09-13 18:47:00 +02:00
|
|
|
|
2021-02-23 18:01:38 +01:00
|
|
|
liftoff_device_register_all_planes(device);
|
|
|
|
|
2019-11-24 13:07:12 +01:00
|
|
|
output = liftoff_output_create(device, liftoff_mock_drm_crtc_id);
|
2019-09-13 18:47:00 +02:00
|
|
|
layer = add_layer(output, 0, 0, 1920, 1080);
|
2019-09-13 09:33:49 +02:00
|
|
|
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
|
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
req = drmModeAtomicAlloc();
|
2020-11-10 16:27:55 +01:00
|
|
|
ok = liftoff_output_apply(output, req, 0);
|
2019-09-13 09:33:49 +02:00
|
|
|
assert(ok);
|
2020-05-26 18:47:52 +02:00
|
|
|
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
|
|
|
|
assert(ret == 0);
|
2020-01-13 12:14:33 +01:00
|
|
|
assert(liftoff_mock_plane_get_layer(mock_plane) == layer);
|
2019-09-13 09:33:49 +02:00
|
|
|
drmModeAtomicFree(req);
|
|
|
|
|
2019-11-24 13:07:12 +01:00
|
|
|
liftoff_device_destroy(device);
|
2019-09-13 09:33:49 +02:00
|
|
|
close(drm_fd);
|
2019-09-13 18:47:00 +02:00
|
|
|
}
|
|
|
|
|
2019-12-19 17:54:49 +01:00
|
|
|
/* Checks that the library doesn't allocate a plane for a layer without a
|
|
|
|
* non-zero FB_ID set. */
|
2020-05-26 20:08:12 +02:00
|
|
|
static void test_no_fb_fail(bool zero_fb_id)
|
2019-12-19 17:41:32 +01:00
|
|
|
{
|
|
|
|
struct liftoff_mock_plane *mock_plane;
|
|
|
|
int drm_fd;
|
|
|
|
struct liftoff_device *device;
|
|
|
|
struct liftoff_output *output;
|
|
|
|
struct liftoff_layer *layer;
|
|
|
|
drmModeAtomicReq *req;
|
|
|
|
bool ok;
|
2020-05-26 18:47:52 +02:00
|
|
|
int ret;
|
2019-12-19 17:41:32 +01:00
|
|
|
|
|
|
|
mock_plane = liftoff_mock_drm_create_plane(DRM_PLANE_TYPE_PRIMARY);
|
|
|
|
|
|
|
|
drm_fd = liftoff_mock_drm_open();
|
|
|
|
device = liftoff_device_create(drm_fd);
|
|
|
|
assert(device != NULL);
|
|
|
|
|
2021-02-23 18:01:38 +01:00
|
|
|
liftoff_device_register_all_planes(device);
|
|
|
|
|
2019-12-19 17:41:32 +01:00
|
|
|
output = liftoff_output_create(device, liftoff_mock_drm_crtc_id);
|
|
|
|
layer = liftoff_layer_create(output);
|
2019-12-19 17:54:49 +01:00
|
|
|
if (zero_fb_id) {
|
|
|
|
liftoff_layer_set_property(layer, "FB_ID", 0);
|
|
|
|
}
|
2019-12-19 17:41:32 +01:00
|
|
|
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
|
|
|
|
|
|
|
|
req = drmModeAtomicAlloc();
|
2020-11-10 16:27:55 +01:00
|
|
|
ok = liftoff_output_apply(output, req, 0);
|
2019-12-19 17:41:32 +01:00
|
|
|
assert(ok);
|
2020-05-26 18:47:52 +02:00
|
|
|
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
|
|
|
|
assert(ret == 0);
|
|
|
|
assert(liftoff_mock_plane_get_layer(mock_plane) == NULL);
|
2019-12-19 17:41:32 +01:00
|
|
|
drmModeAtomicFree(req);
|
|
|
|
|
|
|
|
liftoff_device_destroy(device);
|
|
|
|
close(drm_fd);
|
|
|
|
}
|
|
|
|
|
2020-05-26 20:08:12 +02:00
|
|
|
/* Checks that the library doesn't fallback to composition when a layer doesn't
|
|
|
|
* have a FB. */
|
|
|
|
static void test_composition_zero_fb(void)
|
|
|
|
{
|
|
|
|
struct liftoff_mock_plane *mock_plane;
|
|
|
|
int drm_fd;
|
|
|
|
struct liftoff_device *device;
|
|
|
|
struct liftoff_output *output;
|
|
|
|
struct liftoff_layer *composition_layer, *layer_with_fb,
|
|
|
|
*layer_without_fb;
|
|
|
|
drmModeAtomicReq *req;
|
|
|
|
bool ok;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mock_plane = liftoff_mock_drm_create_plane(DRM_PLANE_TYPE_PRIMARY);
|
|
|
|
|
|
|
|
drm_fd = liftoff_mock_drm_open();
|
|
|
|
device = liftoff_device_create(drm_fd);
|
|
|
|
assert(device != NULL);
|
|
|
|
|
2021-02-23 18:01:38 +01:00
|
|
|
liftoff_device_register_all_planes(device);
|
|
|
|
|
2020-05-26 20:08:12 +02:00
|
|
|
output = liftoff_output_create(device, liftoff_mock_drm_crtc_id);
|
|
|
|
composition_layer = add_layer(output, 0, 0, 1920, 1080);
|
|
|
|
layer_with_fb = add_layer(output, 0, 0, 1920, 1080);
|
|
|
|
layer_without_fb = liftoff_layer_create(output);
|
|
|
|
(void)layer_with_fb;
|
|
|
|
|
|
|
|
liftoff_output_set_composition_layer(output, composition_layer);
|
|
|
|
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane, composition_layer);
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane, layer_without_fb);
|
|
|
|
liftoff_mock_plane_add_compatible_layer(mock_plane, layer_with_fb);
|
|
|
|
|
|
|
|
req = drmModeAtomicAlloc();
|
2020-11-10 16:27:55 +01:00
|
|
|
ok = liftoff_output_apply(output, req, 0);
|
2020-05-26 20:08:12 +02:00
|
|
|
assert(ok);
|
|
|
|
ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
|
|
|
|
assert(ret == 0);
|
|
|
|
assert(liftoff_mock_plane_get_layer(mock_plane) == layer_with_fb);
|
|
|
|
drmModeAtomicFree(req);
|
|
|
|
|
|
|
|
liftoff_device_destroy(device);
|
|
|
|
close(drm_fd);
|
|
|
|
}
|
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
const char *test_name;
|
|
|
|
size_t i;
|
|
|
|
|
2021-03-31 12:44:24 +02:00
|
|
|
liftoff_log_set_priority(LIFTOFF_DEBUG);
|
2019-10-07 08:47:53 +02:00
|
|
|
|
2019-09-13 18:47:00 +02:00
|
|
|
if (argc != 2) {
|
|
|
|
fprintf(stderr, "usage: %s <test-name>\n", argv[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
test_name = argv[1];
|
|
|
|
|
|
|
|
if (strcmp(test_name, "basic") == 0) {
|
|
|
|
test_basic();
|
|
|
|
return 0;
|
2020-05-26 20:08:12 +02:00
|
|
|
} else if (strcmp(test_name, "no-props-fail") == 0) {
|
|
|
|
test_no_fb_fail(false);
|
|
|
|
return 0;
|
|
|
|
} else if (strcmp(test_name, "zero-fb-id-fail") == 0) {
|
|
|
|
test_no_fb_fail(true);
|
2019-12-19 17:54:49 +01:00
|
|
|
return 0;
|
2020-05-26 20:08:12 +02:00
|
|
|
} else if (strcmp(test_name, "composition-zero-fb-id") == 0) {
|
|
|
|
test_composition_zero_fb();
|
2019-12-19 17:41:32 +01:00
|
|
|
return 0;
|
2019-09-13 18:47:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
|
|
|
if (strcmp(tests[i].name, test_name) == 0) {
|
|
|
|
run_test(tests[i].layers);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "no such test: %s\n", test_name);
|
|
|
|
return 1;
|
2019-09-13 09:33:49 +02:00
|
|
|
}
|