Pin dpiscale on the mod's own render targets (voxel mode silently dead on high-DPI iOS) - #40
Conversation
sceneSize() asks for love.graphics.getPixelDimensions(), which is right -- the engine composites a pipeline's canvas with draw(canvas, 0, 0, 0, 1/dpiX, 1/dpiY), so the canvas has to REPORT the pixel size for that blit to cover the window. But love.graphics.newCanvas defaults `dpiscale` to getDPIScale(), so the texture it allocates is that size times the DPI scale again. On an iPad reporting 2, asking for the window's 2752x2064 pixels allocated 5504x4128 -- past the driver's maximum texture size: Cannot create texture: pixel width of 5504 is too large for this system newCanvas threw, beginScene returned false, drawWorld returned nil -- and returning nil IS the 2D fallback, so the engine quietly kept drawing the flat world. Every switch said the mode was on (the VOXEL row, the mod manager, the hotkey) and nothing ever appeared, with no error anywhere the player could see. Desktop never sees it: dpiscale is 1 there. Every render target in this mod is already counted in framebuffer pixels -- the scene canvas is getPixelDimensions(), the shadow map is a resolution rung whose texel count sunTexel is derived from, the blur pair is the size of the image it was handed -- so all three go through a small lib/PixelCanvas.lua that pins dpiscale = 1. getWidth()/getHeight() always reported the REQUESTED size, so nothing downstream moves: no geometry, no UV, no compositing scale. It also squares the FX overlay, whose projected coordinates are already in canvas pixels and were being scaled by the DPI factor a second time. Verified on an iPad Pro 13-inch (M5) simulator running iPadOS 27 at DPI scale 2, where the diorama had been silently falling back to the flat world: terrain, cast shadows, depth occlusion and the tilt-shift pass all render, across the 15/35/50/75 rungs and FULL. The mod's own suite is unchanged -- same single pre-existing failure before and after.
|
Worth separating this from #34, since the titles look alike — this is the same fix, applied to the canvases that one did not reach. #34 pinned
This PR routes those four through a small helper that pins the scale, so it is your #34 technique extended to the remaining targets rather than a different approach. Tested on a physical iPhone 17 Pro (iOS 27) — before/after captures to follow in a reply. Glad to test anything you would rather write yourself; I have the device set up and can turn a build around quickly. |

The symptom
On iPadOS the mod loads, registers both pipelines, compiles both shader
variants and reports
Voxel3D.available() == true— and the diorama neverappears. The
VOXELrow, the mod manager and the hotkey all say the mode ison. The world stays flat, with no error anywhere a player could see it.
The cause
sceneSize()asking forlove.graphics.getPixelDimensions()is correct — theengine composites a pipeline's canvas with
draw(canvas, 0, 0, 0, 1/dpiX, 1/dpiY), so the canvas has to report the pixel size for that blit to coverthe window.
But
love.graphics.newCanvasdefaultsdpiscaletogetDPIScale(), so thetexture it allocates is that size multiplied by the DPI scale again. On an
iPad reporting 2, asking for the window's 2752x2064 pixels allocated
5504x4128 — past the driver's maximum texture size:
newCanvasthrew →beginScenereturned false →drawWorldreturned nil —and returning nil is the 2D fallback, so the engine quietly kept drawing the
flat world. Desktop never sees this:
dpiscaleis 1 there.The fix
Every render target in this mod is already counted in framebuffer pixels: the
scene canvas is
getPixelDimensions(), the shadow map is a resolution rung(the very texel count
sunTexelis derived from), the blur pair is the sizeof the image it was handed. All three now go through a small
lib/PixelCanvas.luathat pinsdpiscale = 1.getWidth()/getHeight()always reported the requested size, so nothingdownstream moves — no geometry, no UV, no compositing scale. It also squares
the FX overlay, whose projected coordinates are already in canvas pixels and
were being scaled by the DPI factor a second time.
Two smaller wins that come with it: the scene no longer renders at 4x the
pixels it needs before being downsampled, and the shadow map's 2x2 filter taps
land on the texels
ShadowMap.ressays they do.Verification
Reproduced and fixed on an iPad Pro 13-inch (M5) simulator, iPadOS 27, DPI
scale 2, on top of
master(7f76caa, v1.3.1). Before the change the log showsthe texture failure and
drawWorld canvas=nilon every frame; after it,drawWorld canvas=ok, and terrain, cast shadows, depth occlusion and thetilt-shift pass all render — checked across the 15 / 35 / 50 / 75 rungs and
FULL.
tests/dramatic_shape_test.luais unchanged by this: the same singlepre-existing failure (
a scripted trainer goes through pushBattle) before andafter, on
masterwith and without the patch.Note on Android
Android was almost certainly hitting the same double-scale — a density of
2.625 asked for a texture ~2.6x too large in each direction. It stayed
invisible there only because those drivers permit the larger texture; the
scene was still being rendered at ~7x the pixels it needed.