ShaderFit
Client module for Blockland v21. While a world is drawing, it watches the frame rate and changes the shadow map to match. A low frame rate steps the map down. A stretch of high frame rate steps it back up. The map also stays inside the size this 32-bit process can allocate.
Shader source is not modified.
Install
Place ShaderFit.dll in the game's modules folder, then quit and start Blockland from Steam. A DLL that is already loaded stays mapped until that restart. The module only changes rendering on this client.
What it follows
The shader quality setting chooses a shadow resolution and a cascade count. ShaderFit remembers that pair. Automatic changes are a temporary limit on top of it. Choosing a different quality replaces the remembered pair and clears the limit.
Each presented frame updates a smoothed frame rate. A frame longer than a quarter second is ignored, so a hitch does not look like a low rate. Scaling waits one second after shadows start, and then waits 2.5 seconds after every change.
Shadows have to be in use. Scaling pauses when shaders are off, when the shader name is minimum, or when the shadow size or cascade count is 0. It starts again when shadows are back.
Stepping down and up
Under 50 fps for one second, the resolution is cut in half. The floor is 1024. At 1024, the next step removes one cascade. It will not go below one cascade.
Over 70 fps for eight seconds, one cascade is restored, up to the count from the quality setting. After the cascades are back, the resolution doubles, up to the cap for this adapter.
The frame rate gained by a step down is not treated as spare room. The map is raised only when the rate would still be about 65 fps after giving that gain back. If a raise drops the rate under 50 within the following second, the step is undone and another raise is blocked for 45 seconds.
Each change rebuilds the shadow map and writes a console line with the frame rate, resolution, and cascade count.
How large a map can be
The shadow map is one 16-bit depth array: width times height times cascades times 2 bytes. The cap is the largest power of two that fits all of these:
- under 2 GB
- under a quarter of dedicated video memory
- inside
GL_MAX_TEXTURE_SIZE - at least 1024
8192 with 4 cascades is 512 MB. 16384 with 4 cascades is exactly 2 GB. This process runs out of memory at that size, so it is not chosen. If a rebuild larger than 8192 still turns shaders off, ShaderFit turns them back on at 8192.
An unknown adapter is not used as a reason to shrink the quality setting. Once an OpenGL context exists, the cap is taken from the current renderer, dedicated memory, and the maximum texture size. Before that, a vendor guess is used: NVIDIA 8192, AMD 4096, and Intel from the amount of dedicated memory.
Console variables
$ShaderFit::Vendor$ShaderFit::Renderer$ShaderFit::DedicatedMB$ShaderFit::ShadowCap$ShaderFit::FrameRate
The first shadow rebuild also logs the vendor, renderer name, dedicated memory, and cap.
Build
Blockland is a 32-bit program, so the DLL has to be built with a 32-bit MinGW toolchain (i686-w64-mingw32). Put that toolchain's bin directory first on PATH so CMake finds that gcc and mingw32-make.
find_package(TSFUNCS) needs a tsfuncs 1.0.1 package: tsfuncs-config.cmake next to include/ and lib/. MinHook's cmake package lives under that package's lib/minhook. Pass the package directory as CMAKE_PREFIX_PATH.
From this repo:
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_PREFIX_PATH=C:/path/to/tsfuncs -S . -B build
cmake --build build
The result is build/ShaderFit.dll. CMake is set so the filename has no lib prefix. The link line is tsfuncs, MinHook, psapi, opengl32, and dxgi, with static libgcc and libstdc++.
Source is src/main.cpp and CMakeLists.txt. The current DLL is attached to the v1.0.0 release.