/* * Geforce NV2A PGRAPH OpenGL Renderer * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "hw/xbox/nv2a/nv2a_int.h" #include "hw/xbox/nv2a/pgraph/pgraph.h" #include "debug.h" #include "renderer.h" GloContext *g_nv2a_context_render; GloContext *g_nv2a_context_display; static void early_context_init(void) { g_nv2a_context_render = glo_context_create(); g_nv2a_context_display = glo_context_create(); } static void pgraph_gl_init(NV2AState *d, Error **errp) { PGRAPHState *pg = &d->pgraph; pg->gl_renderer_state = g_malloc0(sizeof(*pg->gl_renderer_state)); PGRAPHGLState *r = pg->gl_renderer_state; /* fire up opengl */ glo_set_current(g_nv2a_context_render); #if DEBUG_NV2A_GL gl_debug_initialize(); #endif /* DXT textures */ assert(glo_check_extension("GL_EXT_texture_compression_s3tc")); /* Internal RGB565 texture format */ assert(glo_check_extension("GL_ARB_ES2_compatibility")); glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, r->supported_smooth_line_width_range); glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, r->supported_aliased_line_width_range); pgraph_gl_init_surfaces(pg); pgraph_gl_init_reports(d); pgraph_gl_init_textures(d); pgraph_gl_init_buffers(d); pgraph_gl_init_shaders(pg); pgraph_gl_init_display(d); pgraph_gl_update_entire_memory_buffer(d); pg->uniform_attrs = 0; pg->swizzle_attrs = 0; } static void pgraph_gl_finalize(NV2AState *d) { PGRAPHState *pg = &d->pgraph; glo_set_current(g_nv2a_context_render); pgraph_gl_finalize_surfaces(pg); pgraph_gl_finalize_shaders(pg); pgraph_gl_finalize_textures(pg); pgraph_gl_finalize_reports(pg); pgraph_gl_finalize_buffers(pg); pgraph_gl_finalize_display(pg); glo_set_current(NULL); g_free(pg->gl_renderer_state); pg->gl_renderer_state = NULL; } static void pgraph_gl_flip_stall(NV2AState *d) { NV2A_GL_DFRAME_TERMINATOR(); glFinish(); } static void pgraph_gl_flush(NV2AState *d) { pgraph_gl_surface_flush(d); pgraph_gl_mark_textures_possibly_dirty(d, 0, memory_region_size(d->vram)); pgraph_gl_update_entire_memory_buffer(d); /* FIXME: Flush more? */ qatomic_set(&d->pgraph.flush_pending, false); qemu_event_set(&d->pgraph.flush_complete); } static void pgraph_gl_process_pending(NV2AState *d) { PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; if (qatomic_read(&r->downloads_pending) || qatomic_read(&r->download_dirty_surfaces_pending) || qatomic_read(&d->pgraph.sync_pending) || qatomic_read(&d->pgraph.flush_pending) || qatomic_read(&r->shader_cache_writeback_pending)) { qemu_mutex_unlock(&d->pfifo.lock); qemu_mutex_lock(&d->pgraph.lock); if (qatomic_read(&r->downloads_pending)) { pgraph_gl_process_pending_downloads(d); } if (qatomic_read(&r->download_dirty_surfaces_pending)) { pgraph_gl_download_dirty_surfaces(d); } if (qatomic_read(&d->pgraph.sync_pending)) { pgraph_gl_sync(d); } if (qatomic_read(&d->pgraph.flush_pending)) { pgraph_gl_flush(d); } if (qatomic_read(&r->shader_cache_writeback_pending)) { pgraph_gl_shader_write_cache_reload_list(&d->pgraph); } qemu_mutex_unlock(&d->pgraph.lock); qemu_mutex_lock(&d->pfifo.lock); } } static void pgraph_gl_pre_savevm_trigger(NV2AState *d) { PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; qatomic_set(&r->download_dirty_surfaces_pending, true); qemu_event_reset(&r->dirty_surfaces_download_complete); } static void pgraph_gl_pre_savevm_wait(NV2AState *d) { PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; qemu_event_wait(&r->dirty_surfaces_download_complete); } static void pgraph_gl_pre_shutdown_trigger(NV2AState *d) { PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; qatomic_set(&r->shader_cache_writeback_pending, true); qemu_event_reset(&r->shader_cache_writeback_complete); } static void pgraph_gl_pre_shutdown_wait(NV2AState *d) { PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; 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 void __attribute__((constructor)) register_renderer(void) { pgraph_renderer_register(&pgraph_gl_renderer); }