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

@@ -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);