Page MenuHomeFreeBSD

D57373.id179027.diff
No OneTemporary

D57373.id179027.diff

diff --git a/stand/common/gfx_fb.h b/stand/common/gfx_fb.h
--- a/stand/common/gfx_fb.h
+++ b/stand/common/gfx_fb.h
@@ -216,6 +216,12 @@
struct gen_fb tg_fb;
uint32_t *tg_shadow_fb; /* units of 4 bytes */
size_t tg_shadow_sz; /* units of pages */
+ /* Dirty rectangle for double-buffered flush */
+ uint32_t tg_dirty_x1;
+ uint32_t tg_dirty_y1;
+ uint32_t tg_dirty_x2;
+ uint32_t tg_dirty_y2;
+ bool tg_dirty;
teken_funcs_t *tg_functions;
void *tg_private;
} teken_gfx_t;
@@ -264,6 +270,7 @@
bool gfx_get_edid_resolution(struct vesa_edid_info *, edid_res_list_t *);
void gfx_framework_init(void);
void gfx_fb_cons_display(uint32_t, uint32_t, uint32_t, uint32_t, void *);
+void gfx_fb_flush(void);
void gfx_fb_setpixel(uint32_t, uint32_t);
void gfx_fb_drawrect(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
void gfx_term_drawrect(uint32_t, uint32_t, uint32_t, uint32_t);
diff --git a/stand/common/gfx_fb.c b/stand/common/gfx_fb.c
--- a/stand/common/gfx_fb.c
+++ b/stand/common/gfx_fb.c
@@ -816,6 +816,27 @@
return (0);
}
+static void
+gfx_shadow_mark_dirty(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ if (gfx_state.tg_dirty) {
+ if (x < gfx_state.tg_dirty_x1)
+ gfx_state.tg_dirty_x1 = x;
+ if (y < gfx_state.tg_dirty_y1)
+ gfx_state.tg_dirty_y1 = y;
+ if (x + w > gfx_state.tg_dirty_x2)
+ gfx_state.tg_dirty_x2 = x + w;
+ if (y + h > gfx_state.tg_dirty_y2)
+ gfx_state.tg_dirty_y2 = y + h;
+ } else {
+ gfx_state.tg_dirty = true;
+ gfx_state.tg_dirty_x1 = x;
+ gfx_state.tg_dirty_y1 = y;
+ gfx_state.tg_dirty_x2 = x + w;
+ gfx_state.tg_dirty_y2 = y + h;
+ }
+}
+
static void
gfxfb_shadow_fill(uint32_t *BltBuffer,
uint32_t DestinationX, uint32_t DestinationY,
@@ -846,6 +867,86 @@
gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
}
}
+
+ gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, Height);
+}
+
+/*
+ * Write a pixel buffer into the shadow framebuffer.
+ */
+static void
+gfxfb_shadow_buf_write(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height, uint32_t Delta)
+{
+ uint32_t fbW = gfx_state.tg_fb.fb_width;
+ uint32_t sy, dy;
+
+ if (Delta == 0)
+ Delta = Width * sizeof(*gfx_state.tg_shadow_fb);
+
+ for (sy = SourceY, dy = DestinationY; sy < SourceY + Height; sy++, dy++) {
+ uint32_t *src = (uint32_t *)((uint8_t *)BltBuffer + sy * Delta) +
+ SourceX;
+ uint32_t *dst = gfx_state.tg_shadow_fb + dy * fbW + DestinationX;
+
+ bcopy(src, dst, Width * sizeof(*gfx_state.tg_shadow_fb));
+ }
+
+ gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, Height);
+}
+
+/*
+ * Copy a region within the shadow framebuffer.
+ */
+static void
+gfxfb_shadow_vid_to_vid(uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height)
+{
+ uint32_t fbW = gfx_state.tg_fb.fb_width;
+ uint32_t h = Height;
+ int step = 1;
+ uint32_t sy = SourceY, dy = DestinationY;
+
+ if (dy * fbW + DestinationX > sy * fbW + SourceX) {
+ sy += Height - 1;
+ dy += Height - 1;
+ step = -1;
+ }
+
+ while (Height-- > 0) {
+ bcopy(gfx_state.tg_shadow_fb + sy * fbW + SourceX,
+ gfx_state.tg_shadow_fb + dy * fbW + DestinationX,
+ Width * sizeof(*gfx_state.tg_shadow_fb));
+ sy += step;
+ dy += step;
+ }
+
+ gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, h);
+}
+
+/*
+ * Read a region from the shadow framebuffer into a pixel buffer.
+ */
+static void
+gfxfb_shadow_buf_read(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
+ uint32_t DestinationX, uint32_t DestinationY,
+ uint32_t Width, uint32_t Height, uint32_t Delta)
+{
+ uint32_t fbW = gfx_state.tg_fb.fb_width;
+ uint32_t sy, dy;
+
+ if (Delta == 0)
+ Delta = Width * sizeof(*gfx_state.tg_shadow_fb);
+
+ for (sy = SourceY, dy = DestinationY; sy < SourceY + Height; sy++, dy++) {
+ uint32_t *src = gfx_state.tg_shadow_fb + sy * fbW + SourceX;
+ uint32_t *dst = (uint32_t *)((uint8_t *)BltBuffer + dy * Delta) +
+ DestinationX;
+
+ bcopy(src, dst, Width * sizeof(*gfx_state.tg_shadow_fb));
+ }
}
int
@@ -855,6 +956,42 @@
uint32_t Width, uint32_t Height, uint32_t Delta)
{
int rv;
+
+ /*
+ * When a shadow framebuffer is present, redirect every blt operation
+ * into it regardless of the underlying display backend (EFI GOP or
+ * direct framebuffer). Reads are served from the shadow to avoid
+ * unreliable video readback. The real video is updated in bulk by
+ * gfx_fb_flush() once a logical output operation is complete.
+ */
+ if (gfx_state.tg_shadow_fb != NULL) {
+ switch (BltOperation) {
+ case GfxFbBltVideoFill:
+ gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
+ Width, Height);
+ break;
+
+ case GfxFbBltVideoToBltBuffer:
+ gfxfb_shadow_buf_read(BltBuffer, SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height, Delta);
+ break;
+
+ case GfxFbBltBufferToVideo:
+ gfxfb_shadow_buf_write(BltBuffer, SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height, Delta);
+ break;
+
+ case GfxFbBltVideoToVideo:
+ gfxfb_shadow_vid_to_vid(SourceX, SourceY,
+ DestinationX, DestinationY, Width, Height);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+ }
+
#if defined(EFI)
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
@@ -874,8 +1011,6 @@
tpl = BS->RaiseTPL(TPL_NOTIFY);
switch (BltOperation) {
case GfxFbBltVideoFill:
- gfxfb_shadow_fill(BltBuffer, DestinationX,
- DestinationY, Width, Height);
status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
SourceX, SourceY, DestinationX, DestinationY,
Width, Height, Delta);
@@ -927,8 +1062,6 @@
switch (BltOperation) {
case GfxFbBltVideoFill:
- gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
- Width, Height);
rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
Width, Height);
break;
@@ -955,6 +1088,57 @@
return (rv);
}
+/*
+ * Flush the dirty region of the shadow framebuffer to the real framebuffer.
+ * Must be called after each logical output operation when shadow fb is in use.
+ */
+void
+gfx_fb_flush(void)
+{
+ uint32_t x, y, w, h, pitch;
+
+ if (gfx_state.tg_shadow_fb == NULL || !gfx_state.tg_dirty ||
+ gfx_state.tg_fb_type == FB_TEXT)
+ return;
+
+ x = gfx_state.tg_dirty_x1;
+ y = gfx_state.tg_dirty_y1;
+ pitch = gfx_state.tg_fb.fb_width;
+
+ if (x >= pitch || y >= gfx_state.tg_fb.fb_height)
+ goto done;
+
+ w = gfx_state.tg_dirty_x2 - x;
+ h = gfx_state.tg_dirty_y2 - y;
+
+ if (x + w > pitch)
+ w = pitch - x;
+ if (y + h > gfx_state.tg_fb.fb_height)
+ h = gfx_state.tg_fb.fb_height - y;
+
+#if defined(EFI)
+ if (gfx_state.tg_fb_type == FB_GOP && !ignore_gop_blt &&
+ boot_services_active) {
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = gfx_state.tg_private;
+ EFI_TPL tpl;
+
+ assert(gop != NULL);
+ tpl = BS->RaiseTPL(TPL_NOTIFY);
+ (void) gop->Blt(gop,
+ (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gfx_state.tg_shadow_fb,
+ EfiBltBufferToVideo, x, y, x, y, w, h,
+ pitch * sizeof(*gfx_state.tg_shadow_fb));
+ BS->RestoreTPL(tpl);
+ goto done;
+ }
+#endif
+ (void) gfxfb_blt_buffer_to_video(gfx_state.tg_shadow_fb,
+ x, y, x, y, w, h, pitch * sizeof(*gfx_state.tg_shadow_fb));
+
+done:
+ gfx_state.tg_dirty = false;
+}
+
void
gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
const teken_attr_t *a, uint32_t alpha, bool cursor)
@@ -1057,6 +1241,8 @@
c = teken_get_cursor(&state->tg_teken);
gfx_fb_cursor_draw(state, c, true);
}
+
+ gfx_fb_flush();
}
void
@@ -1096,6 +1282,7 @@
gfx_fb_cursor_draw(state, c, true);
}
+ gfx_fb_flush();
TSEXIT();
}
@@ -1132,6 +1319,7 @@
if (state->tg_cursor_visible) {
gfx_fb_cursor_draw(state, &state->tg_cursor, false);
gfx_fb_cursor_draw(state, p, true);
+ gfx_fb_flush();
}
}
@@ -1158,6 +1346,7 @@
state->tg_cursor_visible = true;
else
state->tg_cursor_visible = false;
+ gfx_fb_flush();
break;
default:
/* Not yet implemented */
@@ -1205,7 +1394,7 @@
width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
/*
- * With no shadow fb, use video to video copy.
+ * With no shadow fb, use video to video copy directly.
*/
if (state->tg_shadow_fb == NULL) {
(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
@@ -1218,14 +1407,17 @@
}
/*
- * With shadow fb, we need to copy data on both shadow and video,
- * to preserve the consistency. We only read data from shadow fb.
+ * With shadow fb, copy within shadow only. Mark the destination dirty
+ * so gfx_fb_flush() will push it to the real framebuffer.
*/
-
step = 1;
pitch = state->tg_fb.fb_width;
bytes = width * sizeof (*state->tg_shadow_fb);
+ uint32_t dst_x = dx + state->tg_origin.tp_col;
+ uint32_t dst_y = dy + state->tg_origin.tp_row;
+ uint32_t dst_h = height;
+
/*
* To handle overlapping areas, set up reverse copy here.
*/
@@ -1240,13 +1432,11 @@
uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
bcopy(source, destination, bytes);
- (void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
- 0, 0, dx + state->tg_origin.tp_col,
- dy + state->tg_origin.tp_row, width, 1, 0);
-
sy += step;
dy += step;
}
+
+ gfx_shadow_mark_dirty(dst_x, dst_y, width, dst_h);
}
static void
@@ -1345,6 +1535,8 @@
c = teken_get_cursor(&state->tg_teken);
gfx_fb_cursor_draw(state, c, true);
}
+
+ gfx_fb_flush();
}
/*
@@ -1434,23 +1626,22 @@
size_t size;
/*
- * If we do have shadow fb, we will use shadow to render data,
- * and copy shadow to video.
+ * If we do have shadow fb, render into it only. The caller is
+ * responsible for flushing the dirty region to the real framebuffer
+ * via gfx_fb_flush() once a logical output operation is complete.
*/
if (gfx_state.tg_shadow_fb != NULL) {
uint32_t pitch = gfx_state.tg_fb.fb_width;
+ uint32_t sy = y - gfx_state.tg_origin.tp_row;
+ uint32_t sx = x - gfx_state.tg_origin.tp_col;
- /* Copy rectangle line by line. */
p = data;
- for (uint32_t sy = 0; sy < height; sy++) {
+ for (uint32_t row = 0; row < height; row++) {
buf = (void *)(gfx_state.tg_shadow_fb +
- (y - gfx_state.tg_origin.tp_row) * pitch +
- x - gfx_state.tg_origin.tp_col);
- bitmap_cpy(buf, &p[sy * width], width);
- (void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
- 0, 0, x, y, width, 1, 0);
- y++;
+ (sy + row) * pitch + sx);
+ bitmap_cpy(buf, &p[row * width], width);
}
+ gfx_shadow_mark_dirty(x, y, width, height);
return;
}
@@ -1566,6 +1757,7 @@
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
}
+ gfx_fb_flush();
}
void
@@ -1618,6 +1810,7 @@
y0 += sy;
}
}
+ gfx_fb_flush();
}
/*
@@ -1690,6 +1883,7 @@
} while (dy < dx); /* gradient negates -> algorithm fails */
}
gfx_fb_line(x0, y0, x2, y2, width);
+ gfx_fb_flush();
}
/*
@@ -2042,6 +2236,7 @@
gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
free(data);
+ gfx_fb_flush();
return (0);
}
diff --git a/stand/ficl/gfx_loader.c b/stand/ficl/gfx_loader.c
--- a/stand/ficl/gfx_loader.c
+++ b/stand/ficl/gfx_loader.c
@@ -156,9 +156,10 @@
vmCheckStack(pVM, 2, 0);
#endif
- y = stackPopUNS(pVM->pStack);
- x = stackPopUNS(pVM->pStack);
- gfx_fb_setpixel(x, y);
+ y = stackPopUNS(pVM->pStack);
+ x = stackPopUNS(pVM->pStack);
+ gfx_fb_setpixel(x, y);
+ gfx_fb_flush();
}
void
diff --git a/stand/liblua/gfx_utils.c b/stand/liblua/gfx_utils.c
--- a/stand/liblua/gfx_utils.c
+++ b/stand/liblua/gfx_utils.c
@@ -133,7 +133,8 @@
x = luaL_checknumber(L, 1);
y = luaL_checknumber(L, 2);
- gfx_fb_setpixel(x, y);
+ gfx_fb_setpixel(x, y);
+ gfx_fb_flush();
return 0;
}

File Metadata

Mime Type
text/plain
Expires
Fri, Jun 12, 2:26 AM (7 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33897558
Default Alt Text
D57373.id179027.diff (11 KB)

Event Timeline