video: sandbox: Add test for sync() damage rectangle

Add a test that verifies the sandbox SDL driver receives the correct
damage rectangle in its sync() method. The test:

- Clears the display and verifies full-screen damage is passed to sync()
- Writes text at a specific position and verifies smaller damage region
- Draws a box and verifies the damage matches the box dimensions

The damage rectangle is recorded in sandbox_sdl_plat.last_sync_damage
and can be retrieved using sandbox_sdl_get_sync_damage() for testing
purposes.

Series-to: concept
Series-cc: heinrich
Cover-letter:
video: Enhancements to support a pointer
The video subsystem has a video_sync() function which does quite a few
things and is rather hard to predict and control:

- it may or may not actually sync, depending on a timer, etc
- it may be called when U-Boot is idle, so any time delays can cause a
  sync

These two problems make it hard to test.

This series introduces a deterministic video_manual_sync() which does
exactly what it is told, using a set of flags:

   VIDSYNC_FORCE - the force flag was provided (info for the driver)
   VIDSYNC_FLUSH - framebuffer changes should be flushed to hardware
   VIDSYNC_COPY - the copy framebuffer (if any) should be updated

The video_sync() method is renamed to sync() and is passed the flags,
so that drivers can find out what to do. This allows the
sandbox-specific code in video_sync() to move to the driver.

These features will (later) be used by expo to provide a better user
experience, e.g. to sync only part of the display, or to sync quickly
when there is mouse movement.

The pointer also needs to be drawn with transparency which is not well
supported by the BMP code. This series adds support for a simple
transparency colour for now, although an alpha channel may be
appropriate in the future.
END

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
Series-links: 1:45
Series-version: 2
This commit is contained in:
Simon Glass
2025-10-02 13:58:00 -06:00
parent 0902eb2bfb
commit 4a7ccfee27
5 changed files with 136 additions and 8 deletions

View File

