Skip to content

Commit 4bdec58

Browse files
committed
nv2a: Download overlapping VGA framebuffer surfaces
1 parent 38d3336 commit 4bdec58

File tree

10 files changed

+320
-88
lines changed

10 files changed

+320
-88
lines changed

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

Lines changed: 87 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,46 @@ static void pgraph_gl_pre_savevm_wait(NV2AState *d)
153161
qemu_event_wait(&r->dirty_surfaces_download_complete);
154162
}
155163

164+
static bool pgraph_gl_have_overlapping_dirty_surfaces(NV2AState *d,
165+
hwaddr start, hwaddr size)
166+
{
167+
PGRAPHState *pg = &d->pgraph;
168+
PGRAPHGLState *r = pg->gl_renderer_state;
169+
170+
SurfaceBinding *surface;
171+
172+
hwaddr end = start + size - 1;
173+
174+
QTAILQ_FOREACH (surface, &r->surfaces, entry) {
175+
hwaddr surf_end = surface->vram_addr + surface->size - 1;
176+
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
177+
if (overlapping && surface->draw_dirty) {
178+
return true;
179+
}
180+
}
181+
182+
return false;
183+
}
184+
185+
static void pgraph_gl_download_overlapping_surfaces_trigger(NV2AState *d,
186+
hwaddr start,
187+
hwaddr size)
188+
{
189+
PGRAPHState *pg = &d->pgraph;
190+
PGRAPHGLState *r = pg->gl_renderer_state;
191+
192+
r->download_dirty_surfaces_in_range_start = start;
193+
r->download_dirty_surfaces_in_range_size = size;
194+
qemu_event_reset(&r->dirty_surfaces_download_complete);
195+
qatomic_set(&r->download_dirty_surfaces_in_range_pending, true);
196+
}
197+
198+
static void pgraph_gl_download_overlapping_surfaces_wait(NV2AState *d)
199+
{
200+
qemu_event_wait(
201+
&d->pgraph.gl_renderer_state->dirty_surfaces_download_complete);
202+
}
203+
156204
static void pgraph_gl_pre_shutdown_trigger(NV2AState *d)
157205
{
158206
PGRAPHState *pg = &d->pgraph;
@@ -170,33 +218,45 @@ static void pgraph_gl_pre_shutdown_wait(NV2AState *d)
170218
qemu_event_wait(&r->shader_cache_writeback_complete);
171219
}
172220

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-
};
221+
static PGRAPHRenderer
222+
pgraph_gl_renderer = { .type = CONFIG_DISPLAY_RENDERER_OPENGL,
223+
.name = "OpenGL",
224+
.ops = {
225+
.init = pgraph_gl_init,
226+
.early_context_init = early_context_init,
227+
.finalize = pgraph_gl_finalize,
228+
.clear_report_value =
229+
pgraph_gl_clear_report_value,
230+
.clear_surface = pgraph_gl_clear_surface,
231+
.draw_begin = pgraph_gl_draw_begin,
232+
.draw_end = pgraph_gl_draw_end,
233+
.flip_stall = pgraph_gl_flip_stall,
234+
.flush_draw = pgraph_gl_flush_draw,
235+
.get_report = pgraph_gl_get_report,
236+
.image_blit = pgraph_gl_image_blit,
237+
.pre_savevm_trigger =
238+
pgraph_gl_pre_savevm_trigger,
239+
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
240+
.pre_shutdown_trigger =
241+
pgraph_gl_pre_shutdown_trigger,
242+
.pre_shutdown_wait = pgraph_gl_pre_shutdown_wait,
243+
.process_pending = pgraph_gl_process_pending,
244+
.process_pending_reports =
245+
pgraph_gl_process_pending_reports,
246+
.surface_update = pgraph_gl_surface_update,
247+
.have_overlapping_dirty_surfaces =
248+
pgraph_gl_have_overlapping_dirty_surfaces,
249+
.download_overlapping_surfaces_trigger =
250+
pgraph_gl_download_overlapping_surfaces_trigger,
251+
.download_overlapping_surfaces_wait =
252+
pgraph_gl_download_overlapping_surfaces_wait,
253+
.set_surface_scale_factor =
254+
pgraph_gl_set_surface_scale_factor,
255+
.get_surface_scale_factor =
256+
pgraph_gl_get_surface_scale_factor,
257+
.get_framebuffer_surface =
258+
pgraph_gl_get_framebuffer_surface,
259+
} };
200260

