@@ -409,25 +409,26 @@ impl<'a, 'b, F: DmaFence> VirtIOGPU<'a, 'b, F> {
409409 // areas when using `rect.width` or `rect.height` as a divisor:
410410 0
411411 } else if current_draw_offset. 0 == 0 {
412- // Okay, we can start drawing the full rectangle. We want to try
413- // drawing any full rows, if there are any left, and if not the
414- // last partial row:
412+ // We're at the start of a row. If the client buffer holds less than
413+ // a full row, copy just those pixels as a partial trailing row.
414+ // Otherwise, copy as many full rows as fit in the buffer (leaving
415+ // any remainder to a subsequent iteration).
416+ //
417+ // However, batching multiple rows into a single
418+ // `TRANSFER_TO_HOST_2D` is only safe when `draw_rect.width` matches
419+ // the resource's width: the device reads row `h` of the transfer
420+ // rectangle from backing offset `t2d.offset + h * (resource_width *
421+ // bpp)`, but our backing is a compact `draw_rect.width * bpp`
422+ // per-row buffer. When the widths don't match, cap to a single row
423+ // so the strided read collapses to a single contiguous copy:
415424 assert ! ( current_draw_offset. 1 <= draw_rect. height || remaining_pixels == 0 ) ;
416- if current_draw_offset. 1 >= draw_rect. height {
417- // Just one row left to draw, and we start from `x ==
418- // 0`. This means we can just copy however much more data
419- // the client buffer holds. We've previously checked that
420- // the client buffer fully fits into the draw area, but
421- // re-check that assertion here:
422- assert ! ( draw_rect. width as usize >= write_buffer_remaining_pixels) ;
425+ if write_buffer_remaining_pixels < draw_rect. width as usize {
423426 write_buffer_remaining_pixels
427+ } else if draw_rect. width == self . width {
428+ ( write_buffer_remaining_pixels / draw_rect. width as usize )
429+ * draw_rect. width as usize
424430 } else {
425- // There is more than one row left to copy, and we start
426- // from `x == 0`. If the client buffer lines up with the end
427- // of a row, we can copy them as a single
428- // rectangle. Otherwise, we need two copies:
429- write_buffer_remaining_pixels / ( draw_rect. width as usize )
430- * ( draw_rect. width as usize )
431+ draw_rect. width as usize
431432 }
432433 } else {
433434 // Our current draw offset is not zero. This means we must copy
@@ -580,7 +581,9 @@ impl<'a, 'b, F: DmaFence> VirtIOGPU<'a, 'b, F> {
580581
581582 // We always draw left -> right, top -> bottom, so we can simply set the
582583 // current `x` and `y` coordinates to the bottom-right most coordinates
583- // we've just drawn (while wrapping and carrying the one):
584+ // we've just drawn (while wrapping and carrying the one). `y` is the
585+ // last row we drew (relative to `draw_rect`); the wrap below advances
586+ // it to the next row when we've finished the row's rightmost column.
584587 current_draw_offset. 0 = drawn_area
585588 . x
586589 . checked_add ( drawn_area. width )
@@ -589,7 +592,8 @@ impl<'a, 'b, F: DmaFence> VirtIOGPU<'a, 'b, F> {
589592 current_draw_offset. 1 = drawn_area
590593 . y
591594 . checked_add ( drawn_area. height )
592- . and_then ( |drawn_y1| drawn_y1. checked_sub ( draw_rect. y ) )
595+ . and_then ( |drawn_y1| drawn_y1. checked_sub ( 1 ) )
596+ . and_then ( |last_row| last_row. checked_sub ( draw_rect. y ) )
593597 . unwrap ( ) ;
594598
595599 // Wrap to the next line when we've finished writing the column of our
@@ -1016,7 +1020,7 @@ impl<'a, F: DmaFence> Screen<'a> for VirtIOGPU<'a, '_, F> {
10161020
10171021 fn set_brightness ( & self , _brightness : u16 ) -> Result < ( ) , ErrorCode > {
10181022 // nop, not supported
1019- Ok ( ( ) )
1023+ Err ( ErrorCode :: NOSUPPORT )
10201024 }
10211025
10221026 fn set_power ( & self , enabled : bool ) -> Result < ( ) , ErrorCode > {
0 commit comments