@@ -312,7 +312,7 @@ static int copy_to_texture(void *lcd_base, const struct vid_bbox *damage)
return 0;
}
int sandbox_sdl_sync(void *lcd_base, const struct video_bbox *damage)
int sandbox_sdl_sync(void *lcd_base, const struct vid_bbox *damage)
{
struct SDL_Rect rect;
int ret;

View File

@@ -10,7 +10,7 @@
#include <video.h>
struct mouse_event;
struct video_bbox;
struct vid_bbox;
#ifdef CONFIG_SANDBOX_SDL
@@ -46,7 +46,7 @@ int sandbox_sdl_remove_display(void);
* @damage: Optional damage rectangle to limit the update region (may be NULL)
* Return: 0 if screen was updated, -ENODEV is there is no screen.
*/
int sandbox_sdl_sync(void *lcd_base, const struct video_bbox *damage);
int sandbox_sdl_sync(void *lcd_base, const struct vid_bbox *damage);
/**
* sandbox_sdl_scan_keys() - scan for pressed keys
@@ -132,7 +132,7 @@ static inline int sandbox_sdl_remove_display(void)
}
static inline int sandbox_sdl_sync(void *lcd_base,
const struct video_bbox *damage)
const struct vid_bbox *damage)
{
return -ENODEV;
}

View File

@@ -129,22 +129,39 @@ static int sandbox_sdl_bind(struct udevice *dev)
static int sandbox_sdl_video_sync(struct udevice *vid, uint flags)
{
struct video_priv *priv = dev_get_uclass_priv(vid);
const struct video_bbox *damage = NULL;
struct sandbox_sdl_plat *plat = dev_get_plat(vid);
struct video_priv *uc_priv = dev_get_uclass_priv(vid);
const struct vid_bbox *damage = NULL;
if (!(flags & VIDSYNC_FLUSH))
return 0;
if (IS_ENABLED(CONFIG_VIDEO_DAMAGE))
damage = &priv->damage;
damage = &uc_priv->damage;
return sandbox_sdl_sync(priv->fb, damage);
/* Record the damage box for testing */
if (damage)
plat->last_sync_damage = *damage;
else
memset(&plat->last_sync_damage, '\0',
sizeof(plat->last_sync_damage));
return sandbox_sdl_sync(uc_priv->fb, damage);
}
static const struct video_ops sandbox_sdl_ops = {
.sync = sandbox_sdl_video_sync,
};
int sandbox_sdl_get_sync_damage(struct udevice *dev, struct vid_bbox *damage)
{
struct sandbox_sdl_plat *plat = dev_get_plat(dev);
*damage = plat->last_sync_damage;
return 0;
}
static const struct udevice_id sandbox_sdl_ids[] = {
{ .compatible = "sandbox,lcd-sdl" },
{ }

View File

@@ -7,6 +7,7 @@
#define __DM_TEST_H
#include <linux/types.h>
#include <video_defs.h>
struct udevice;
@@ -157,6 +158,7 @@ extern struct unit_test_state global_dm_test_state;
* 2=upside down, 3=90 degree counterclockwise)
* @vidconsole_drv_name: Name of video console driver (set by tests)
* @font_size: Console font size to select (set by tests)
* @last_sync_damage: Last damage rectangle passed to sync() method (for testing)
*/
struct sandbox_sdl_plat {
int xres;
@@ -165,6 +167,7 @@ struct sandbox_sdl_plat {
int rot;
const char *vidconsole_drv_name;
int font_size;
struct vid_bbox last_sync_damage;
};
/**
@@ -232,4 +235,16 @@ void dm_leak_check_start(struct unit_test_state *uts);
* @dms: Overall test state
*/int dm_leak_check_end(struct unit_test_state *uts);
/**
* sandbox_sdl_get_sync_damage() - Get the last damage rect passed to sync()
*
* This is used for testing to verify that the correct damage rectangle was
* passed to the driver's sync() method.
*
* @dev: Video device
* @damage: Returns the last damage rectangle
* Return: 0 if OK, -ve on error
*/
int sandbox_sdl_get_sync_damage(struct udevice *dev, struct vid_bbox *damage);
#endif

View File

@@ -1373,3 +1373,99 @@ static int dm_test_video_manual_sync(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_video_manual_sync, UTF_SCAN_PDATA | UTF_SCAN_FDT);
/* Test that sync() receives the correct damage rectangle */
static int dm_test_video_sync_damage(struct unit_test_state *uts)
{
struct vid_bbox damage;
struct udevice *dev, *con;
struct video_priv *priv;
if (!IS_ENABLED(CONFIG_VIDEO_DAMAGE))
return -EAGAIN;
ut_assertok(select_vidconsole(uts, "vidconsole0"));
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
ut_assertok(vidconsole_select_font(con, "8x16", 0));
priv = dev_get_uclass_priv(dev);
/* Use manual sync to prevent interference with the test */
video_set_manual_sync(true);
/* Clear the display - this creates a full-screen damage and syncs */
video_clear(dev);
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
ut_asserteq(46, video_compress_fb(uts, dev, false));
/* Get the damage rectangle that was passed to sync() */
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
/* Should be the full screen */
ut_assert(vid_bbox_valid(&damage));
ut_asserteq(0, damage.x0);
ut_asserteq(0, damage.y0);
ut_asserteq(priv->xsize, damage.x1);
ut_asserteq(priv->ysize, damage.y1);
/* Sync again with no changes - should have empty damage */
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
ut_assert(!vid_bbox_valid(&damage));
/* Check that priv->damage is still reset to empty */
ut_assert(!vid_bbox_valid(&priv->damage));
/* Write a small piece of text at a specific position */
vidconsole_putc_xy(con, VID_TO_POS(400), 67, 'T');
/* Check priv->damage before sync - should have text damage */
ut_assert(vid_bbox_valid(&priv->damage));
ut_asserteq(400, priv->damage.x0);
ut_asserteq(67, priv->damage.y0);
ut_asserteq(400 + 8, priv->damage.x1); /* 8x16 font */
ut_asserteq(67 + 16, priv->damage.y1);
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
/* Get the damage rectangle that was passed to sync() */
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
/* The damage should cover just the character */
ut_assert(vid_bbox_valid(&damage));
ut_asserteq(400, damage.x0);
ut_asserteq(67, damage.y0);
ut_asserteq(400 + 8, damage.x1);
ut_asserteq(67 + 16, damage.y1);
/* Check priv->damage after sync - should be reset to empty */
ut_assert(!vid_bbox_valid(&priv->damage));
/* Draw a filled box at a different position */
ut_assertok(video_draw_box(dev, 200, 300, 250, 340, 1, 0xffffff, true));
/* Check priv->damage before sync - should have box damage */
ut_assert(vid_bbox_valid(&priv->damage));
ut_asserteq(200, priv->damage.x0);
ut_asserteq(300, priv->damage.y0);
ut_asserteq(250, priv->damage.x1);
ut_asserteq(340, priv->damage.y1);
ut_assertok(video_manual_sync(dev, VIDSYNC_FLUSH | VIDSYNC_COPY));
/* Get the damage rectangle for the box */
ut_assertok(sandbox_sdl_get_sync_damage(dev, &damage));
/* The damage should cover the box area */
ut_assert(vid_bbox_valid(&damage));
ut_asserteq(200, damage.x0);
ut_asserteq(300, damage.y0);
ut_asserteq(250, damage.x1);
ut_asserteq(340, damage.y1);
/* Check priv->damage after sync - should be reset to inverted/empty */
ut_assert(!vid_bbox_valid(&priv->damage));
return 0;
}
DM_TEST(dm_test_video_sync_damage, UTF_SCAN_PDATA | UTF_SCAN_FDT);