diff --git a/hw/xbox/nv2a/debug.h b/hw/xbox/nv2a/debug.h index 01a1e1875a..2b05539eec 100644 --- a/hw/xbox/nv2a/debug.h +++ b/hw/xbox/nv2a/debug.h @@ -21,6 +21,8 @@ #ifndef HW_NV2A_DEBUG_H #define HW_NV2A_DEBUG_H +#include + #define NV2A_XPRINTF(x, ...) do { \ if (x) { \ fprintf(stderr, "nv2a: " __VA_ARGS__); \ @@ -94,4 +96,57 @@ void gl_debug_frame_terminator(void); #endif +#define NV2A_PROF_COUNTERS_XMAC \ + _X(NV2A_PROF_BEGIN_ENDS) \ + _X(NV2A_PROF_DRAW_ARRAYS) \ + _X(NV2A_PROF_INLINE_BUFFERS) \ + _X(NV2A_PROF_INLINE_ARRAYS) \ + _X(NV2A_PROF_INLINE_ELEMENTS) \ + _X(NV2A_PROF_QUERY) \ + _X(NV2A_PROF_SHADER_GEN) \ + _X(NV2A_PROF_SHADER_BIND) \ + _X(NV2A_PROF_TEX_UPLOAD) \ + _X(NV2A_PROF_TEX_BIND) \ + _X(NV2A_PROF_GEOM_CONV) \ + _X(NV2A_PROF_GEOM_BUFFER_UPDATE_1) \ + _X(NV2A_PROF_GEOM_BUFFER_UPDATE_2) \ + _X(NV2A_PROF_GEOM_BUFFER_UPDATE_3) \ + _X(NV2A_PROF_GEOM_BUFFER_UPDATE_4) \ + _X(NV2A_PROF_SURF_DOWNLOAD) \ + _X(NV2A_PROF_SURF_UPLOAD) \ + _X(NV2A_PROF_SURF_TO_TEX) \ + +enum NV2A_PROF_COUNTERS_ENUM { + #define _X(x) x, + NV2A_PROF_COUNTERS_XMAC + #undef _X + NV2A_PROF__COUNT +}; + +#define NV2A_PROF_NUM_FRAMES 300 + +typedef struct NV2AStats { + int64_t last_flip_time; + unsigned int frame_count; + unsigned int increment_fps; + struct { + int mspf; + int counters[NV2A_PROF__COUNT]; + } frame_working, frame_history[NV2A_PROF_NUM_FRAMES]; + unsigned int frame_ptr; +} NV2AStats; + +#ifdef __cplusplus +extern "C" { +#endif + +extern NV2AStats g_nv2a_stats; + +const char *nv2a_profile_get_counter_name(unsigned int cnt); +int nv2a_profile_get_counter_value(unsigned int cnt); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c index 356b27ae78..5761c33a1a 100644 --- a/hw/xbox/nv2a/pgraph.c +++ b/hw/xbox/nv2a/pgraph.c @@ -25,34 +25,70 @@ #define DBG_SURFACES 0 #define DBG_SURFACE_SYNC 0 -#define DBG_FRAME_TIME 0 static NV2AState *g_nv2a; GloContext *g_nv2a_context_render; GloContext *g_nv2a_context_display; -#if DBG_FRAME_TIME -static int64_t g_last_flip_time; -#endif +NV2AStats g_nv2a_stats; static void nv2a_profile_increment(void) { -#if DBG_FRAME_TIME - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - g_last_flip_time = now; -#endif + int64_t now = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + const int64_t fps_update_interval = 250000; + g_nv2a_stats.last_flip_time = now; + + static int64_t frame_count = 0; + frame_count++; + + static int64_t ts = 0; + int64_t delta = now - ts; + if (delta >= fps_update_interval) { + g_nv2a_stats.increment_fps = frame_count * 1000000 / delta; + ts = now; + frame_count = 0; + } } static void nv2a_profile_flip_stall(void) { -#if DBG_FRAME_TIME glFinish(); - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - int64_t render_time = (now-g_last_flip_time)/1000000; - NV2A_XPRINTF(DBG_FRAME_TIME, - "**** Render Time = %ld ms (~%.2f FPS)\n", - render_time, 1000.0f/(float)(render_time > 0 ? render_time : 1)); -#endif + + int64_t now = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + int64_t render_time = (now-g_nv2a_stats.last_flip_time)/1000; + + g_nv2a_stats.frame_working.mspf = render_time; + g_nv2a_stats.frame_history[g_nv2a_stats.frame_ptr] = + g_nv2a_stats.frame_working; + g_nv2a_stats.frame_ptr = + (g_nv2a_stats.frame_ptr + 1) % NV2A_PROF_NUM_FRAMES; + g_nv2a_stats.frame_count++; + memset(&g_nv2a_stats.frame_working, 0, sizeof(g_nv2a_stats.frame_working)); +} + +static void nv2a_profile_inc_counter(enum NV2A_PROF_COUNTERS_ENUM cnt) +{ + g_nv2a_stats.frame_working.counters[cnt] += 1; +} + +const char *nv2a_profile_get_counter_name(unsigned int cnt) +{ + const char *default_names[NV2A_PROF__COUNT] = { + #define _X(x) stringify(x), + NV2A_PROF_COUNTERS_XMAC + #undef _X + }; + + assert(cnt < NV2A_PROF__COUNT); + return default_names[cnt] + 10; /* 'NV2A_PROF_' */ +} + +int nv2a_profile_get_counter_value(unsigned int cnt) +{ + assert(cnt < NV2A_PROF__COUNT); + unsigned int idx = (g_nv2a_stats.frame_ptr + NV2A_PROF_NUM_FRAMES - 1) % + NV2A_PROF_NUM_FRAMES; + return g_nv2a_stats.frame_history[idx].counters[cnt]; } static const GLenum pgraph_texture_min_filter_map[] = { @@ -1908,9 +1944,12 @@ void pgraph_method(NV2AState *d, if (parameter == NV097_SET_BEGIN_END_OP_END) { + nv2a_profile_inc_counter(NV2A_PROF_BEGIN_ENDS); + assert(pg->shader_binding); if (pg->draw_arrays_length) { + nv2a_profile_inc_counter(NV2A_PROF_DRAW_ARRAYS); NV2A_GL_DPRINTF(false, "Draw Arrays"); @@ -1925,6 +1964,7 @@ void pgraph_method(NV2AState *d, pg->gl_draw_arrays_count, pg->draw_arrays_length); } else if (pg->inline_buffer_length) { + nv2a_profile_inc_counter(NV2A_PROF_INLINE_BUFFERS); NV2A_GL_DPRINTF(false, "Inline Buffer"); @@ -1936,7 +1976,7 @@ void pgraph_method(NV2AState *d, VertexAttribute *attribute = &pg->vertex_attributes[i]; if (attribute->inline_buffer) { - + nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_3); glBindBuffer(GL_ARRAY_BUFFER, attribute->gl_inline_buffer); glBufferData(GL_ARRAY_BUFFER, @@ -1962,6 +2002,7 @@ void pgraph_method(NV2AState *d, glDrawArrays(pg->shader_binding->gl_primitive_mode, 0, pg->inline_buffer_length); } else if (pg->inline_array_length) { + nv2a_profile_inc_counter(NV2A_PROF_INLINE_ARRAYS); NV2A_GL_DPRINTF(false, "Inline Array"); @@ -1973,6 +2014,7 @@ void pgraph_method(NV2AState *d, glDrawArrays(pg->shader_binding->gl_primitive_mode, 0, index_count); } else if (pg->inline_elements_length) { + nv2a_profile_inc_counter(NV2A_PROF_INLINE_ELEMENTS); NV2A_GL_DPRINTF(false, "Inline Elements"); @@ -2008,6 +2050,7 @@ void pgraph_method(NV2AState *d, /* End of visibility testing */ if (pg->zpass_pixel_count_enable) { + nv2a_profile_inc_counter(NV2A_PROF_QUERY); glEndQuery(GL_SAMPLES_PASSED); } @@ -3383,6 +3426,7 @@ static void pgraph_bind_shaders(PGRAPHState *pg) pg->shader_binding = cached_shader; } else { pg->shader_binding = generate_shaders(state); + nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN); /* cache it */ ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state)); @@ -3392,6 +3436,9 @@ static void pgraph_bind_shaders(PGRAPHState *pg) } bool binding_changed = (pg->shader_binding != old_binding); + if (binding_changed) { + nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND); + } glUseProgram(pg->shader_binding->gl_program); @@ -3577,6 +3624,8 @@ static void pgraph_render_surface_to_texture(NV2AState *d, const ColorFormatInfo *f = &kelvin_color_format_map[texture_shape->color_format]; assert(texture_shape->color_format < ARRAY_SIZE(kelvin_color_format_map)); + nv2a_profile_inc_counter(NV2A_PROF_SURF_TO_TEX); + glActiveTexture(GL_TEXTURE0 + texture_unit); // Reallocate space for new texture, bind as render target @@ -4011,6 +4060,8 @@ static void pgraph_download_surface_data(NV2AState *d, // FIXME: Respect write enable at last TOU? + nv2a_profile_inc_counter(NV2A_PROF_SURF_DOWNLOAD); + PGRAPHState *pg = &d->pgraph; uint8_t *data = d->vram_ptr; uint8_t *buf = data + surface->vram_addr; @@ -4116,6 +4167,8 @@ static void pgraph_upload_surface_data( return; } + nv2a_profile_inc_counter(NV2A_PROF_SURF_UPLOAD); + NV2A_XPRINTF(DBG_SURFACE_SYNC, "[RAM->GPU] %s (%s) surface @ %" HWADDR_PRIx " (w=%d,h=%d,p=%d,bpp=%d)\n", @@ -4687,6 +4740,8 @@ static void pgraph_bind_textures(NV2AState *d) continue; } + nv2a_profile_inc_counter(NV2A_PROF_TEX_BIND); + if (!pg->texture_dirty[i] && pg->texture_binding[i]) { glBindTexture(pg->texture_binding[i]->gl_target, pg->texture_binding[i]->gl_texture); @@ -5060,6 +5115,7 @@ static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, addr, end - addr, DIRTY_MEMORY_NV2A)) { + nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_1); glBufferSubData(GL_ARRAY_BUFFER, addr, end - addr, d->vram_ptr + addr); } } @@ -5104,6 +5160,7 @@ static void pgraph_bind_vertex_attributes(NV2AState *d, if (attribute->needs_conversion) { NV2A_DPRINTF("converted %d\n", i); + nv2a_profile_inc_counter(NV2A_PROF_GEOM_CONV); unsigned int out_stride = attribute->converted_size * attribute->converted_count; @@ -5209,6 +5266,7 @@ static unsigned int pgraph_bind_inline_array(NV2AState *d) NV2A_DPRINTF("draw inline array %d, %d\n", vertex_size, index_count); + nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_2); glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); glBufferData(GL_ARRAY_BUFFER, pg->inline_array_length*4, pg->inline_array, GL_DYNAMIC_DRAW); @@ -5321,6 +5379,7 @@ static void upload_gl_texture(GLenum gl_target, const uint8_t *palette_data) { ColorFormatInfo f = kelvin_color_format_map[s.color_format]; + nv2a_profile_inc_counter(NV2A_PROF_TEX_UPLOAD); switch(gl_target) { case GL_TEXTURE_1D: