From b16c5c51312b34fa6fbcffee3d3495f4b3a3185c Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 3 Nov 2023 23:09:07 -0400 Subject: [PATCH 1/6] Add separate "Terrain Normals / Specular" quality setting Controls the max texture size used specifically for terrain normal & specular maps. Turning this down to "Medium Quality" (512) yields decent-looking normal / specular mapping effects and a potentially significant performance boost. --- src/configuration.cpp | 9 +++++++ src/frontend.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++ src/frontend.h | 2 ++ src/terrain.cpp | 40 ++++++++++++++++++++++++------- src/terrain.h | 3 +++ 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/src/configuration.cpp b/src/configuration.cpp index 5653516880f..bf844152e1a 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -603,6 +603,14 @@ bool loadConfig() debug(LOG_WARNING, "Unsupported / invalid terrainShaderQuality value: %d; using default", intValue); } } + if (auto value = iniGetIntegerOpt("terrainNormalSpecularQuality")) + { + auto intValue = value.value(); + if (!setTerrainMappingTexturesMaxSize(intValue)) + { + debug(LOG_WARNING, "Unsupported / invalid terrainNormalSpecularQuality value: %d; using default", intValue); + } + } war_setShadowFilterSize(iniGetInteger("shadowFilterSize", (int)war_getShadowFilterSize()).value()); if (auto value = iniGetIntegerOpt("shadowMapResolution")) { @@ -783,6 +791,7 @@ bool saveConfig() iniSetInteger("fogEnd", war_getFogEnd()); iniSetInteger("fogStart", war_getFogStart()); iniSetInteger("terrainShaderQuality", getTerrainShaderQuality()); + iniSetInteger("terrainNormalSpecularQuality", getTerrainMappingTexturesMaxSize()); iniSetInteger("shadowFilterSize", (int)war_getShadowFilterSize()); iniSetInteger("shadowMapResolution", (int)war_getShadowMapResolution()); iniSetInteger("configVersion", CURRCONFVERSION); diff --git a/src/frontend.cpp b/src/frontend.cpp index e004a93cc22..39eb400e0f4 100644 --- a/src/frontend.cpp +++ b/src/frontend.cpp @@ -969,6 +969,55 @@ static std::shared_ptr makeTerrainQualityDropdown() return Margin(0, 10).wrap(dropdown); } +static std::shared_ptr makeTerrainNormalSpecularDropdown() +{ + std::vector> dropDownChoices = { + {_("Medium Quality"), 512}, + {_("High Quality"), 1024}, + }; + + size_t currentSettingIdx = 0; + auto currValue = getTerrainMappingTexturesMaxSize(); + auto it = std::find_if(dropDownChoices.begin(), dropDownChoices.end(), [currValue](const std::tuple& item) -> bool { + return std::get<1>(item) == currValue; + }); + if (it != dropDownChoices.end()) + { + currentSettingIdx = it - dropDownChoices.begin(); + } + + auto dropdown = std::make_shared(); + dropdown->id = FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY_R; + dropdown->setListHeight(FRONTEND_BUTHEIGHT * std::min(5, dropDownChoices.size())); + const auto paddingSize = 10; + + for (const auto& option : dropDownChoices) + { + bool supportedMode = true; + auto item = makeTextButton(0, std::get<0>(option).toUtf8(), supportedMode ? 0 : WBUT_DISABLE); + if (!supportedMode) + { + item->setTip(_("Terrain quality mode not available.")); + } + dropdown->addItem(Margin(0, paddingSize).wrap(item)); + } + + dropdown->setSelectedIndex(currentSettingIdx); + + dropdown->setCanChange([dropDownChoices](DropdownWidget &widget, size_t newIndex, std::shared_ptr newSelectedWidget) -> bool { + ASSERT_OR_RETURN(false, newIndex < dropDownChoices.size(), "Invalid index"); + auto newMode = std::get<1>(dropDownChoices.at(newIndex)); + if (!setTerrainMappingTexturesMaxSize(newMode)) + { + debug(LOG_ERROR, "Failed to set terrain mapping texture quality: %d", newMode); + return false; + } + return true; + }); + + return Margin(0, 10).wrap(dropdown); +} + static std::shared_ptr makeShadowMapResolutionDropdown() { std::vector> dropDownChoices = { @@ -1117,6 +1166,12 @@ void startGraphicsOptionsMenu() grid->place({1, 1, false}, row, makeTerrainQualityDropdown()); row.start++; + // Terrain Normals / Specular + // TRANSLATORS: "Normals" and "Specular" refer to Normal Mapping and Specular Mapping (technical, graphics-related terms) - they may or may not make sense to translate + grid->place({0}, row, addMargin(makeTextButton(FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY, _("Terrain Normals / Specular"), WBUT_SECONDARY))); + grid->place({1, 1, false}, row, makeTerrainNormalSpecularDropdown()); + row.start++; + //////////// // Shadows grid->place({0}, row, addMargin(makeTextButton(FRONTEND_SHADOWS, _("Shadows"), WBUT_SECONDARY))); diff --git a/src/frontend.h b/src/frontend.h index d5dc942b778..3fcce6106c5 100644 --- a/src/frontend.h +++ b/src/frontend.h @@ -302,6 +302,8 @@ enum FRONTEND_SSHAKE_R, FRONTEND_TERRAIN_QUALITY, FRONTEND_TERRAIN_QUALITY_R, + FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY, + FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY_R, FRONTEND_GROUPS, FRONTEND_GROUPS_R, diff --git a/src/terrain.cpp b/src/terrain.cpp index 4f425d08d77..b1d87a43596 100644 --- a/src/terrain.cpp +++ b/src/terrain.cpp @@ -167,6 +167,7 @@ bool drawRangeElementsStarted = false; TerrainShaderQuality terrainShaderQuality = TerrainShaderQuality::UNINITIALIZED_PICK_DEFAULT; TerrainShaderType terrainShaderType = TerrainShaderType::SINGLE_PASS; bool initializedTerrainShaderType = false; +int32_t maxTerrainMappingTextureSize = 1024; void drawWaterClassic(const glm::mat4 &ModelViewProjection, const glm::mat4 &ModelUVLightmap, const Vector3f &cameraPos, const Vector3f &sunPos); @@ -783,7 +784,7 @@ void markTileDirty(int i, int j) } } -void loadWaterTextures(int maxTerrainTextureSize); +void loadWaterTextures(int maxTerrainTextureSize, optional maxTerrainAuxTextureSize = nullopt); void loadTerrainTextures_Fallback(MAP_TILESET mapTileset) { @@ -805,6 +806,24 @@ void loadTerrainTextures_Fallback(MAP_TILESET mapTileset) loadWaterTextures(maxTerrainTextureSize); } +bool setTerrainMappingTexturesMaxSize(int texSize) +{ + bool isPowerOfTwo = !(texSize == 0) && !(texSize & (texSize - 1)); + if (!isPowerOfTwo || texSize < MIN_TERRAIN_TEXTURE_SIZE || texSize > 1024) + { + debug(LOG_ERROR, "Attempted to set bad terrain mapping texture max size %d! Ignored.", texSize); + return false; + } + maxTerrainMappingTextureSize = texSize; + debug(LOG_TEXTURE, "terrain mapping texture max size set to %d", texSize); + return true; +} + +int getTerrainMappingTexturesMaxSize() +{ + return maxTerrainMappingTextureSize; +} + static gfx_api::texture_array* groundTexArr = nullptr; static gfx_api::texture_array* groundNormalArr = nullptr; static gfx_api::texture_array* groundSpecularArr = nullptr; @@ -857,7 +876,7 @@ gfx_api::texture* getWaterClassicTexture() return waterClassicTexture; } -void loadWaterTextures(int maxTerrainTextureSize) +void loadWaterTextures(int maxTerrainTextureSize, optional maxTerrainAuxTextureSizeOpt) { waterTexturesNormal.clear(); waterTexturesHigh.clear(); @@ -914,11 +933,13 @@ void loadWaterTextures(int maxTerrainTextureSize) waterTexturesHigh.tex_nm = nullptr; waterTexturesHigh.tex_sm = nullptr; + int maxTerrainAuxTextureSize = maxTerrainAuxTextureSizeOpt.value_or(maxTerrainTextureSize); + if (std::any_of(waterTextureFilenames_nm.begin(), waterTextureFilenames_nm.end(), [](const WzString& filename) { return !filename.isEmpty(); })) { - waterTexturesHigh.tex_nm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_nm, gfx_api::texture_type::normal_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr { + waterTexturesHigh.tex_nm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_nm, gfx_api::texture_type::normal_map, maxTerrainAuxTextureSize, maxTerrainAuxTextureSize, [](int width, int height, int channels) -> std::unique_ptr { std::unique_ptr pDefaultNormalMap = std::unique_ptr(new iV_Image); pDefaultNormalMap->allocate(width, height, channels, true); // default normal map: (0,0,1) @@ -940,7 +961,7 @@ void loadWaterTextures(int maxTerrainTextureSize) return !filename.isEmpty(); })) { - waterTexturesHigh.tex_sm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_sm, gfx_api::texture_type::specular_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr { + waterTexturesHigh.tex_sm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_sm, gfx_api::texture_type::specular_map, maxTerrainAuxTextureSize, maxTerrainAuxTextureSize, [](int width, int height, int channels) -> std::unique_ptr { std::unique_ptr pDefaultSpecularMap = std::unique_ptr(new iV_Image); // default specular map: 0 pDefaultSpecularMap->allocate(width, height, channels, true); @@ -1000,13 +1021,16 @@ void loadTerrainTextures_SinglePass(MAP_TILESET mapTileset) // load the textures into the texture arrays groundTexArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames, gfx_api::texture_type::game_texture, maxTerrainTextureSize, maxTerrainTextureSize, nullptr, []() { resDoResLoadCallback(); }); ASSERT(groundTexArr != nullptr, "Failed to load terrain textures"); + + int maxTerrainAuxTextureSize = std::max(std::min({getTextureSize(), getTerrainMappingTexturesMaxSize(), maxGfxTextureSize}), MIN_TERRAIN_TEXTURE_SIZE); + if (terrainShaderQuality == TerrainShaderQuality::NORMAL_MAPPING) { if (std::any_of(groundTextureFilenames_nm.begin(), groundTextureFilenames_nm.end(), [](const WzString& filename) { return !filename.isEmpty(); })) { - groundNormalArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_nm, gfx_api::texture_type::normal_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr { + groundNormalArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_nm, gfx_api::texture_type::normal_map, maxTerrainAuxTextureSize, maxTerrainAuxTextureSize, [](int width, int height, int channels) -> std::unique_ptr { std::unique_ptr pDefaultNormalMap = std::unique_ptr(new iV_Image); pDefaultNormalMap->allocate(width, height, channels, true); // default normal map: (0,0,1) @@ -1028,7 +1052,7 @@ void loadTerrainTextures_SinglePass(MAP_TILESET mapTileset) return !filename.isEmpty(); })) { - groundSpecularArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_spec, gfx_api::texture_type::specular_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr { + groundSpecularArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_spec, gfx_api::texture_type::specular_map, maxTerrainAuxTextureSize, maxTerrainAuxTextureSize, [](int width, int height, int channels) -> std::unique_ptr { std::unique_ptr pDefaultSpecularMap = std::unique_ptr(new iV_Image); // default specular map: 0 pDefaultSpecularMap->allocate(width, height, channels, true); @@ -1040,7 +1064,7 @@ void loadTerrainTextures_SinglePass(MAP_TILESET mapTileset) return !filename.isEmpty(); })) { - groundHeightArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_height, gfx_api::texture_type::height_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr { + groundHeightArr = gfx_api::context::get().loadTextureArrayFromFiles(groundTextureFilenames_height, gfx_api::texture_type::height_map, maxTerrainAuxTextureSize, maxTerrainAuxTextureSize, [](int width, int height, int channels) -> std::unique_ptr { std::unique_ptr pDefaultHeightMap = std::unique_ptr(new iV_Image); // default height map: 0 pDefaultHeightMap->allocate(width, height, channels, true); @@ -1051,7 +1075,7 @@ void loadTerrainTextures_SinglePass(MAP_TILESET mapTileset) } // load water textures - loadWaterTextures(maxTerrainTextureSize); + loadWaterTextures(maxTerrainTextureSize, maxTerrainAuxTextureSize); } void loadTerrainTextures(MAP_TILESET mapTileset) diff --git a/src/terrain.h b/src/terrain.h index fc182b33b7f..37e7e3f0cfd 100644 --- a/src/terrain.h +++ b/src/terrain.h @@ -66,6 +66,9 @@ std::vector getAllTerrainShaderQualityOptions(); bool isSupportedTerrainShaderQualityOption(TerrainShaderQuality value); std::string to_display_string(TerrainShaderQuality value); +bool setTerrainMappingTexturesMaxSize(int texSize); +int getTerrainMappingTexturesMaxSize(); + void initTerrainShaderType(); // must be called after the graphics context is initialized bool debugToggleTerrainShaderType(); From 1e40f747d2d7b059cb0da1524f7be147a21e82b2 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 3 Nov 2023 23:52:41 -0400 Subject: [PATCH 2/6] gfx_api: Add `dedicatedOnly` option to get_estimated_vram_mb --- lib/ivis_opengl/gfx_api.h | 2 +- lib/ivis_opengl/gfx_api_gl.cpp | 17 ++++++++++++++--- lib/ivis_opengl/gfx_api_gl.h | 2 +- lib/ivis_opengl/gfx_api_null.cpp | 2 +- lib/ivis_opengl/gfx_api_null.h | 2 +- lib/ivis_opengl/gfx_api_vk.cpp | 22 +++++++++++++++++++++- lib/ivis_opengl/gfx_api_vk.h | 2 +- src/terrain.cpp | 2 +- 8 files changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/ivis_opengl/gfx_api.h b/lib/ivis_opengl/gfx_api.h index 657ad53069e..a199c377247 100644 --- a/lib/ivis_opengl/gfx_api.h +++ b/lib/ivis_opengl/gfx_api.h @@ -383,7 +383,7 @@ namespace gfx_api virtual void set_polygon_offset(const float& offset, const float& slope) = 0; virtual void set_depth_range(const float& min, const float& max) = 0; virtual int32_t get_context_value(const context_value property) = 0; - virtual uint64_t get_estimated_vram_mb() = 0; + virtual uint64_t get_estimated_vram_mb(bool dedicatedOnly) = 0; static context& get(); static bool initialize(const gfx_api::backend_Impl_Factory& impl, int32_t antialiasing, swap_interval_mode mode, optional mipLodBias, uint32_t depthMapResolution, gfx_api::backend_type backend); static bool isInitialized(); diff --git a/lib/ivis_opengl/gfx_api_gl.cpp b/lib/ivis_opengl/gfx_api_gl.cpp index 96dcdc5f1ff..56813fcca81 100644 --- a/lib/ivis_opengl/gfx_api_gl.cpp +++ b/lib/ivis_opengl/gfx_api_gl.cpp @@ -2721,7 +2721,7 @@ int32_t gl_context::get_context_value(const context_value property) return value; } -uint64_t gl_context::get_estimated_vram_mb() +uint64_t gl_context::get_estimated_vram_mb(bool dedicatedOnly) { if (GLAD_GL_NVX_gpu_memory_info) { @@ -2735,7 +2735,7 @@ uint64_t gl_context::get_estimated_vram_mb() return static_cast(total_graphics_mem_kb / 1024); } } - else if (GLAD_GL_ATI_meminfo) + else if (GLAD_GL_ATI_meminfo && !dedicatedOnly) { // For GL_ATI_meminfo, get the current free texture memory (stats_kb[0]) GLint stats_kb[4] = {0, 0, 0, 0}; @@ -2750,6 +2750,17 @@ uint64_t gl_context::get_estimated_vram_mb() } } +#if defined (__APPLE__) + WzString openGL_vendor = (const char*)wzSafeGlGetString(GL_VENDOR); + WzString openGL_renderer = (const char*)wzSafeGlGetString(GL_RENDERER); + if (openGL_vendor == "Apple" && openGL_renderer.startsWith("Apple")) + { + // For Apple GPUs, use system ("unified") RAM value + auto systemRAMinMiB = wzGetCurrentSystemRAM(); + return systemRAMinMiB; + } +#endif + return 0; } @@ -3436,7 +3447,7 @@ bool gl_context::initGLContext() debug(LOG_3D, " * (current) Max array texture layers is %d.", (int) glMaxArrayTextureLayers); maxArrayTextureLayers = glMaxArrayTextureLayers; - uint32_t estimatedVRAMinMiB = get_estimated_vram_mb(); + uint32_t estimatedVRAMinMiB = get_estimated_vram_mb(false); if (estimatedVRAMinMiB > 0) { debug(LOG_3D, " * Estimated VRAM is %" PRIu32 " MiB", estimatedVRAMinMiB); diff --git a/lib/ivis_opengl/gfx_api_gl.h b/lib/ivis_opengl/gfx_api_gl.h index 0c3caab0ac9..27368c20ec1 100644 --- a/lib/ivis_opengl/gfx_api_gl.h +++ b/lib/ivis_opengl/gfx_api_gl.h @@ -296,7 +296,7 @@ struct gl_context final : public gfx_api::context virtual void set_polygon_offset(const float& offset, const float& slope) override; virtual void set_depth_range(const float& min, const float& max) override; virtual int32_t get_context_value(const context_value property) override; - virtual uint64_t get_estimated_vram_mb() override; + virtual uint64_t get_estimated_vram_mb(bool dedicatedOnly) override; virtual size_t numDepthPasses() override; virtual bool setDepthPassProperties(size_t numDepthPasses, size_t depthBufferResolution) override; diff --git a/lib/ivis_opengl/gfx_api_null.cpp b/lib/ivis_opengl/gfx_api_null.cpp index 5c81b608243..1ebcd73417d 100644 --- a/lib/ivis_opengl/gfx_api_null.cpp +++ b/lib/ivis_opengl/gfx_api_null.cpp @@ -331,7 +331,7 @@ int32_t null_context::get_context_value(const context_value property) return 0; } -uint64_t null_context::get_estimated_vram_mb() +uint64_t null_context::get_estimated_vram_mb(bool dedicatedOnly) { return 0; } diff --git a/lib/ivis_opengl/gfx_api_null.h b/lib/ivis_opengl/gfx_api_null.h index 8c6f935397b..36521e10521 100644 --- a/lib/ivis_opengl/gfx_api_null.h +++ b/lib/ivis_opengl/gfx_api_null.h @@ -124,7 +124,7 @@ struct null_context final : public gfx_api::context virtual void set_polygon_offset(const float& offset, const float& slope) override; virtual void set_depth_range(const float& min, const float& max) override; virtual int32_t get_context_value(const context_value property) override; - virtual uint64_t get_estimated_vram_mb() override; + virtual uint64_t get_estimated_vram_mb(bool dedicatedOnly) override; virtual void beginRenderPass() override; virtual void endRenderPass() override; diff --git a/lib/ivis_opengl/gfx_api_vk.cpp b/lib/ivis_opengl/gfx_api_vk.cpp index 824ae33a26c..302821c9e4d 100644 --- a/lib/ivis_opengl/gfx_api_vk.cpp +++ b/lib/ivis_opengl/gfx_api_vk.cpp @@ -5962,11 +5962,31 @@ int32_t VkRoot::get_context_value(const gfx_api::context::context_value property return 0; } -uint64_t VkRoot::get_estimated_vram_mb() +static bool shouldTreatAsDedicatedGPU(const vk::PhysicalDeviceProperties &physicalDeviceProperties) +{ + if (physicalDeviceProperties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) + { + return true; + } + else if (physicalDeviceProperties.vendorID == 4203) // Apple GPU + { + return true; + } + + return false; +} + +uint64_t VkRoot::get_estimated_vram_mb(bool dedicatedOnly) { optional largestDeviceLocalMemoryHeap = getVKLargestDeviceLocalMemoryHeapIndex(memprops); ASSERT_OR_RETURN(0, largestDeviceLocalMemoryHeap.has_value(), "Couldn't find the largest device local memory heap?"); auto largestDeviceLocalMemoryHeapSize = memprops.memoryHeaps[largestDeviceLocalMemoryHeap.value()].size; + + if (dedicatedOnly && !shouldTreatAsDedicatedGPU(physDeviceProps)) + { + return 0; + } + return static_cast(largestDeviceLocalMemoryHeapSize / 1048576); } diff --git a/lib/ivis_opengl/gfx_api_vk.h b/lib/ivis_opengl/gfx_api_vk.h index 901ade3b516..58632909843 100644 --- a/lib/ivis_opengl/gfx_api_vk.h +++ b/lib/ivis_opengl/gfx_api_vk.h @@ -840,7 +840,7 @@ struct VkRoot final : gfx_api::context public: virtual int32_t get_context_value(const gfx_api::context::context_value property) override; - virtual uint64_t get_estimated_vram_mb() override; + virtual uint64_t get_estimated_vram_mb(bool dedicatedOnly) override; virtual void debugStringMarker(const char *str) override; virtual void debugSceneBegin(const char *descr) override; virtual void debugSceneEnd(const char *descr) override; diff --git a/src/terrain.cpp b/src/terrain.cpp index b1d87a43596..726266b9e5d 100644 --- a/src/terrain.cpp +++ b/src/terrain.cpp @@ -2437,7 +2437,7 @@ static TerrainShaderQuality determineDefaultTerrainQuality() } // Try to get the estimated available VRAM - auto estimatedVRAMinMiB = gfx_api::context::get().get_estimated_vram_mb(); + auto estimatedVRAMinMiB = gfx_api::context::get().get_estimated_vram_mb(false); if (estimatedVRAMinMiB > 0) { // If estimated VRAM < 2 GiB From 0380e51b23e9e6727eca39bfe21f05415320374b Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 3 Nov 2023 23:56:29 -0400 Subject: [PATCH 3/6] terrain: Improve determining defaults for quality options --- src/terrain.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/src/terrain.cpp b/src/terrain.cpp index 726266b9e5d..ca1fbe42f96 100644 --- a/src/terrain.cpp +++ b/src/terrain.cpp @@ -167,7 +167,7 @@ bool drawRangeElementsStarted = false; TerrainShaderQuality terrainShaderQuality = TerrainShaderQuality::UNINITIALIZED_PICK_DEFAULT; TerrainShaderType terrainShaderType = TerrainShaderType::SINGLE_PASS; bool initializedTerrainShaderType = false; -int32_t maxTerrainMappingTextureSize = 1024; +int32_t maxTerrainMappingTextureSize = 0; // uninitialized - pick default on first run void drawWaterClassic(const glm::mat4 &ModelViewProjection, const glm::mat4 &ModelUVLightmap, const Vector3f &cameraPos, const Vector3f &sunPos); @@ -2400,9 +2400,9 @@ static TerrainShaderQuality determineDefaultTerrainQuality() // Based on system properties, determine a reasonable default (for performance reasons) // (Uses a heuristic based on system RAM, graphics renderer, and estimated VRAM) - // If < 7.5 GiB system RAM, default to medium ("normal") + // If <= 4 GiB system RAM, default to medium ("normal") auto systemRAMinMiB = wzGetCurrentSystemRAM(); - if (systemRAMinMiB < 7680) + if (systemRAMinMiB <= 4096) { debug(LOG_INFO, "Due to system RAM (%" PRIu64 " MiB), defaulting to terrain quality: Normal", systemRAMinMiB); return TerrainShaderQuality::MEDIUM; @@ -2459,6 +2459,59 @@ static TerrainShaderQuality determineDefaultTerrainQuality() #endif } +static int32_t determineDefaultTerrainMappingTextureSize() +{ + if (determineDefaultTerrainQuality() == TerrainShaderQuality::MEDIUM) + { + // system checks yield medium terrain quality as the default + // so default the terrain mapping texture size to 512 (in case the user switches to NORMAL_MAPPING) + return 512; + } + + // If < 8 GiB system RAM, default to 512 + auto systemRAMinMiB = wzGetCurrentSystemRAM(); + if (systemRAMinMiB < 8192) + { + debug(LOG_INFO, "Due to system RAM (%" PRIu64 " MiB), defaulting to terrain mapping texture size: 512", systemRAMinMiB); + return 512; + } + + // For specific older integrated cards on OpenGL, default to 512 + auto backendInfo = gfx_api::context::get().getBackendGameInfo(); + auto it = backendInfo.find("openGL_renderer"); + if (it != backendInfo.end()) + { + // If opengl_renderer starts with "Intel(R) HD Graphics" + if (it->second.rfind("Intel(R) HD Graphics", 0) == 0) + { + return 512; + } + // If opengl_renderer starts with "Intel(R) Graphics Media Accelerator" + if (it->second.rfind("Intel(R) Graphics Media Accelerator", 0) == 0) + { + return 512; + } + } + + // Get the estimated *dedicated* VRAM + auto estimatedVRAMinMiB = gfx_api::context::get().get_estimated_vram_mb(true); + if (estimatedVRAMinMiB <= 4096) + { + // If estimated dedicated VRAM value is <= 4 GiB (or cannot be determined), default to 512 + debug(LOG_INFO, "Due to estimated dedicated VRAM (%" PRIu64 " MiB), defaulting to terrain mapping texture size: 512", estimatedVRAMinMiB); + return 512; + } + +#if INTPTR_MAX <= INT32_MAX + // On 32-bit builds, default to 512 + debug(LOG_INFO, "32-bit build defaulting to terrain mapping texture size: 512"); + return 512; +#else + // If all of the above check out, default to 1024 + return 1024; +#endif +} + void initTerrainShaderType() { terrainShaderType = determineSupportedTerrainShader(); @@ -2468,6 +2521,10 @@ void initTerrainShaderType() terrainShaderQuality = determineDefaultTerrainQuality(); debug(LOG_INFO, "Defaulting terrain quality to: %s", to_display_string(terrainShaderQuality).c_str()); } + if (maxTerrainMappingTextureSize == 0) + { + maxTerrainMappingTextureSize = determineDefaultTerrainMappingTextureSize(); + } setTerrainShaderQuality(terrainShaderQuality, true); // checks and resets unsupported values } From 347be3baa15134b474e946ea5376697c8eadfec7 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 4 Nov 2023 12:40:58 -0400 Subject: [PATCH 4/6] Change the new setting name to "Terrain Shading" Also tweak config value names --- src/configuration.cpp | 12 ++++++------ src/frontend.cpp | 11 +++++------ src/frontend.h | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/configuration.cpp b/src/configuration.cpp index bf844152e1a..3739762b1b2 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -591,7 +591,7 @@ bool loadConfig() war_setMPopenSpectatorSlots(static_cast(std::max(0, std::min(openSpecSlotsIntValue, MAX_SPECTATOR_SLOTS)))); war_setFogEnd(iniGetInteger("fogEnd", 8000).value()); war_setFogStart(iniGetInteger("fogStart", 4000).value()); - if (auto value = iniGetIntegerOpt("terrainShaderQuality")) + if (auto value = iniGetIntegerOpt("terrainMode")) { auto intValue = value.value(); if (intValue >= 0 && intValue <= TerrainShaderQuality_MAX) @@ -600,15 +600,15 @@ bool loadConfig() } else { - debug(LOG_WARNING, "Unsupported / invalid terrainShaderQuality value: %d; using default", intValue); + debug(LOG_WARNING, "Unsupported / invalid terrainMode value: %d; using default", intValue); } } - if (auto value = iniGetIntegerOpt("terrainNormalSpecularQuality")) + if (auto value = iniGetIntegerOpt("terrainShadingQuality")) { auto intValue = value.value(); if (!setTerrainMappingTexturesMaxSize(intValue)) { - debug(LOG_WARNING, "Unsupported / invalid terrainNormalSpecularQuality value: %d; using default", intValue); + debug(LOG_WARNING, "Unsupported / invalid terrainShadingQuality value: %d; using default", intValue); } } war_setShadowFilterSize(iniGetInteger("shadowFilterSize", (int)war_getShadowFilterSize()).value()); @@ -790,8 +790,8 @@ bool saveConfig() iniSetInteger("oldLogsLimit", war_getOldLogsLimit()); iniSetInteger("fogEnd", war_getFogEnd()); iniSetInteger("fogStart", war_getFogStart()); - iniSetInteger("terrainShaderQuality", getTerrainShaderQuality()); - iniSetInteger("terrainNormalSpecularQuality", getTerrainMappingTexturesMaxSize()); + iniSetInteger("terrainMode", getTerrainShaderQuality()); + iniSetInteger("terrainShadingQuality", getTerrainMappingTexturesMaxSize()); iniSetInteger("shadowFilterSize", (int)war_getShadowFilterSize()); iniSetInteger("shadowMapResolution", (int)war_getShadowMapResolution()); iniSetInteger("configVersion", CURRCONFVERSION); diff --git a/src/frontend.cpp b/src/frontend.cpp index 39eb400e0f4..07f48f2c746 100644 --- a/src/frontend.cpp +++ b/src/frontend.cpp @@ -969,7 +969,7 @@ static std::shared_ptr makeTerrainQualityDropdown() return Margin(0, 10).wrap(dropdown); } -static std::shared_ptr makeTerrainNormalSpecularDropdown() +static std::shared_ptr makeTerrainShadingQualityDropdown() { std::vector> dropDownChoices = { {_("Medium Quality"), 512}, @@ -987,7 +987,7 @@ static std::shared_ptr makeTerrainNormalSpecularDropdown() } auto dropdown = std::make_shared(); - dropdown->id = FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY_R; + dropdown->id = FRONTEND_TERRAIN_SHADING_QUALITY_R; dropdown->setListHeight(FRONTEND_BUTHEIGHT * std::min(5, dropDownChoices.size())); const auto paddingSize = 10; @@ -1166,10 +1166,9 @@ void startGraphicsOptionsMenu() grid->place({1, 1, false}, row, makeTerrainQualityDropdown()); row.start++; - // Terrain Normals / Specular - // TRANSLATORS: "Normals" and "Specular" refer to Normal Mapping and Specular Mapping (technical, graphics-related terms) - they may or may not make sense to translate - grid->place({0}, row, addMargin(makeTextButton(FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY, _("Terrain Normals / Specular"), WBUT_SECONDARY))); - grid->place({1, 1, false}, row, makeTerrainNormalSpecularDropdown()); + // Terrain Shading Quality + grid->place({0}, row, addMargin(makeTextButton(FRONTEND_TERRAIN_SHADING_QUALITY, _("Terrain Shading"), WBUT_SECONDARY))); + grid->place({1, 1, false}, row, makeTerrainShadingQualityDropdown()); row.start++; //////////// diff --git a/src/frontend.h b/src/frontend.h index 3fcce6106c5..469450d4ede 100644 --- a/src/frontend.h +++ b/src/frontend.h @@ -302,8 +302,8 @@ enum FRONTEND_SSHAKE_R, FRONTEND_TERRAIN_QUALITY, FRONTEND_TERRAIN_QUALITY_R, - FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY, - FRONTEND_TERRAIN_NORMSPEC_MAPPING_QUALITY_R, + FRONTEND_TERRAIN_SHADING_QUALITY, + FRONTEND_TERRAIN_SHADING_QUALITY_R, FRONTEND_GROUPS, FRONTEND_GROUPS_R, From 1fa5feb2ce7bc8e7a69b6719f89e32bbf6de5a49 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 4 Nov 2023 12:51:57 -0400 Subject: [PATCH 5/6] Use std::string for serverName --- src/configuration.cpp | 2 +- src/titleui/protocol.cpp | 18 ++++++++++-------- src/titleui/titleui.cpp | 1 - src/titleui/titleui.h | 7 +++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/configuration.cpp b/src/configuration.cpp index 3739762b1b2..84ff6b73397 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -417,7 +417,7 @@ bool loadConfig() setAutoratingUrl(iniGetString("autoratingUrlV2", WZ_DEFAULT_PUBLIC_RATING_LOOKUP_SERVICE_URL).value()); setAutoratingEnable(iniGetBool("autorating", false).value()); NETsetMasterserverName(iniGetString("masterserver_name", "lobby.wz2100.net").value().c_str()); - mpSetServerName(iniGetString("server_name", "").value().c_str()); + mpSetServerName(iniGetString("server_name", "").value()); // iV_font(ini.value("fontname", "DejaVu Sans").toString().toUtf8().constData(), // ini.value("fontface", "Book").toString().toUtf8().constData(), // ini.value("fontfacebold", "Bold").toString().toUtf8().constData()); diff --git a/src/titleui/protocol.cpp b/src/titleui/protocol.cpp index c4a54234f40..03c5ce462be 100644 --- a/src/titleui/protocol.cpp +++ b/src/titleui/protocol.cpp @@ -39,6 +39,8 @@ #include "../frend.h" #include "lib/widget/checkbox.h" +static std::string serverName; + WzProtocolTitleUI::WzProtocolTitleUI() { @@ -48,15 +50,15 @@ WzProtocolTitleUI::WzProtocolTitleUI() * Set the server name * \param hostname The hostname or IP address of the server to connect to */ -void mpSetServerName(const char *hostname) +void mpSetServerName(const std::string& hostname) { - sstrcpy(serverName, hostname); + serverName = hostname; } /** * @return The hostname or IP address of the server we will connect to. */ -const char *mpGetServerName() +const std::string& mpGetServerName() { return serverName; } @@ -117,10 +119,10 @@ TITLECODE WzProtocolTitleUI::run() break; case CON_OK: { - sstrcpy(serverName, widgGetString(curScreen, CON_IP)); - if (serverName[0] == '\0') + serverName = widgGetWzString(curScreen, CON_IP).toUtf8(); + if (serverName.empty()) { - sstrcpy(serverName, "127.0.0.1"); // Default to localhost. + serverName = "127.0.0.1"; // Default to localhost. } bool asSpectator = false; auto pSpectatorCheckbox = dynamic_cast(widgGetFromID(psSettingsScreen, CON_SPECTATOR_BOX)); @@ -130,7 +132,7 @@ TITLECODE WzProtocolTitleUI::run() } hasWaitingIP = true; closeIPDialog(); - joinGame(serverName, asSpectator); + joinGame(serverName.c_str(), asSpectator); break; } case CON_IP_CANCEL: @@ -209,7 +211,7 @@ void WzProtocolTitleUI::openIPDialog() //internet options sEdInit.y = CON_IPY; sEdInit.width = CON_NAMEBOXWIDTH; sEdInit.height = CON_NAMEBOXHEIGHT; - sEdInit.pText = mpGetServerName(); + sEdInit.pText = serverName.c_str(); sEdInit.pBoxDisplay = intDisplayEditBox; if (!widgAddEditBox(psSettingsScreen, &sEdInit)) { diff --git a/src/titleui/titleui.cpp b/src/titleui/titleui.cpp index a4db4cae98a..0d7edd96d7b 100644 --- a/src/titleui/titleui.cpp +++ b/src/titleui/titleui.cpp @@ -28,7 +28,6 @@ #include "../hci.h" std::shared_ptr wzTitleUICurrent; -char serverName[128]; WzTitleUI::~WzTitleUI() { diff --git a/src/titleui/titleui.h b/src/titleui/titleui.h index e37267fc5aa..ead21a08436 100644 --- a/src/titleui/titleui.h +++ b/src/titleui/titleui.h @@ -31,6 +31,7 @@ #include #include +#include // Regarding construction vs. start(): // This allows a reference to the parent to be held for a stack-like effect. @@ -47,8 +48,6 @@ class WzTitleUI : public std::enable_shared_from_this // Pointer to the current UI. Dynamic allocation helps with the encapsulation. extern std::shared_ptr wzTitleUICurrent; -extern char serverName[128]; - void changeTitleUI(std::shared_ptr ui); // - old.cpp - @@ -141,7 +140,7 @@ class WzGameFindTitleUI: public WzTitleUI #define WZ_MSGBOX_TUI_LEAVE 4597000 -void mpSetServerName(const char *hostname); -const char *mpGetServerName(); +void mpSetServerName(const std::string& hostname); +const std::string& mpGetServerName(); #endif From 766cfcf03742b1e6b28a94a7ada12408c5af0a7a Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 4 Nov 2023 13:02:58 -0400 Subject: [PATCH 6/6] configuration.cpp: Additional sanity checks --- src/configuration.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/configuration.cpp b/src/configuration.cpp index 84ff6b73397..079e9840db4 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -674,6 +674,16 @@ bool saveConfig() auto iniSetString = [&iniGeneral](const std::string& key, const std::string& value) { iniGeneral[key] = value; }; + auto iniSetFromCString = [&iniGeneral](const std::string& key, const char* value, size_t maxLength) { + std::string strVal; + if (value) + { + size_t len = strnlen(value, maxLength); + ASSERT(len < maxLength, "Input c-string value (for key: %s) appears to be missing null-terminator?", key.c_str()); + strVal.assign(value, len); + } + iniGeneral[key] = strVal; + }; // ////////////////////////// // voicevol, fxvol and cdvol @@ -730,7 +740,7 @@ bool saveConfig() iniSetBool("PauseOnFocusLoss", war_GetPauseOnFocusLoss()); iniSetString("autoratingUrlV2", getAutoratingUrl()); iniSetBool("autorating", getAutoratingEnable()); - iniSetString("masterserver_name", NETgetMasterserverName()); + iniSetFromCString("masterserver_name", NETgetMasterserverName(), 255); iniSetInteger("masterserver_port", (int)NETgetMasterserverPort()); iniSetString("server_name", mpGetServerName()); if (!netGameserverPortOverride) // do not save the config port setting if there's a command-line override @@ -752,7 +762,7 @@ bool saveConfig() { if (bMultiPlayer && NetPlay.bComms) { - iniSetString("gameName", game.name); // last hosted game + iniSetFromCString("gameName", game.name, 128); // last hosted game war_setMPInactivityMinutes(game.inactivityMinutes); war_setMPGameTimeLimitMinutes(game.gameTimeLimitMinutes); war_setMPPlayerLeaveMode(game.playerLeaveMode); @@ -761,7 +771,7 @@ bool saveConfig() auto currentSpectatorSlotInfo = SpectatorInfo::currentNetPlayState(); war_setMPopenSpectatorSlots(currentSpectatorSlotInfo.totalSpectatorSlots); } - iniSetString("mapName", game.map); // map name + iniSetFromCString("mapName", game.map, 128); // map name iniSetString("mapHash", game.hash.toString()); // map hash iniSetInteger("maxPlayers", (int)game.maxPlayers); // maxPlayers iniSetInteger("powerLevel", game.power); // power @@ -769,7 +779,7 @@ bool saveConfig() iniSetInteger("alliance", (int)game.alliance); // allow alliances iniSetInteger("newScavengers", game.scavengers); } - iniSetString("playerName", (char *)sPlayer); // player name + iniSetFromCString("playerName", (char *)sPlayer, 128); // player name } iniSetInteger("colourMP", war_getMPcolour()); iniSetInteger("inactivityMinutesMP", war_getMPInactivityMinutes());