Skip to content

Commit 55df9bc

Browse files
committed
nv2a: Download overlapping VGA framebuffer surfaces
1 parent ef1b08d commit 55df9bc

File tree

9 files changed

+260
-88
lines changed

9 files changed

+260
-88
lines changed

hw/xbox/nv2a/pgraph/gl/renderer.c

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ static void pgraph_gl_process_pending(NV2AState *d)
111111

112112
if (qatomic_read(&r->downloads_pending) ||
113113
qatomic_read(&r->download_dirty_surfaces_pending) ||
114+
qatomic_read(&r->download_dirty_surfaces_in_range_pending) ||
114115
qatomic_read(&d->pgraph.sync_pending) ||
115116
qatomic_read(&d->pgraph.flush_pending) ||
116117
qatomic_read(&r->shader_cache_writeback_pending)) {
@@ -122,6 +123,13 @@ static void pgraph_gl_process_pending(NV2AState *d)
122123
if (qatomic_read(&r->download_dirty_surfaces_pending)) {
123124
pgraph_gl_download_dirty_surfaces(d);
124125
}
126+
if (qatomic_read(&r->download_dirty_surfaces_in_range_pending)) {
127+
pgraph_gl_download_surfaces_in_range_if_dirty(
128+
d, r->download_dirty_surfaces_in_range_start,
129+
r->download_dirty_surfaces_in_range_size);
130+
qatomic_set(&r->download_dirty_surfaces_in_range_pending, false);
131+
qemu_event_set(&r->dirty_surfaces_download_complete);
132+
}
125133
if (qatomic_read(&d->pgraph.sync_pending)) {
126134
pgraph_gl_sync(d);
127135
}
@@ -153,6 +161,25 @@ static void pgraph_gl_pre_savevm_wait(NV2AState *d)
153161
qemu_event_wait(&r->dirty_surfaces_download_complete);
154162
}
155163

164+
static void pgraph_gl_download_overlapping_surfaces_trigger(NV2AState *d,
165+
hwaddr start,
166+
hwaddr size)
167+
{
168+
PGRAPHState *pg = &d->pgraph;
169+
PGRAPHGLState *r = pg->gl_renderer_state;
170+
171+
r->download_dirty_surfaces_in_range_start = start;
172+
r->download_dirty_surfaces_in_range_size = size;
173+
qatomic_set(&r->download_dirty_surfaces_in_range_pending, true);
174+
qemu_event_reset(&r->dirty_surfaces_download_complete);
175+
}
176+
177+
static void pgraph_gl_download_overlapping_surfaces_wait(NV2AState *d)
178+
{
179+
qemu_event_wait(
180+
&d->pgraph.gl_renderer_state->dirty_surfaces_download_complete);
181+
}
182+
156183
static void pgraph_gl_pre_shutdown_trigger(NV2AState *d)
157184
{
158185
PGRAPHState *pg = &d->pgraph;
@@ -170,33 +197,43 @@ static void pgraph_gl_pre_shutdown_wait(NV2AState *d)
170197
qemu_event_wait(&r->shader_cache_writeback_complete);
171198
}
172199

173-
static PGRAPHRenderer pgraph_gl_renderer = {
174-
.type = CONFIG_DISPLAY_RENDERER_OPENGL,
175-
.name = "OpenGL",
176-
.ops = {
177-
.init = pgraph_gl_init,
178-
.early_context_init = early_context_init,
179-
.finalize = pgraph_gl_finalize,
180-
.clear_report_value = pgraph_gl_clear_report_value,
181-
.clear_surface = pgraph_gl_clear_surface,
182-
.draw_begin = pgraph_gl_draw_begin,
183-
.draw_end = pgraph_gl_draw_end,
184-
.flip_stall = pgraph_gl_flip_stall,
185-
.flush_draw = pgraph_gl_flush_draw,
186-
.get_report = pgraph_gl_get_report,
187-
.image_blit = pgraph_gl_image_blit,
188-
.pre_savevm_trigger = pgraph_gl_pre_savevm_trigger,
189-
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
190-
.pre_shutdown_trigger = pgraph_gl_pre_shutdown_trigger,
191-
.pre_shutdown_wait = pgraph_gl_pre_shutdown_wait,
192-
.process_pending = pgraph_gl_process_pending,
193-
.process_pending_reports = pgraph_gl_process_pending_reports,
194-
.surface_update = pgraph_gl_surface_update,
195-
.set_surface_scale_factor = pgraph_gl_set_surface_scale_factor,
196-
.get_surface_scale_factor = pgraph_gl_get_surface_scale_factor,
197-
.get_framebuffer_surface = pgraph_gl_get_framebuffer_surface,
198-
}
199-
};
200+
static PGRAPHRenderer
201+
pgraph_gl_renderer = { .type = CONFIG_DISPLAY_RENDERER_OPENGL,
202+
.name = "OpenGL",
203+
.ops = {
204+
.init = pgraph_gl_init,
205+
.early_context_init = early_context_init,
206+
.finalize = pgraph_gl_finalize,
207+
.clear_report_value =
208+
pgraph_gl_clear_report_value,
209+
.clear_surface = pgraph_gl_clear_surface,
210+
.draw_begin = pgraph_gl_draw_begin,
211+
.draw_end = pgraph_gl_draw_end,
212+
.flip_stall = pgraph_gl_flip_stall,
213+
.flush_draw = pgraph_gl_flush_draw,
214+
.get_report = pgraph_gl_get_report,
215+
.image_blit = pgraph_gl_image_blit,
216+
.pre_savevm_trigger =
217+
pgraph_gl_pre_savevm_trigger,
218+
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
219+
.pre_shutdown_trigger =
220+
pgraph_gl_pre_shutdown_trigger,
221+
.pre_shutdown_wait = pgraph_gl_pre_shutdown_wait,
222+
.process_pending = pgraph_gl_process_pending,
223+
.process_pending_reports =
224+
pgraph_gl_process_pending_reports,
225+
.surface_update = pgraph_gl_surface_update,
226+
.download_overlapping_surfaces_trigger =
227+
pgraph_gl_download_overlapping_surfaces_trigger,
228+
.download_overlapping_surfaces_wait =
229+
pgraph_gl_download_overlapping_surfaces_wait,
230+
.set_surface_scale_factor =
231+
pgraph_gl_set_surface_scale_factor,
232+
.get_surface_scale_factor =
233+
pgraph_gl_get_surface_scale_factor,
234+
.get_framebuffer_surface =
235+
pgraph_gl_get_framebuffer_surface,
236+
} };
200237

201238
static void __attribute__((constructor)) register_renderer(void)
202239
{

hw/xbox/nv2a/pgraph/gl/renderer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ typedef struct PGRAPHGLState {
190190
SurfaceBinding *color_binding, *zeta_binding;
191191
bool downloads_pending;
192192
QemuEvent downloads_complete;
193+
193194
bool download_dirty_surfaces_pending;
195+
bool download_dirty_surfaces_in_range_pending;
196+
hwaddr download_dirty_surfaces_in_range_start;
197+
hwaddr download_dirty_surfaces_in_range_size;
194198
QemuEvent dirty_surfaces_download_complete; // common
195199

196200
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
@@ -276,6 +280,7 @@ void pgraph_gl_reload_surface_scale_factor(PGRAPHState *pg);
276280
void pgraph_gl_render_surface_to_texture(NV2AState *d, SurfaceBinding *surface, TextureBinding *texture, TextureShape *texture_shape, int texture_unit);
277281
void pgraph_gl_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta);
278282
void pgraph_gl_surface_download_if_dirty(NV2AState *d, SurfaceBinding *surface);
283+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size);
279284
SurfaceBinding *pgraph_gl_surface_get(NV2AState *d, hwaddr addr);
280285
SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr);
281286
void pgraph_gl_surface_invalidate(NV2AState *d, SurfaceBinding *e);

hw/xbox/nv2a/pgraph/gl/surface.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,24 @@ void pgraph_gl_surface_download_if_dirty(NV2AState *d,
602602
}
603603
}
604604

