Skip to content

Commit 887da83

Browse files
committed
nv2a: Download overlapping VGA framebuffer surfaces
1 parent dcd524c commit 887da83

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
@@ -162,7 +162,11 @@ typedef struct PGRAPHGLState {
162162
SurfaceBinding *color_binding, *zeta_binding;
163163
bool downloads_pending;
164164
QemuEvent downloads_complete;
165+
165166
bool download_dirty_surfaces_pending;
167+
bool download_dirty_surfaces_in_range_pending;
168+
hwaddr download_dirty_surfaces_in_range_start;
169+
hwaddr download_dirty_surfaces_in_range_size;
166170
QemuEvent dirty_surfaces_download_complete; // common
167171

168172
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
@@ -248,6 +252,7 @@ void pgraph_gl_reload_surface_scale_factor(PGRAPHState *pg);
248252
void pgraph_gl_render_surface_to_texture(NV2AState *d, SurfaceBinding *surface, TextureBinding *texture, TextureShape *texture_shape, int texture_unit);
249253
void pgraph_gl_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta);
250254
void pgraph_gl_surface_download_if_dirty(NV2AState *d, SurfaceBinding *surface);
255+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size);
251256
SurfaceBinding *pgraph_gl_surface_get(NV2AState *d, hwaddr addr);
252257
SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr);
253258
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
@@ -628,6 +628,24 @@ void pgraph_gl_surface_download_if_dirty(NV2AState *d,
628628
}
629629
}
630630

631+
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size)
632+
{
633+
PGRAPHState *pg = &d->pgraph;
634+
PGRAPHGLState *r = pg->gl_renderer_state;
635+
636+
SurfaceBinding *surface;
637+
638+
hwaddr end = start + size - 1;
639+
640+
QTAILQ_FOREACH(surface, &r->surfaces, entry) {
641+
hwaddr surf_end = surface->vram_addr + surface->size - 1;
642+
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
643+
if (overlapping) {
644+
pgraph_gl_surface_download_if_dirty(d, surface);
645+
}
646+
}
647+
}
648+
631649
static void bind_current_surface(NV2AState *d)
632650
{
633651
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
@@ -354,6 +354,55 @@ void pgraph_destroy(PGRAPHState *pg)
354354
qemu_mutex_destroy(&pg->lock);
355355
}
356356

357+
static void pgraph_download_vga_surfaces(NV2AState *d)
358+
{
359+
VGADisplayParams vga_display_params;
360+
d->vga.get_params(&d->vga, &vga_display_params);
361+
362+
hwaddr framebuffer_start = d->pcrtc.start + vga_display_params.line_offset;
363+
if (!framebuffer_start) {
364+
// Early exit during guest startup.
365+
return;
366+
}
367+
368+
PGRAPHState *pg = &d->pgraph;
369+
{
370+
DisplaySurface *display_surface = qemu_console_surface(d->vga.con);
371+
hwaddr framebuffer_size =
372+
surface_height(display_surface) * surface_stride(display_surface);
373+
374+
if (!pg->renderer->ops.have_overlapping_dirty_surfaces(
375+
d, framebuffer_start, framebuffer_size)) {
376+
return;
377+
}
378+
379+
trace_nv2a_pgraph_surface_download_vga_overlapping(framebuffer_start,
380+
framebuffer_size);
381+
bql_lock();
382+
qemu_mutex_lock(&d->pfifo.lock);
383+
qatomic_set(&d->pfifo.halt, true);
384+
385+
pg->renderer->ops.download_overlapping_surfaces_trigger(
386+
d, framebuffer_start, framebuffer_size);
387+
388+
pfifo_kick(d);
389+
qemu_mutex_unlock(&d->pfifo.lock);
390+
391+
bql_unlock();
392+
}
393+
394+
pg->renderer->ops.download_overlapping_surfaces_wait(d);
395+
396+
{
397+
bql_lock();
398+
qemu_mutex_lock(&d->pfifo.lock);
399+
qatomic_set(&d->pfifo.halt, false);
400+
pfifo_kick(d);
401+
qemu_mutex_unlock(&d->pfifo.lock);
402+
bql_unlock();
403+
}
404+
}
405+
357406
int nv2a_get_framebuffer_surface(void)
358407
{
359408
NV2AState *d = g_nv2a;
@@ -363,9 +412,21 @@ int nv2a_get_framebuffer_surface(void)
363412
qemu_mutex_lock(&pg->renderer_lock);
364413
assert(!pg->framebuffer_in_use);
365414
pg->framebuffer_in_use = true;
415+
416+
// Presumably in the case that there is no get_framebuffer_surface it's
417+
// impossible for there to be an overlapping modified GL/VK surface and we
418+
// could just bail early?
366419
if (pg->renderer->ops.get_framebuffer_surface) {
367420
s = pg->renderer->ops.get_framebuffer_surface(d);
368421
}
422+
423+
if (!s) {
424+
// If there is no framebuffer surface, it is possible that there are
425+
// some GL surfaces that overlap the VGA region. Such surfaces must be
426+
// downloaded before the default VGA texture is created.
427+
pgraph_download_vga_surfaces(d);
428+
}
429+
369430
qemu_mutex_unlock(&pg->renderer_lock);
370431

371432
return s;

hw/xbox/nv2a/pgraph/pgraph.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ typedef struct PGRAPHRenderer {
119119
void (*process_pending_reports)(NV2AState *d);
120120
void (*surface_flush)(NV2AState *d);
121121
void (*surface_update)(NV2AState *d, bool upload, bool color_write, bool zeta_write);
122+
bool (*have_overlapping_dirty_surfaces)(NV2AState *d, hwaddr start,
123+
hwaddr size);
124+
void (*download_overlapping_surfaces_trigger)(NV2AState *d,
125+
hwaddr start,
126+
hwaddr size);
127+
void (*download_overlapping_surfaces_wait)(NV2AState *d);
122128
void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale);
123129
unsigned int (*get_surface_scale_factor)(NV2AState *d);
124130
int (*get_framebuffer_surface)(NV2AState *d);

0 commit comments

Comments
 (0)