diff --git a/config_spec.yml b/config_spec.yml index d013c806ac..3744025e9b 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -146,6 +146,9 @@ display: surface_scale: type: integer default: 1 + anisotropic_filter_level: + type: integer + default: 1 window: fullscreen_on_startup: bool fullscreen_exclusive: bool diff --git a/hw/xbox/nv2a/nv2a.h b/hw/xbox/nv2a/nv2a.h index 2a9b7312d4..6bbf47a7f9 100644 --- a/hw/xbox/nv2a/nv2a.h +++ b/hw/xbox/nv2a/nv2a.h @@ -27,6 +27,8 @@ int nv2a_get_framebuffer_surface(void); void nv2a_release_framebuffer_surface(void); void nv2a_set_surface_scale_factor(unsigned int scale); unsigned int nv2a_get_surface_scale_factor(void); +void nv2a_set_anisotropic_filter_level(unsigned int level_po2); +unsigned int nv2a_get_anisotropic_filter_level(void); const uint8_t *nv2a_get_dac_palette(void); int nv2a_get_screen_off(void); diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c index 36b8029439..49aa4c7d43 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.c +++ b/hw/xbox/nv2a/pgraph/gl/renderer.c @@ -194,6 +194,8 @@ static PGRAPHRenderer pgraph_gl_renderer = { .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, + .set_anisotropic_filter_level = pgraph_gl_set_anisotropic_filter_level, + .get_anisotropic_filter_level = pgraph_gl_get_anisotropic_filter_level, .get_framebuffer_surface = pgraph_gl_get_framebuffer_surface, } }; diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index 3529006898..7e7e0ef6bd 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -284,6 +284,9 @@ bool pgraph_gl_shader_load_from_memory(ShaderBinding *snode); void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg); void pgraph_gl_set_surface_scale_factor(NV2AState *d, unsigned int scale); unsigned int pgraph_gl_get_surface_scale_factor(NV2AState *d); +void pgraph_gl_set_anisotropic_filter_level(NV2AState *d, unsigned int level_po2); +unsigned int pgraph_gl_get_anisotropic_filter_level(NV2AState *d); +void pgraph_gl_reload_anisotropic_filter_level(PGRAPHState *pg); int pgraph_gl_get_framebuffer_surface(NV2AState *d); #endif diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index 317fcc1070..4964075ad7 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -24,6 +24,7 @@ #include "hw/xbox/nv2a/pgraph/swizzle.h" #include "hw/xbox/nv2a/pgraph/s3tc.h" #include "hw/xbox/nv2a/pgraph/texture.h" +#include "ui/xemu-settings.h" #include "debug.h" #include "renderer.h" @@ -112,6 +113,7 @@ static void apply_texture_parameters(TextureBinding *binding, unsigned int dimensionality, unsigned int filter, unsigned int address, + unsigned int anisotropic_filter_level, bool is_bordered, uint32_t border_color) { @@ -174,6 +176,8 @@ static void apply_texture_parameters(TextureBinding *binding, needs_border_color = needs_border_color || binding->addrp == NV_PGRAPH_TEXADDRESS0_ADDRU_BORDER; } + glTexParameterf(binding->gl_target, GL_TEXTURE_MAX_ANISOTROPY, 1 << anisotropic_filter_level); + if (!is_bordered && needs_border_color) { if (!binding->border_color_set || binding->border_color != border_color) { /* FIXME: Color channels might be wrong order */ @@ -260,6 +264,7 @@ void pgraph_gl_bind_textures(NV2AState *d) state.dimensionality, filter, address, + pg->anisotropic_filter_level, state.border, border_color); continue; @@ -370,6 +375,7 @@ void pgraph_gl_bind_textures(NV2AState *d) state.dimensionality, filter, address, + pg->anisotropic_filter_level, state.border, border_color); @@ -771,6 +777,53 @@ static bool texture_cache_entry_compare(Lru *lru, LruNode *node, void *key) return memcmp(&tnode->key, key, sizeof(TextureKey)); } +void pgraph_gl_set_anisotropic_filter_level(NV2AState *d, unsigned int level_po2) +{ + PGRAPHState *pg = &d->pgraph; + PGRAPHGLState *r = pg->gl_renderer_state; + + g_config.display.quality.anisotropic_filter_level = level_po2; + + qemu_mutex_lock(&d->pfifo.lock); + qatomic_set(&d->pfifo.halt, true); + qemu_mutex_unlock(&d->pfifo.lock); + + qemu_mutex_lock(&d->pgraph.lock); + qemu_event_reset(&r->dirty_surfaces_download_complete); + qatomic_set(&r->download_dirty_surfaces_pending, true); + qemu_mutex_unlock(&d->pgraph.lock); + qemu_mutex_lock(&d->pfifo.lock); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + qemu_event_wait(&r->dirty_surfaces_download_complete); + + qemu_mutex_lock(&d->pgraph.lock); + qemu_event_reset(&d->pgraph.flush_complete); + qatomic_set(&d->pgraph.flush_pending, true); + qemu_mutex_unlock(&d->pgraph.lock); + qemu_mutex_lock(&d->pfifo.lock); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + qemu_event_wait(&d->pgraph.flush_complete); + + qemu_mutex_lock(&d->pfifo.lock); + qatomic_set(&d->pfifo.halt, false); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + + pgraph_gl_reload_anisotropic_filter_level(&d->pgraph); +} + +unsigned int pgraph_gl_get_anisotropic_filter_level(NV2AState *d) +{ + return d->pgraph.anisotropic_filter_level; +} + +void pgraph_gl_reload_anisotropic_filter_level(PGRAPHState *pg) +{ + pg->anisotropic_filter_level = g_config.display.quality.anisotropic_filter_level; +} + void pgraph_gl_init_textures(NV2AState *d) { PGRAPHState *pg = &d->pgraph; diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c index 9db475796e..02ee0d52b0 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.c +++ b/hw/xbox/nv2a/pgraph/pgraph.c @@ -406,6 +406,35 @@ unsigned int nv2a_get_surface_scale_factor(void) return s; } +void nv2a_set_anisotropic_filter_level(unsigned int level_po2) +{ + NV2AState *d = g_nv2a; + + bql_unlock(); + qemu_mutex_lock(&d->pgraph.renderer_lock); + if (d->pgraph.renderer->ops.set_anisotropic_filter_level) { + d->pgraph.renderer->ops.set_anisotropic_filter_level(d, level_po2); + } + qemu_mutex_unlock(&d->pgraph.renderer_lock); + bql_lock(); +} + +unsigned int nv2a_get_anisotropic_filter_level(void) +{ + NV2AState *d = g_nv2a; + int s = 1; + + bql_unlock(); + qemu_mutex_lock(&d->pgraph.renderer_lock); + if (d->pgraph.renderer->ops.get_anisotropic_filter_level) { + s = d->pgraph.renderer->ops.get_anisotropic_filter_level(d); + } + qemu_mutex_unlock(&d->pgraph.renderer_lock); + bql_lock(); + + return s; +} + #define METHOD_ADDR(gclass, name) \ gclass ## _ ## name #define METHOD_ADDR_TO_INDEX(x) ((x)>>2) @@ -3022,4 +3051,3 @@ void pgraph_pre_shutdown_wait(NV2AState *d) PGRAPHState *pg = &d->pgraph; pg->renderer->ops.pre_shutdown_wait(d); } - diff --git a/hw/xbox/nv2a/pgraph/pgraph.h b/hw/xbox/nv2a/pgraph/pgraph.h index 64b671e71d..a2a58be27f 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.h +++ b/hw/xbox/nv2a/pgraph/pgraph.h @@ -120,6 +120,8 @@ typedef struct PGRAPHRenderer { void (*surface_update)(NV2AState *d, bool upload, bool color_write, bool zeta_write); void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale); unsigned int (*get_surface_scale_factor)(NV2AState *d); + void (*set_anisotropic_filter_level)(NV2AState *d, unsigned int level_po2); + unsigned int (*get_anisotropic_filter_level)(NV2AState *d); int (*get_framebuffer_surface)(NV2AState *d); } ops; } PGRAPHRenderer; @@ -241,6 +243,9 @@ typedef struct PGRAPHState { unsigned int surface_scale_factor; uint8_t *scale_buf; + // Defined as a power of 2 + unsigned int anisotropic_filter_level; + const PGRAPHRenderer *renderer; union { PGRAPHNullState *null_renderer_state; diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.c b/hw/xbox/nv2a/pgraph/vk/renderer.c index 3dbc724b95..b45949dd61 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.c +++ b/hw/xbox/nv2a/pgraph/vk/renderer.c @@ -164,7 +164,7 @@ static void pgraph_vk_pre_shutdown_trigger(NV2AState *d) static void pgraph_vk_pre_shutdown_wait(NV2AState *d) { - // qemu_event_wait(&d->pgraph.vk_renderer_state->shader_cache_writeback_complete); + // qemu_event_wait(&d->pgraph.vk_renderer_state->shader_cache_writeback_complete); } static int pgraph_vk_get_framebuffer_surface(NV2AState *d) @@ -226,6 +226,8 @@ static PGRAPHRenderer pgraph_vk_renderer = { .surface_update = pgraph_vk_surface_update, .set_surface_scale_factor = pgraph_vk_set_surface_scale_factor, .get_surface_scale_factor = pgraph_vk_get_surface_scale_factor, + .set_anisotropic_filter_level = pgraph_vk_set_anisotropic_filter_level, + .get_anisotropic_filter_level = pgraph_vk_get_anisotropic_filter_level, .get_framebuffer_surface = pgraph_vk_get_framebuffer_surface, } }; diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index eb0726ac2f..197ff0b244 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -544,6 +544,9 @@ void pgraph_vk_bind_textures(NV2AState *d); void pgraph_vk_mark_textures_possibly_dirty(NV2AState *d, hwaddr addr, hwaddr size); void pgraph_vk_trim_texture_cache(PGRAPHState *pg); +void pgraph_vk_set_anisotropic_filter_level(NV2AState *d, unsigned int level_po2); +unsigned int pgraph_vk_get_anisotropic_filter_level(NV2AState *d); +void pgraph_vk_reload_anisotropic_filter_level(PGRAPHState *pg); // shaders.c void pgraph_vk_init_shaders(PGRAPHState *pg); diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 69043b2e04..a878afb7fe 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -28,6 +28,7 @@ #include "hw/xbox/nv2a/pgraph/swizzle.h" #include "qemu/fast-hash.h" #include "qemu/lru.h" +#include "ui/xemu-settings.h" #include "renderer.h" static void texture_cache_release_node_resources(PGRAPHVkState *r, TextureBinding *snode); @@ -1345,9 +1346,8 @@ static void create_texture(PGRAPHState *pg, int texture_idx) GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV)), .addressModeW = lookup_texture_address_mode( GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP)), - .anisotropyEnable = VK_FALSE, - // .anisotropyEnable = VK_TRUE, - // .maxAnisotropy = properties.limits.maxSamplerAnisotropy, + .anisotropyEnable = pg->anisotropic_filter_level >= 1, + .maxAnisotropy = 1 << pg->anisotropic_filter_level, .borderColor = vk_border_color, .compareEnable = VK_FALSE, .compareOp = VK_COMPARE_OP_ALWAYS, @@ -1529,6 +1529,50 @@ void pgraph_vk_trim_texture_cache(PGRAPHState *pg) NV2A_VK_DPRINTF("Evicted %d textures, %d remain", num_evicted, r->texture_cache.num_used); } +void pgraph_vk_set_anisotropic_filter_level(NV2AState *d, unsigned int level_po2) +{ + g_config.display.quality.anisotropic_filter_level = level_po2; + + qemu_mutex_lock(&d->pfifo.lock); + qatomic_set(&d->pfifo.halt, true); + qemu_mutex_unlock(&d->pfifo.lock); + + qemu_mutex_lock(&d->pgraph.lock); + qemu_event_reset(&d->pgraph.vk_renderer_state->dirty_surfaces_download_complete); + qatomic_set(&d->pgraph.vk_renderer_state->download_dirty_surfaces_pending, true); + qemu_mutex_unlock(&d->pgraph.lock); + qemu_mutex_lock(&d->pfifo.lock); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + qemu_event_wait(&d->pgraph.vk_renderer_state->dirty_surfaces_download_complete); + + qemu_mutex_lock(&d->pgraph.lock); + qemu_event_reset(&d->pgraph.flush_complete); + qatomic_set(&d->pgraph.flush_pending, true); + qemu_mutex_unlock(&d->pgraph.lock); + qemu_mutex_lock(&d->pfifo.lock); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + qemu_event_wait(&d->pgraph.flush_complete); + + qemu_mutex_lock(&d->pfifo.lock); + qatomic_set(&d->pfifo.halt, false); + pfifo_kick(d); + qemu_mutex_unlock(&d->pfifo.lock); + + pgraph_vk_reload_anisotropic_filter_level(&d->pgraph); +} + +unsigned int pgraph_vk_get_anisotropic_filter_level(NV2AState *d) +{ + return d->pgraph.anisotropic_filter_level; +} + +void pgraph_vk_reload_anisotropic_filter_level(PGRAPHState *pg) +{ + pg->anisotropic_filter_level = g_config.display.quality.anisotropic_filter_level; +} + void pgraph_vk_init_textures(PGRAPHState *pg) { PGRAPHVkState *r = pg->vk_renderer_state; diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index 9bb5dcf33f..3e391f4f6d 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -172,14 +172,14 @@ void MainMenuInputView::Draw() driver = DRIVER_DUKE_DISPLAY_NAME; else if (strcmp(driver, DRIVER_S) == 0) driver = DRIVER_S_DISPLAY_NAME; - + ImGui::SetNextItemWidth(-FLT_MIN); if (ImGui::BeginCombo("###InputDrivers", driver, ImGuiComboFlags_NoArrowButton)) { const char *available_drivers[] = { DRIVER_DUKE, DRIVER_S }; - const char *driver_display_names[] = { - DRIVER_DUKE_DISPLAY_NAME, - DRIVER_S_DISPLAY_NAME + const char *driver_display_names[] = { + DRIVER_DUKE_DISPLAY_NAME, + DRIVER_S_DISPLAY_NAME }; bool is_selected = false; int num_drivers = sizeof(driver_display_names) / sizeof(driver_display_names[0]); @@ -513,10 +513,21 @@ void MainMenuDisplayView::Draw() "8x\0" "9x\0" "10x\0", - "Increase surface scaling factor for higher quality")) { + "Increase surface scaling factor for higher overall quality")) { nv2a_set_surface_scale_factor(rendering_scale+1); } + int anisotropic_filter_level = nv2a_get_anisotropic_filter_level(); + if (ChevronCombo("Anisotropic filtering level", &anisotropic_filter_level, + "Disabled\0" + "2x\0" + "4x\0" + "8x\0" + "16x\0", + "Increase anisotropic filtering level for sharper textures at oblique angles")) { + nv2a_set_anisotropic_filter_level(anisotropic_filter_level); + } + SectionTitle("Window"); bool fs = xemu_is_fullscreen(); if (Toggle("Fullscreen", &fs, "Enable fullscreen now")) { diff --git a/ui/xui/menubar.cc b/ui/xui/menubar.cc index f0b6c1d5c2..f60eba95b4 100644 --- a/ui/xui/menubar.cc +++ b/ui/xui/menubar.cc @@ -192,6 +192,16 @@ void ShowMainMenu() nv2a_set_surface_scale_factor(rendering_scale + 1); } + int anisotropic_filter_level = nv2a_get_anisotropic_filter_level(); + if (ImGui::Combo("Anisotropic Filtering Level", &anisotropic_filter_level, + "Disabled\0" + "2x\0" + "4x\0" + "8x\0" + "16x\0")) { + nv2a_set_anisotropic_filter_level(anisotropic_filter_level); + } + ImGui::Combo("Display Mode", &g_config.display.ui.fit, "Center\0Scale\0Stretch\0"); ImGui::SameLine();