605+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size)
606+
{
607+
PGRAPHState *pg = &d->pgraph;
608+
PGRAPHGLState *r = pg->gl_renderer_state;
609+
610+
SurfaceBinding *surface;
611+
612+
hwaddr end = start + size - 1;
613+
614+
QTAILQ_FOREACH(surface, &r->surfaces, entry) {
615+
hwaddr surf_end = surface->vram_addr + surface->size - 1;
616+
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
617+
if (overlapping) {
618+
pgraph_gl_surface_download_if_dirty(d, surface);
619+
}
620+
}
621+
}
622+
605623
static void bind_current_surface(NV2AState *d)
606624
{
607625
PGRAPHState *pg = &d->pgraph;

hw/xbox/nv2a/pgraph/gl/texture.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,8 @@ void pgraph_gl_bind_textures(NV2AState *d)
283283
// FIXME: Restructure to support rendering surfaces to cubemap faces
284284

285285
// Writeback any surfaces which this texture may index
286-
hwaddr tex_vram_end = texture_vram_offset + length - 1;
287-
QTAILQ_FOREACH(surface, &r->surfaces, entry) {
288-
hwaddr surf_vram_end = surface->vram_addr + surface->size - 1;
289-
bool overlapping = !(surface->vram_addr >= tex_vram_end
290-
|| texture_vram_offset >= surf_vram_end);
291-
if (overlapping) {
292-
pgraph_gl_surface_download_if_dirty(d, surface);
293-
}
294-
}
286+
pgraph_gl_download_surfaces_in_range_if_dirty(
287+
d, texture_vram_offset, length);
295288
}
296289

297290
TextureKey key;