201261
static void __attribute__((constructor)) register_renderer(void)
202262
{

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ typedef struct PGRAPHGLState {
189189
SurfaceBinding *color_binding, *zeta_binding;
190190
bool downloads_pending;
191191
QemuEvent downloads_complete;
192+
192193
bool download_dirty_surfaces_pending;
194+
bool download_dirty_surfaces_in_range_pending;
195+
hwaddr download_dirty_surfaces_in_range_start;
196+
hwaddr download_dirty_surfaces_in_range_size;
193197
QemuEvent dirty_surfaces_download_complete; // common
194198

195199
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
@@ -275,6 +279,7 @@ void pgraph_gl_reload_surface_scale_factor(PGRAPHState *pg);
275279
void pgraph_gl_render_surface_to_texture(NV2AState *d, SurfaceBinding *surface, TextureBinding *texture, TextureShape *texture_shape, int texture_unit);
276280
void pgraph_gl_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta);
277281
void pgraph_gl_surface_download_if_dirty(NV2AState *d, SurfaceBinding *surface);
282+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size);
278283
SurfaceBinding *pgraph_gl_surface_get(NV2AState *d, hwaddr addr);
279284
SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr);
280285
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
@@ -598,6 +598,24 @@ void pgraph_gl_surface_download_if_dirty(NV2AState *d,
598598
}
599599
}
600600

601+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size)
602+
{
603+
PGRAPHState *pg = &d->pgraph;
604+
PGRAPHGLState *r = pg->gl_renderer_state;
605+
606+
SurfaceBinding *surface;
607+
608+
hwaddr end = start + size - 1;
609+
610+
QTAILQ_FOREACH(surface, &r->surfaces, entry) {
611+
hwaddr surf_end = surface->vram_addr + surface->size - 1;
612+
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
613+
if (overlapping) {
614+
pgraph_gl_surface_download_if_dirty(d, surface);
615+
}
616+
}
617+
}
618+
601619
static void bind_current_surface(NV2AState *d)
602620
{
603621
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: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -111,34 +111,61 @@ static void pgraph_null_surface_update(NV2AState *d, bool upload,
111111
{
112112
}
113113

114+
static bool pgraph_null_have_overlapping_dirty_surfaces(NV2AState *d,
115+
hwaddr start,
116+
hwaddr size)
117+
{
118+
return false;
119+
}
120+
121+
static void pgraph_null_download_overlapping_surfaces_trigger(NV2AState *d,
122+
hwaddr start,
123+
hwaddr size)
124+
{
125+
}
126+
127+
static void pgraph_null_download_overlapping_surfaces_wait(NV2AState *d)
128+
{
129+
}
130+
114131
static void pgraph_null_init(NV2AState *d, Error **errp)
115132
{
116133
PGRAPHState *pg = &d->pgraph;
117134
pg->null_renderer_state = NULL;
118135
}
119136

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-
};
137+
static PGRAPHRenderer
138+
pgraph_null_renderer = { .type = CONFIG_DISPLAY_RENDERER_NULL,
139+
.name = "Null",
140+
.ops = {
141+
.init = pgraph_null_init,
142+
.clear_report_value =
143+
pgraph_null_clear_report_value,
144+
.clear_surface = pgraph_null_clear_surface,
145+
.draw_begin = pgraph_null_draw_begin,
146+
.draw_end = pgraph_null_draw_end,
147+
.flip_stall = pgraph_null_flip_stall,
148+
.flush_draw = pgraph_null_flush_draw,
149+
.get_report = pgraph_null_get_report,
150+
.image_blit = pgraph_null_image_blit,
151+
.pre_savevm_trigger =
152+
pgraph_null_pre_savevm_trigger,
153+
.pre_savevm_wait = pgraph_null_pre_savevm_wait,
154+
.pre_shutdown_trigger =
155+
pgraph_null_pre_shutdown_trigger,
156+
.pre_shutdown_wait =
157+
pgraph_null_pre_shutdown_wait,
158+
.process_pending = pgraph_null_process_pending,
159+
.process_pending_reports =
160+
pgraph_null_process_pending_reports,
161+
.surface_update = pgraph_null_surface_update,
162+
.have_overlapping_dirty_surfaces =
163+
pgraph_null_have_overlapping_dirty_surfaces,
164+
.download_overlapping_surfaces_trigger =
165+
pgraph_null_download_overlapping_surfaces_trigger,
166+
.download_overlapping_surfaces_wait =
167+
pgraph_null_download_overlapping_surfaces_wait,
168+
} };
142169

143170
static void __attribute__((constructor)) register_renderer(void)
144171
{

hw/xbox/nv2a/pgraph/pgraph.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,55 @@ 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+
372+
if (!pg->renderer->ops.have_overlapping_dirty_surfaces(
373+
d, framebuffer_start, framebuffer_size)) {
374+
return;
375+
}
376+
377+
trace_nv2a_pgraph_surface_download_vga_overlapping(framebuffer_start,
378+
framebuffer_size);
379+
bql_lock();
380+
qemu_mutex_lock(&d->pfifo.lock);
381+
qatomic_set(&d->pfifo.halt, true);
382+
383+
pg->renderer->ops.download_overlapping_surfaces_trigger(
384+
d, framebuffer_start, framebuffer_size);
385+
386+
pfifo_kick(d);
387+
qemu_mutex_unlock(&d->pfifo.lock);
388+
389+
bql_unlock();
390+
}
391+
392+
pg->renderer->ops.download_overlapping_surfaces_wait(d);
393+
394+
{
395+
bql_lock();
396+
qemu_mutex_lock(&d->pfifo.lock);
397+
qatomic_set(&d->pfifo.halt, false);
398+
pfifo_kick(d);
399+
qemu_mutex_unlock(&d->pfifo.lock);
400+
bql_unlock();
401+
}
402+
}
403+
355404
int nv2a_get_framebuffer_surface(void)
356405
{
357406
NV2AState *d = g_nv2a;
@@ -361,9 +410,21 @@ int nv2a_get_framebuffer_surface(void)
361410
qemu_mutex_lock(&pg->renderer_lock);
362411
assert(!pg->framebuffer_in_use);
363412
pg->framebuffer_in_use = true;
413+
414+
// Presumably in the case that there is no get_framebuffer_surface it's
415+
// impossible for there to be an overlapping modified GL/VK surface and we
416+
// could just bail early?
364417
if (pg->renderer->ops.get_framebuffer_surface) {
365418
s = pg->renderer->ops.get_framebuffer_surface(d);
366419
}
420+
421+
if (!s) {
422+
// If there is no framebuffer surface, it is possible that there are
423+
// some GL surfaces that overlap the VGA region. Such surfaces must be
424+
// downloaded before the default VGA texture is created.
425+
pgraph_download_vga_surfaces(d);
426+
}
427+
367428
qemu_mutex_unlock(&pg->renderer_lock);
368429

369430
return s;

hw/xbox/nv2a/pgraph/pgraph.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ 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+
bool (*have_overlapping_dirty_surfaces)(NV2AState *d, hwaddr start,
122+
hwaddr size);
123+
void (*download_overlapping_surfaces_trigger)(NV2AState *d,
124+
hwaddr start,
125+
hwaddr size);
126+
void (*download_overlapping_surfaces_wait)(NV2AState *d);
121127
void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale);
122128
unsigned int (*get_surface_scale_factor)(NV2AState *d);
123129
int (*get_framebuffer_surface)(NV2AState *d);

0 commit comments

Comments
 (0)