From ad2aa65503d9f40760a35bffc35e0bc6966cb903 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Mon, 30 Mar 2026 17:51:41 -0400 Subject: [PATCH] Port hakuX Phase 1 super fast path foundation --- hw/xbox/nv2a/pgraph/vk/draw.c | 72 ++++++++++++++++++++++++------- hw/xbox/nv2a/pgraph/vk/renderer.h | 7 +++ hw/xbox/nv2a/pgraph/vk/texture.c | 18 ++++++++ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/vk/draw.c b/hw/xbox/nv2a/pgraph/vk/draw.c index 02130fed4d..4d1a2f64cb 100644 --- a/hw/xbox/nv2a/pgraph/vk/draw.c +++ b/hw/xbox/nv2a/pgraph/vk/draw.c @@ -764,17 +764,8 @@ static bool check_pipeline_dirty(PGRAPHState *pg) r->texture_bindings_changed || check_render_pass_dirty(pg)) { return true; } - - const unsigned int regs[] = { - NV_PGRAPH_BLEND, NV_PGRAPH_BLENDCOLOR, NV_PGRAPH_CONTROL_0, - NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_2, NV_PGRAPH_CONTROL_3, - NV_PGRAPH_SETUPRASTER, NV_PGRAPH_ZOFFSETBIAS, NV_PGRAPH_ZOFFSETFACTOR, - }; - - for (int i = 0; i < ARRAY_SIZE(regs); i++) { - if (pgraph_is_reg_dirty(pg, regs[i])) { - return true; - } + if (pg->pipeline_state_gen != r->last_pipeline_state_gen) { + return true; } // FIXME: Use dirty bits instead @@ -827,16 +818,31 @@ static void create_pipeline(PGRAPHState *pg) NV2AState *d = container_of(pg, NV2AState, pgraph); PGRAPHVkState *r = pg->vk_renderer_state; + bool textures_clean = pgraph_vk_check_textures_fast_skip(pg); - pgraph_vk_bind_textures(d); + if (r->pipeline_binding && + pg->texture_state_gen == r->last_texture_state_gen && + textures_clean && + !check_render_pass_dirty(pg) && + pg->shader_state_gen == r->last_shader_state_gen && + pg->pipeline_state_gen == r->last_pipeline_state_gen && + pg->primitive_mode == r->shader_binding->state.geom.primitive_mode) { + NV2A_VK_DGROUP_END(); + return; + } + + if (pg->texture_state_gen != r->last_texture_state_gen || !textures_clean) { + pgraph_vk_bind_textures(d); + r->last_texture_state_gen = pg->texture_state_gen; + } pgraph_vk_bind_shaders(pg); - // FIXME: If nothing was dirty, don't even try creating the key or hashing. - // Just use the same pipeline. bool pipeline_dirty = check_pipeline_dirty(pg); pgraph_clear_dirty_reg_map(pg); - // FIXME: We could clear less + r->last_pipeline_state_gen = pg->pipeline_state_gen; + r->last_shader_state_gen = pg->shader_state_gen; + r->last_any_reg_gen = pg->any_reg_gen; if (r->pipeline_binding && !pipeline_dirty) { NV2A_VK_DPRINTF("Cache hit"); @@ -1531,15 +1537,42 @@ void pgraph_vk_end_nondraw_commands(PGRAPHState *pg, VkCommandBuffer cmd) static void begin_pre_draw(PGRAPHState *pg) { PGRAPHVkState *r = pg->vk_renderer_state; + bool textures_clean = pgraph_vk_check_textures_fast_skip(pg); assert(r->color_binding || r->zeta_binding); assert(!r->color_binding || r->color_binding->initialized); assert(!r->zeta_binding || r->zeta_binding->initialized); + if (!pg->clearing && + r->pipeline_binding && + r->shader_binding && + r->pipeline_binding->pipeline != VK_NULL_HANDLE && + !r->pipeline_binding_changed && + r->in_command_buffer && + r->in_render_pass && + r->framebuffer_index > 0 && + !r->framebuffer_dirty && + !r->uniforms_changed && + r->descriptor_set_index > 0 && + pg->texture_state_gen == r->last_texture_state_gen && + textures_clean && + pg->shader_state_gen == r->last_shader_state_gen && + pg->pipeline_state_gen == r->last_pipeline_state_gen && + pg->any_reg_gen == r->last_any_reg_gen && + pg->primitive_mode == r->shader_binding->state.geom.primitive_mode && + !pg->program_data_dirty && + pg->vertex_attr_gen == r->pipeline_vertex_attr_gen) { + r->pre_draw_skipped = true; + return; + } + + r->pre_draw_skipped = false; + if (pg->clearing) { create_clear_pipeline(pg); } else { create_pipeline(pg); + r->pipeline_vertex_attr_gen = pg->vertex_attr_gen; } bool render_pass_dirty = r->pipeline_binding->render_pass != r->render_pass; @@ -1556,12 +1589,18 @@ static void begin_pre_draw(PGRAPHState *pg) } if (!pg->clearing) { pgraph_vk_update_descriptor_sets(pg); + r->shader_bindings_changed = false; + r->texture_bindings_changed = false; } if (r->framebuffer_index == 0) { create_frame_buffer(pg); } pgraph_vk_ensure_command_buffer(pg); + pgraph_clear_dirty_reg_map(pg); + r->last_any_reg_gen = pg->any_reg_gen; + r->last_shader_state_gen = pg->shader_state_gen; + r->last_pipeline_state_gen = pg->pipeline_state_gen; } static float clamp_line_width_to_device_limits(PGRAPHState *pg, float width) @@ -1615,6 +1654,7 @@ static void begin_draw(PGRAPHState *pg) nv2a_profile_inc_counter(NV2A_PROF_PIPELINE_BIND); vkCmdBindPipeline(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, r->pipeline_binding->pipeline); + r->pipeline_binding_changed = false; r->pipeline_binding->draw_time = pg->draw_time; unsigned int vp_width = pg->surface_binding_dim.width, @@ -1658,7 +1698,7 @@ static void begin_draw(PGRAPHState *pg) } } - if (!pg->clearing) { + if (!pg->clearing && !r->pre_draw_skipped) { bind_descriptor_sets(pg); push_vertex_attr_values(pg); } diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 25f43f70c8..f01b1648f9 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -427,6 +427,10 @@ typedef struct PGRAPHVkState { size_t num_vertex_ram_buffer_syncs; unsigned long *uploaded_bitmap; size_t bitmap_size; + uint32_t pipeline_vertex_attr_gen; + uint32_t last_shader_state_gen; + uint32_t last_pipeline_state_gen; + uint32_t last_any_reg_gen; VkVertexInputAttributeDescription vertex_attribute_descriptions[NV2A_VERTEXSHADER_ATTRIBUTES]; int vertex_attribute_to_description_location[NV2A_VERTEXSHADER_ATTRIBUTES]; @@ -454,6 +458,7 @@ typedef struct PGRAPHVkState { VkImageView tex_surface_direct_views[NV2A_MAX_TEXTURES]; VkImageLayout tex_surface_direct_layout[NV2A_MAX_TEXTURES]; TextureBinding dummy_texture; + uint32_t last_texture_state_gen; bool texture_bindings_changed; VkFormatProperties *texture_format_properties; @@ -472,6 +477,7 @@ typedef struct PGRAPHVkState { uint64_t uniform_buffer_hashes[2]; size_t uniform_buffer_offsets[2]; bool uniforms_changed; + bool pre_draw_skipped; VkQueryPool query_pool; int max_queries_in_flight; // FIXME: Move out to constant @@ -618,6 +624,7 @@ bool pgraph_vk_gl_external_memory_available(void); void pgraph_vk_init_textures(PGRAPHState *pg); void pgraph_vk_finalize_textures(PGRAPHState *pg); void pgraph_vk_bind_textures(NV2AState *d); +bool pgraph_vk_check_textures_fast_skip(PGRAPHState *pg); void pgraph_vk_mark_textures_possibly_dirty(NV2AState *d, hwaddr addr, hwaddr size); void pgraph_vk_trim_texture_cache(PGRAPHState *pg); diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 54ebc98dee..b6e2758308 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1898,6 +1898,24 @@ static bool check_textures_dirty(PGRAPHState *pg) return false; } +bool pgraph_vk_check_textures_fast_skip(PGRAPHState *pg) +{ + PGRAPHVkState *r = pg->vk_renderer_state; + + for (int i = 0; i < NV2A_MAX_TEXTURES; i++) { + if (!r->texture_bindings[i] || pg->texture_dirty[i] || + r->tex_surface_direct[i]) { + return false; + } + if (r->texture_bindings[i] != &r->dummy_texture && + r->texture_bindings[i]->possibly_dirty) { + return false; + } + } + + return true; +} + static void update_timestamps(PGRAPHVkState *r) { for (int i = 0; i < ARRAY_SIZE(r->texture_bindings); i++) {