Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 87 additions & 27 deletions hw/xbox/nv2a/pgraph/gl/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ static void pgraph_gl_process_pending(NV2AState *d)

if (qatomic_read(&r->downloads_pending) ||
qatomic_read(&r->download_dirty_surfaces_pending) ||
qatomic_read(&r->download_dirty_surfaces_in_range_pending) ||
qatomic_read(&d->pgraph.sync_pending) ||
qatomic_read(&d->pgraph.flush_pending) ||
qatomic_read(&r->shader_cache_writeback_pending)) {
Expand All @@ -125,6 +126,13 @@ static void pgraph_gl_process_pending(NV2AState *d)
if (qatomic_read(&r->download_dirty_surfaces_pending)) {
pgraph_gl_download_dirty_surfaces(d);
}
if (qatomic_read(&r->download_dirty_surfaces_in_range_pending)) {
pgraph_gl_download_surfaces_in_range_if_dirty(
d, r->download_dirty_surfaces_in_range_start,
r->download_dirty_surfaces_in_range_size);
qatomic_set(&r->download_dirty_surfaces_in_range_pending, false);
qemu_event_set(&r->dirty_surfaces_download_complete);
}
if (qatomic_read(&d->pgraph.sync_pending)) {
pgraph_gl_sync(d);
}
Expand Down Expand Up @@ -156,6 +164,46 @@ static void pgraph_gl_pre_savevm_wait(NV2AState *d)
qemu_event_wait(&r->dirty_surfaces_download_complete);
}

static bool pgraph_gl_have_overlapping_dirty_surfaces(NV2AState *d,
hwaddr start, hwaddr size)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;

SurfaceBinding *surface;

hwaddr end = start + size - 1;

QTAILQ_FOREACH (surface, &r->surfaces, entry) {
hwaddr surf_end = surface->vram_addr + surface->size - 1;
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
if (overlapping && surface->draw_dirty) {
return true;
}
}

return false;
}

static void pgraph_gl_download_overlapping_surfaces_trigger(NV2AState *d,
hwaddr start,
hwaddr size)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;

r->download_dirty_surfaces_in_range_start = start;
r->download_dirty_surfaces_in_range_size = size;
qemu_event_reset(&r->dirty_surfaces_download_complete);
qatomic_set(&r->download_dirty_surfaces_in_range_pending, true);
}

static void pgraph_gl_download_overlapping_surfaces_wait(NV2AState *d)
{
qemu_event_wait(
&d->pgraph.gl_renderer_state->dirty_surfaces_download_complete);
}

static void pgraph_gl_pre_shutdown_trigger(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
Expand All @@ -173,33 +221,45 @@ static void pgraph_gl_pre_shutdown_wait(NV2AState *d)
qemu_event_wait(&r->shader_cache_writeback_complete);
}