hw/xbox/nv2a/pgraph/null/renderer.c

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -111,34 +111,52 @@ static void pgraph_null_surface_update(NV2AState *d, bool upload,
111111
{
112112
}
113113

114+
static void pgraph_null_download_overlapping_surfaces_trigger(NV2AState *d,
115+
hwaddr start,
116+
hwaddr size)
117+
{
118+
}
119+
120+
static void pgraph_null_download_overlapping_surfaces_wait(NV2AState *d)
121+
{
122+
}
123+
114124
static void pgraph_null_init(NV2AState *d, Error **errp)
115125
{
116126
PGRAPHState *pg = &d->pgraph;
117127
pg->null_renderer_state = NULL;
118128
}
119129

120-
static PGRAPHRenderer pgraph_null_renderer = {
121-
.type = CONFIG_DISPLAY_RENDERER_NULL,
122-
.name = "Null",
123-
.ops = {
124-
.init = pgraph_null_init,
125-
.clear_report_value = pgraph_null_clear_report_value,
126-
.clear_surface = pgraph_null_clear_surface,
127-
.draw_begin = pgraph_null_draw_begin,
128-
.draw_end = pgraph_null_draw_end,
129-
.flip_stall = pgraph_null_flip_stall,
130-
.flush_draw = pgraph_null_flush_draw,
131-
.get_report = pgraph_null_get_report,
132-
.image_blit = pgraph_null_image_blit,
133-
.pre_savevm_trigger = pgraph_null_pre_savevm_trigger,
134-
.pre_savevm_wait = pgraph_null_pre_savevm_wait,
135-
.pre_shutdown_trigger = pgraph_null_pre_shutdown_trigger,
136-
.pre_shutdown_wait = pgraph_null_pre_shutdown_wait,
137-
.process_pending = pgraph_null_process_pending,
138-
.process_pending_reports = pgraph_null_process_pending_reports,
139-
.surface_update = pgraph_null_surface_update,
140-
}
141-
};
130+
static PGRAPHRenderer
131+
pgraph_null_renderer = { .type = CONFIG_DISPLAY_RENDERER_NULL,
132+
.name = "Null",
133+
.ops = {
134+
.init = pgraph_null_init,
135+
.clear_report_value =
136+
pgraph_null_clear_report_value,
137+
.clear_surface = pgraph_null_clear_surface,
138+
.draw_begin = pgraph_null_draw_begin,
139+
.draw_end = pgraph_null_draw_end,
140+
.flip_stall = pgraph_null_flip_stall,
141+
.flush_draw = pgraph_null_flush_draw,
142+
.get_report = pgraph_null_get_report,
143+
.image_blit = pgraph_null_image_blit,
144+
.pre_savevm_trigger =
145+
pgraph_null_pre_savevm_trigger,
146+
.pre_savevm_wait = pgraph_null_pre_savevm_wait,
147+
.pre_shutdown_trigger =
148+
pgraph_null_pre_shutdown_trigger,
149+
.pre_shutdown_wait =
150+
pgraph_null_pre_shutdown_wait,
151+
.process_pending = pgraph_null_process_pending,
152+
.process_pending_reports =
153+
pgraph_null_process_pending_reports,
154+
.surface_update = pgraph_null_surface_update,
155+
.download_overlapping_surfaces_trigger =
156+
pgraph_null_download_overlapping_surfaces_trigger,
157+
.download_overlapping_surfaces_wait =
158+
pgraph_null_download_overlapping_surfaces_wait,
159+
} };
142160

143161
static void __attribute__((constructor)) register_renderer(void)
144162
{

hw/xbox/nv2a/pgraph/pgraph.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,47 @@ void pgraph_destroy(PGRAPHState *pg)
352352
qemu_mutex_destroy(&pg->lock);
353353
}
354354

355+
static void pgraph_download_vga_surfaces(NV2AState *d)
356+
{
357+
VGADisplayParams vga_display_params;
358+
d->vga.get_params(&d->vga, &vga_display_params);
359+
360+
hwaddr framebuffer_start = d->pcrtc.start + vga_display_params.line_offset;
361+
if (!framebuffer_start) {
362+
// Early exit during guest startup.
363+
return;
364+
}
365+
366+
PGRAPHState *pg = &d->pgraph;
367+
{
368+
DisplaySurface *display_surface = qemu_console_surface(d->vga.con);
369+
hwaddr framebuffer_size =
370+
surface_height(display_surface) * surface_stride(display_surface);
371+
bql_lock();
372+
qemu_mutex_lock(&d->pfifo.lock);
373+
qatomic_set(&d->pfifo.halt, true);
374+
375+
pg->renderer->ops.download_overlapping_surfaces_trigger(
376+
d, framebuffer_start, framebuffer_size);
377+
378+
pfifo_kick(d);
379+
qemu_mutex_unlock(&d->pfifo.lock);
380+
381+
bql_unlock();
382+
}
383+
384+
pg->renderer->ops.download_overlapping_surfaces_wait(d);
385+
386+
{
387+
bql_lock();
388+
qemu_mutex_lock(&d->pfifo.lock);
389+
qatomic_set(&d->pfifo.halt, false);
390+
pfifo_kick(d);
391+
qemu_mutex_unlock(&d->pfifo.lock);
392+
bql_unlock();
393+
}
394+
}
395+
355396
int nv2a_get_framebuffer_surface(void)
356397
{
357398
NV2AState *d = g_nv2a;
@@ -361,9 +402,21 @@ int nv2a_get_framebuffer_surface(void)
361402
qemu_mutex_lock(&pg->renderer_lock);
362403
assert(!pg->framebuffer_in_use);
363404
pg->framebuffer_in_use = true;
405+
406+
// Presumably in the case that there is no get_framebuffer_surface it's
407+
// impossible for there to be an overlapping modified GL/VK surface and we
408+
// could just bail early?
364409
if (pg->renderer->ops.get_framebuffer_surface) {
365410
s = pg->renderer->ops.get_framebuffer_surface(d);
366411
}
412+
413+
if (!s) {
414+
// If there is no framebuffer surface, it is possible that there are
415+
// some GL surfaces that overlap the VGA region. Such surfaces must be
416+
// downloaded before the default VGA texture is created.
417+
pgraph_download_vga_surfaces(d);
418+
}
419+
367420
qemu_mutex_unlock(&pg->renderer_lock);
368421

369422
return s;

hw/xbox/nv2a/pgraph/pgraph.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ typedef struct PGRAPHRenderer {
118118
void (*process_pending_reports)(NV2AState *d);
119119
void (*surface_flush)(NV2AState *d);
120120
void (*surface_update)(NV2AState *d, bool upload, bool color_write, bool zeta_write);
121+
void (*download_overlapping_surfaces_trigger)(NV2AState *d,
122+
hwaddr start,
123+
hwaddr size);
124+
void (*download_overlapping_surfaces_wait)(NV2AState *d);
121125
void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale);
122126
unsigned int (*get_surface_scale_factor)(NV2AState *d);
123127
int (*get_framebuffer_surface)(NV2AState *d);

0 commit comments

Comments
 (0)