diff --git a/hw/xbox/nv2a/pgraph/vk/blit.c b/hw/xbox/nv2a/pgraph/vk/blit.c index 9df86bf539..1b6391a21a 100644 --- a/hw/xbox/nv2a/pgraph/vk/blit.c +++ b/hw/xbox/nv2a/pgraph/vk/blit.c @@ -164,6 +164,7 @@ void pgraph_vk_image_blit(NV2AState *d) // download should be discarded. surf_dest->download_pending = false; surf_dest->draw_dirty = false; + surf_dest->download_generation = surf_dest->draw_generation; } surf_dest->upload_pending = true; pg->draw_time++; diff --git a/hw/xbox/nv2a/pgraph/vk/draw.c b/hw/xbox/nv2a/pgraph/vk/draw.c index 40c6d9f7e2..8df6ebc84b 100644 --- a/hw/xbox/nv2a/pgraph/vk/draw.c +++ b/hw/xbox/nv2a/pgraph/vk/draw.c @@ -1952,12 +1952,18 @@ void pgraph_vk_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta) if (r->color_binding) { r->color_binding->draw_dirty |= color; + if (color) { + r->color_binding->draw_generation++; + } r->color_binding->frame_time = pg->frame_time; r->color_binding->cleared = false; } if (r->zeta_binding) { r->zeta_binding->draw_dirty |= zeta; + if (zeta) { + r->zeta_binding->draw_generation++; + } r->zeta_binding->frame_time = pg->frame_time; r->zeta_binding->cleared = false; } diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 2be05ba080..279dc04b8d 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -130,6 +130,8 @@ typedef struct SurfaceBinding { bool draw_dirty; bool download_pending; bool upload_pending; + uint32_t draw_generation; + uint32_t download_generation; BasicSurfaceFormatInfo fmt; SurfaceFormatInfo host_fmt; @@ -222,6 +224,8 @@ typedef struct TextureBinding { uint64_t hash; unsigned int draw_time; uint32_t submit_time; + unsigned int dirty_check_frame; + bool dirty_check_result; } TextureBinding; typedef struct QueryReport { diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index aacd5b333c..256d253c12 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -478,6 +478,11 @@ static void download_surface(NV2AState *d, SurfaceBinding *surface, bool force) return; } + if (!surface->draw_dirty && + surface->download_generation == surface->draw_generation) { + return; + } + // FIXME: Respect write enable at last TOU? download_surface_to_buffer(d, surface, d->vram_ptr + surface->vram_addr); @@ -491,6 +496,7 @@ static void download_surface(NV2AState *d, SurfaceBinding *surface, bool force) surface->download_pending = false; surface->draw_dirty = false; + surface->download_generation = surface->draw_generation; } void pgraph_vk_wait_for_surface_download(SurfaceBinding *surface) @@ -1375,6 +1381,8 @@ static void populate_surface_binding_target_sized(NV2AState *d, bool color, target->upload_pending = true; target->download_pending = false; target->draw_dirty = false; + target->draw_generation = 0; + target->download_generation = 0; target->dma_addr = dma.address; target->dma_len = dma.limit; target->frame_time = pg->frame_time; diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index b303e73b93..433d3bb6ea 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1162,9 +1162,29 @@ static void create_texture(PGRAPHState *pg, int texture_idx) } if (!surface_to_texture && !possibly_dirty_checked) { - possibly_dirty |= check_texture_possibly_dirty( - d, texture_vram_offset, texture_length, texture_palette_vram_offset, - texture_palette_data_size); + bool skip_dirty_check = binding_found && + snode->dirty_check_frame == pg->frame_time && + !snode->dirty_check_result; + if (!skip_dirty_check) { + bool vram_dirty = check_texture_possibly_dirty( + d, texture_vram_offset, texture_length, + texture_palette_vram_offset, texture_palette_data_size); + possibly_dirty |= vram_dirty; + if (binding_found) { + snode->dirty_check_frame = pg->frame_time; + snode->dirty_check_result = vram_dirty; + } + } + } + + if (binding_found && possibly_dirty && !surface_to_texture) { + bool vram_confirmed_clean = + snode->dirty_check_frame == pg->frame_time && + !snode->dirty_check_result; + if (vram_confirmed_clean) { + snode->possibly_dirty = false; + possibly_dirty = false; + } } // Calculate hash of texture data, if necessary @@ -1428,6 +1448,24 @@ void pgraph_vk_bind_textures(NV2AState *d) return; } + for (int i = 0; i < NV2A_MAX_TEXTURES; i++) { + TextureBinding *binding = r->texture_bindings[i]; + + if (!binding || binding == &r->dummy_texture || !binding->possibly_dirty + || binding->dirty_check_frame == pg->frame_time) { + continue; + } + + bool vram_dirty = check_texture_possibly_dirty( + d, binding->key.texture_vram_offset, binding->key.texture_length, + binding->key.palette_vram_offset, binding->key.palette_length); + binding->dirty_check_frame = pg->frame_time; + binding->dirty_check_result = vram_dirty; + if (!vram_dirty) { + binding->possibly_dirty = false; + } + } + for (int i = 0; i < NV2A_MAX_TEXTURES; i++) { if (!pgraph_is_texture_enabled(pg, i)) { r->texture_bindings[i] = &r->dummy_texture; @@ -1452,6 +1490,9 @@ static void texture_cache_entry_init(Lru *lru, LruNode *node, const void *state) snode->allocation = VK_NULL_HANDLE; snode->image_view = VK_NULL_HANDLE; snode->sampler = VK_NULL_HANDLE; + snode->submit_time = 0; + snode->dirty_check_frame = 0; + snode->dirty_check_result = false; } static void texture_cache_release_node_resources(PGRAPHVkState *r, TextureBinding *snode)