static PGRAPHRenderer pgraph_gl_renderer = {
.type = CONFIG_DISPLAY_RENDERER_OPENGL,
.name = "OpenGL",
.ops = {
.init = pgraph_gl_init,
.early_context_init = early_context_init,
.finalize = pgraph_gl_finalize,
.clear_report_value = pgraph_gl_clear_report_value,
.clear_surface = pgraph_gl_clear_surface,
.draw_begin = pgraph_gl_draw_begin,
.draw_end = pgraph_gl_draw_end,
.flip_stall = pgraph_gl_flip_stall,
.flush_draw = pgraph_gl_flush_draw,
.get_report = pgraph_gl_get_report,
.image_blit = pgraph_gl_image_blit,
.pre_savevm_trigger = pgraph_gl_pre_savevm_trigger,
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
.pre_shutdown_trigger = pgraph_gl_pre_shutdown_trigger,
.pre_shutdown_wait = pgraph_gl_pre_shutdown_wait,
.process_pending = pgraph_gl_process_pending,
.process_pending_reports = pgraph_gl_process_pending_reports,
.surface_update = pgraph_gl_surface_update,
.set_surface_scale_factor = pgraph_gl_set_surface_scale_factor,
.get_surface_scale_factor = pgraph_gl_get_surface_scale_factor,
.get_framebuffer_surface = pgraph_gl_get_framebuffer_surface,
}
};
static PGRAPHRenderer
pgraph_gl_renderer = { .type = CONFIG_DISPLAY_RENDERER_OPENGL,
.name = "OpenGL",
.ops = {
.init = pgraph_gl_init,
.early_context_init = early_context_init,
.finalize = pgraph_gl_finalize,
.clear_report_value =
pgraph_gl_clear_report_value,
.clear_surface = pgraph_gl_clear_surface,
.draw_begin = pgraph_gl_draw_begin,
.draw_end = pgraph_gl_draw_end,
.flip_stall = pgraph_gl_flip_stall,
.flush_draw = pgraph_gl_flush_draw,
.get_report = pgraph_gl_get_report,
.image_blit = pgraph_gl_image_blit,
.pre_savevm_trigger =
pgraph_gl_pre_savevm_trigger,
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
.pre_shutdown_trigger =
pgraph_gl_pre_shutdown_trigger,
.pre_shutdown_wait = pgraph_gl_pre_shutdown_wait,
.process_pending = pgraph_gl_process_pending,
.process_pending_reports =
pgraph_gl_process_pending_reports,
.surface_update = pgraph_gl_surface_update,
.have_overlapping_dirty_surfaces =
pgraph_gl_have_overlapping_dirty_surfaces,
.download_overlapping_surfaces_trigger =
pgraph_gl_download_overlapping_surfaces_trigger,
.download_overlapping_surfaces_wait =
pgraph_gl_download_overlapping_surfaces_wait,
.set_surface_scale_factor =
pgraph_gl_set_surface_scale_factor,
.get_surface_scale_factor =
pgraph_gl_get_surface_scale_factor,
.get_framebuffer_surface =
pgraph_gl_get_framebuffer_surface,
} };

static void __attribute__((constructor)) register_renderer(void)
{
Expand Down
5 changes: 5 additions & 0 deletions hw/xbox/nv2a/pgraph/gl/renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,11 @@ typedef struct PGRAPHGLState {
SurfaceBinding *color_binding, *zeta_binding;
bool downloads_pending;
QemuEvent downloads_complete;

bool download_dirty_surfaces_pending;
bool download_dirty_surfaces_in_range_pending;
hwaddr download_dirty_surfaces_in_range_start;
hwaddr download_dirty_surfaces_in_range_size;
QemuEvent dirty_surfaces_download_complete; // common

TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
Expand Down Expand Up @@ -280,6 +284,7 @@ void pgraph_gl_reload_surface_scale_factor(PGRAPHState *pg);
void pgraph_gl_render_surface_to_texture(NV2AState *d, SurfaceBinding *surface, TextureBinding *texture, TextureShape *texture_shape, int texture_unit);
void pgraph_gl_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta);
void pgraph_gl_surface_download_if_dirty(NV2AState *d, SurfaceBinding *surface);
void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size);
SurfaceBinding *pgraph_gl_surface_get(NV2AState *d, hwaddr addr);
SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr);
void pgraph_gl_surface_invalidate(NV2AState *d, SurfaceBinding *e);
Expand Down
18 changes: 18 additions & 0 deletions hw/xbox/nv2a/pgraph/gl/surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,24 @@ void pgraph_gl_surface_download_if_dirty(NV2AState *d,
}
}

void pgraph_gl_download_surfaces_in_range_if_dirty(NV2AState *d, hwaddr start, hwaddr size)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;

SurfaceBinding *surface;

hwaddr end = start + size - 1;

QTAILQ_FOREACH(surface, &r->surfaces, entry) {
hwaddr surf_end = surface->vram_addr + surface->size - 1;
bool overlapping = !(surface->vram_addr >= end || start >= surf_end);
if (overlapping) {
pgraph_gl_surface_download_if_dirty(d, surface);
}
}
}

static void bind_current_surface(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
Expand Down
11 changes: 2 additions & 9 deletions hw/xbox/nv2a/pgraph/gl/texture.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,8 @@ void pgraph_gl_bind_textures(NV2AState *d)
// FIXME: Restructure to support rendering surfaces to cubemap faces

// Writeback any surfaces which this texture may index
hwaddr tex_vram_end = texture_vram_offset + length - 1;
QTAILQ_FOREACH(surface, &r->surfaces, entry) {
hwaddr surf_vram_end = surface->vram_addr + surface->size - 1;
bool overlapping = !(surface->vram_addr >= tex_vram_end
|| texture_vram_offset >= surf_vram_end);
if (overlapping) {
pgraph_gl_surface_download_if_dirty(d, surface);
}
}
pgraph_gl_download_surfaces_in_range_if_dirty(
d, texture_vram_offset, length);
}

TextureKey key;
Expand Down
71 changes: 49 additions & 22 deletions hw/xbox/nv2a/pgraph/null/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,34 +111,61 @@ static void pgraph_null_surface_update(NV2AState *d, bool upload,
{
}

static bool pgraph_null_have_overlapping_dirty_surfaces(NV2AState *d,
hwaddr start,
hwaddr size)
{
return false;
}

static void pgraph_null_download_overlapping_surfaces_trigger(NV2AState *d,
hwaddr start,
hwaddr size)
{
}

static void pgraph_null_download_overlapping_surfaces_wait(NV2AState *d)
{
}

static void pgraph_null_init(NV2AState *d, Error **errp)
{
PGRAPHState *pg = &d->pgraph;
pg->null_renderer_state = NULL;
}

static PGRAPHRenderer pgraph_null_renderer = {
.type = CONFIG_DISPLAY_RENDERER_NULL,
.name = "Null",
.ops = {
.init = pgraph_null_init,
.clear_report_value = pgraph_null_clear_report_value,
.clear_surface = pgraph_null_clear_surface,
.draw_begin = pgraph_null_draw_begin,
.draw_end = pgraph_null_draw_end,
.flip_stall = pgraph_null_flip_stall,
.flush_draw = pgraph_null_flush_draw,
.get_report = pgraph_null_get_report,
.image_blit = pgraph_null_image_blit,
.pre_savevm_trigger = pgraph_null_pre_savevm_trigger,
.pre_savevm_wait = pgraph_null_pre_savevm_wait,
.pre_shutdown_trigger = pgraph_null_pre_shutdown_trigger,
.pre_shutdown_wait = pgraph_null_pre_shutdown_wait,
.process_pending = pgraph_null_process_pending,
.process_pending_reports = pgraph_null_process_pending_reports,
.surface_update = pgraph_null_surface_update,
}
};
static PGRAPHRenderer
pgraph_null_renderer = { .type = CONFIG_DISPLAY_RENDERER_NULL,
.name = "Null",
.ops = {
.init = pgraph_null_init,
.clear_report_value =
pgraph_null_clear_report_value,
.clear_surface = pgraph_null_clear_surface,
.draw_begin = pgraph_null_draw_begin,
.draw_end = pgraph_null_draw_end,
.flip_stall = pgraph_null_flip_stall,
.flush_draw = pgraph_null_flush_draw,
.get_report = pgraph_null_get_report,
.image_blit = pgraph_null_image_blit,
.pre_savevm_trigger =
pgraph_null_pre_savevm_trigger,
.pre_savevm_wait = pgraph_null_pre_savevm_wait,
.pre_shutdown_trigger =
pgraph_null_pre_shutdown_trigger,
.pre_shutdown_wait =
pgraph_null_pre_shutdown_wait,
.process_pending = pgraph_null_process_pending,
.process_pending_reports =
pgraph_null_process_pending_reports,
.surface_update = pgraph_null_surface_update,
.have_overlapping_dirty_surfaces =
pgraph_null_have_overlapping_dirty_surfaces,
.download_overlapping_surfaces_trigger =
pgraph_null_download_overlapping_surfaces_trigger,
.download_overlapping_surfaces_wait =
pgraph_null_download_overlapping_surfaces_wait,
} };

static void __attribute__((constructor)) register_renderer(void)
{
Expand Down
61 changes: 61 additions & 0 deletions hw/xbox/nv2a/pgraph/pgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,55 @@ void pgraph_destroy(PGRAPHState *pg)
qemu_mutex_destroy(&pg->lock);
}

static void pgraph_download_vga_surfaces(NV2AState *d)
{
VGADisplayParams vga_display_params;
d->vga.get_params(&d->vga, &vga_display_params);

hwaddr framebuffer_start = d->pcrtc.start + vga_display_params.line_offset;
if (!framebuffer_start) {
// Early exit during guest startup.
return;
}

PGRAPHState *pg = &d->pgraph;
{
DisplaySurface *display_surface = qemu_console_surface(d->vga.con);
hwaddr framebuffer_size =
surface_height(display_surface) * surface_stride(display_surface);

if (!pg->renderer->ops.have_overlapping_dirty_surfaces(
d, framebuffer_start, framebuffer_size)) {
return;
}

trace_nv2a_pgraph_surface_download_vga_overlapping(framebuffer_start,
framebuffer_size);
bql_lock();
qemu_mutex_lock(&d->pfifo.lock);
qatomic_set(&d->pfifo.halt, true);

pg->renderer->ops.download_overlapping_surfaces_trigger(
d, framebuffer_start, framebuffer_size);

pfifo_kick(d);
qemu_mutex_unlock(&d->pfifo.lock);

bql_unlock();
}

pg->renderer->ops.download_overlapping_surfaces_wait(d);

{
bql_lock();
qemu_mutex_lock(&d->pfifo.lock);
qatomic_set(&d->pfifo.halt, false);
pfifo_kick(d);
qemu_mutex_unlock(&d->pfifo.lock);
bql_unlock();
}
}

int nv2a_get_framebuffer_surface(void)
{
NV2AState *d = g_nv2a;
Expand All @@ -363,9 +412,21 @@ int nv2a_get_framebuffer_surface(void)
qemu_mutex_lock(&pg->renderer_lock);
assert(!pg->framebuffer_in_use);
pg->framebuffer_in_use = true;

// Presumably in the case that there is no get_framebuffer_surface it's
// impossible for there to be an overlapping modified GL/VK surface and we
// could just bail early?
if (pg->renderer->ops.get_framebuffer_surface) {
s = pg->renderer->ops.get_framebuffer_surface(d);
}

if (!s) {
// If there is no framebuffer surface, it is possible that there are
// some GL surfaces that overlap the VGA region. Such surfaces must be
// downloaded before the default VGA texture is created.
pgraph_download_vga_surfaces(d);
}

qemu_mutex_unlock(&pg->renderer_lock);

return s;
Expand Down
6 changes: 6 additions & 0 deletions hw/xbox/nv2a/pgraph/pgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ typedef struct PGRAPHRenderer {
void (*process_pending_reports)(NV2AState *d);
void (*surface_flush)(NV2AState *d);
void (*surface_update)(NV2AState *d, bool upload, bool color_write, bool zeta_write);
bool (*have_overlapping_dirty_surfaces)(NV2AState *d, hwaddr start,
hwaddr size);
void (*download_overlapping_surfaces_trigger)(NV2AState *d,
hwaddr start,
hwaddr size);
void (*download_overlapping_surfaces_wait)(NV2AState *d);
void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale);
unsigned int (*get_surface_scale_factor)(NV2AState *d);
int (*get_framebuffer_surface)(NV2AState *d);
Expand Down
Loading
Loading