From: Svjatoslav Agejenko Date: Sun, 20 Sep 2026 08:17:58 +0000 (+0300) Subject: Hoist per-triangle mipmap metrics out of the per-tile paint path X-Git-Tag: aukio-3d-1.0.0~7 X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=38f0e7c45525beb588fd34d8d1759d8ce7a344cd;p=aukio-3d.git Hoist per-triangle mipmap metrics out of the per-tile paint path - paintFlat no longer recomputes the screen-edge perimeter (3 sqrt) per overlapped tile: callers pass it in. Object path already computed it in paintTriangle (it was computed twice per paint); mesh handles read a per-slot value computed once in TriangleMeshBlock.transform (queued triangles only). Clipped fan sub-triangles compute inline (rare). - TriangleMeshBlock UV perimeter (origTtd) precomputed once at build into uvPerimeter[] instead of 3 sqrt over the final uv[] per paint. Bit-exact: same expressions, same accumulation order, same inputs — verified by identical golden diff counts against the pre-change run (23/23 scenes). --- diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/MeshTriangle.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/MeshTriangle.java index d62fa3b..9e040eb 100644 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/MeshTriangle.java +++ b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/MeshTriangle.java @@ -86,12 +86,25 @@ final class MeshTriangle extends TexturedTriangle { for (int i = 1; i + 1 < count; i++) { loadClipVertex(screen[1], uvs[1], store, clip + i * 7); loadClipVertex(screen[2], uvs[2], store, clip + (i + 1) * 7); + // Fan sub-triangle screen perimeter — same expression as + // the object path (edge12 + edge13 + edge23); straddlers + // are rare, so it is computed here rather than stored. + final double dx01 = screen[0].x - screen[1].x; + final double dy01 = screen[0].y - screen[1].y; + final double dx02 = screen[0].x - screen[2].x; + final double dy02 = screen[0].y - screen[2].y; + final double dx12 = screen[1].x - screen[2].x; + final double dy12 = screen[1].y - screen[2].y; + final double visPerimeter = Math.sqrt(dx01 * dx01 + dy01 * dy01) + + Math.sqrt(dx02 * dx02 + dy02 * dy02) + + Math.sqrt(dx12 * dx12 + dy12 * dy12); paintFlat(renderBuffer, block.texture(index), block.backfaceCull(), screen[0], screen[1], screen[2], uvs[0], uvs[1], uvs[2], store[clip + 2], store[clip + i * 7 + 2], store[clip + (i + 1) * 7 + 2], + visPerimeter, block.clipTtd(slot, clip)); } return; @@ -106,7 +119,8 @@ final class MeshTriangle extends TexturedTriangle { block.camZ(slot, index, 0), block.camZ(slot, index, 1), block.camZ(slot, index, 2), - block.origTtd(index)); + block.screenPerim(slot, index), + block.uvPerimeter(index)); } /** diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.java index 7d46c34..648baec 100644 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.java +++ b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.java @@ -512,6 +512,7 @@ public class TexturedTriangle extends AbstractCoordinateShape { v1.transformedCoordinate(renderBuffer).z, v2.transformedCoordinate(renderBuffer).z, v3.transformedCoordinate(renderBuffer).z, + totalVisibleDistance, totalTextureDistance); } @@ -539,6 +540,11 @@ public class TexturedTriangle extends AbstractCoordinateShape { * @param z1 camera-space depth of vertex 1 * @param z2 camera-space depth of vertex 2 * @param z3 camera-space depth of vertex 3 + * @param totalVisibleDistance screen-edge perimeter for mipmap + * selection, computed once per paint call + * (or once per slot for mesh blocks) — + * callers share it instead of the core + * recomputing per tile * @param totalTextureDistance UV perimeter for mipmap selection. For a * near-plane-clipped fan this is the * ORIGINAL triangle's perimeter (the clip @@ -555,6 +561,7 @@ public class TexturedTriangle extends AbstractCoordinateShape { final Point2D texturePoint2, final Point2D texturePoint3, final double z1, final double z2, final double z3, + final double totalVisibleDistance, final double totalTextureDistance) { // Z-buffer two-pass classification: opaque-class triangles @@ -618,11 +625,6 @@ public class TexturedTriangle extends AbstractCoordinateShape { throw new IllegalStateException( "SDF textures are not supported in mesh blocks"); - final double edge12 = projectedPoint1.getDistanceTo(projectedPoint2); - final double edge13 = projectedPoint1.getDistanceTo(projectedPoint3); - final double edge23 = projectedPoint2.getDistanceTo(projectedPoint3); - final double totalVisibleDistance = edge12 + edge13 + edge23; - final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d; final TextureBitmap mipmap = texture.getMipmapForScale(scaleFactor); diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TriangleMeshBlock.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TriangleMeshBlock.java index 6084ee3..94d8195 100644 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TriangleMeshBlock.java +++ b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/basic/texturedpolygon/TriangleMeshBlock.java @@ -67,6 +67,17 @@ public final class TriangleMeshBlock extends AbstractShape { private final int[][] clipRef = new int[3][]; private final double[][] clipStore = new double[3][]; private final double[][] clipTtd = new double[3][]; + + /** Per-triangle UV perimeter (mipmap metric), computed once at build: + * the uv array never changes afterwards. Same expression and + * accumulation order as {@code TexturedTriangle}'s + * computeTotalTextureDistance: d(0,1) + d(0,2) + d(1,2). */ + private final double[] uvPerimeter; + + /** Per-triangle screen-edge perimeter (mipmap metric's visible side), + * computed once per slot in the transform loop — identical expression + * to what the paint core used to recompute per tile. */ + private final double[][] screenPerim = new double[3][]; private final int[] clipUsed = new int[3]; // Scratch for the composed top transform of the current transform call. @@ -112,6 +123,22 @@ public final class TriangleMeshBlock extends AbstractShape { java.util.Arrays.fill(clipRef[s], -1); clipStore[s] = new double[256]; clipTtd[s] = new double[16]; + screenPerim[s] = new double[triCount]; + } + + uvPerimeter = new double[triCount]; + for (int t = 0; t < triCount; t++) { + final int u = t * 6; + final double d1 = Math.sqrt( + ((uv[u] - uv[u + 2]) * (uv[u] - uv[u + 2])) + + ((uv[u + 1] - uv[u + 3]) * (uv[u + 1] - uv[u + 3]))); + final double d2 = Math.sqrt( + ((uv[u] - uv[u + 4]) * (uv[u] - uv[u + 4])) + + ((uv[u + 1] - uv[u + 5]) * (uv[u + 1] - uv[u + 5]))); + final double d3 = Math.sqrt( + ((uv[u + 2] - uv[u + 4]) * (uv[u + 2] - uv[u + 4])) + + ((uv[u + 3] - uv[u + 5]) * (uv[u + 3] - uv[u + 5]))); + uvPerimeter[t] = d1 + d2 + d3; } double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE, @@ -302,6 +329,16 @@ public final class TriangleMeshBlock extends AbstractShape { if (maxX < rMinX || minX >= rMaxX || maxY < rMinY || minY >= rMaxY) continue; + // Mipmap metric's visible side, once per slot instead of per + // tile: edge12 + edge13 + edge23, the same expression the + // paint core ran per tile (getDistanceTo sequence). + final double dx01 = sx0 - sx1, dy01 = sy0 - sy1; + final double dx02 = sx0 - sx2, dy02 = sy0 - sy2; + final double dx12 = sx1 - sx2, dy12 = sy1 - sy2; + screenPerim[slot][t] = Math.sqrt(dx01 * dx01 + dy01 * dy01) + + Math.sqrt(dx02 * dx02 + dy02 * dy02) + + Math.sqrt(dx12 * dx12 + dy12 * dy12); + aggregator.queueShapeForRendering(handles[t]); } } @@ -333,7 +370,7 @@ public final class TriangleMeshBlock extends AbstractShape { clipTtd[slot] = grownTtd; } final int base = used; - clipTtd[slot][base / CLIP_ENTRY] = origTtd(t); + clipTtd[slot][base / CLIP_ENTRY] = uvPerimeter[t]; final double scale = renderingContext.projectionScale; final double centerX = renderingContext.centerCoordinate.x; @@ -436,23 +473,17 @@ public final class TriangleMeshBlock extends AbstractShape { return clipTtd[slot][clipOffset / CLIP_ENTRY]; } + /** The triangle's UV perimeter, precomputed at build (see field). */ + double uvPerimeter(final int tri) { + return uvPerimeter[tri]; + } + /** - * The triangle's UV perimeter, with the exact expression and - * accumulation order of {@code TexturedTriangle}'s - * computeTotalTextureDistance: d(0,1) + d(0,2) + d(1,2). + * The triangle's screen-edge perimeter for this slot, precomputed in + * the transform loop (only valid for triangles queued unclipped). */ - double origTtd(final int tri) { - final int u = tri * 6; - final double d1 = Math.sqrt( - ((uv[u] - uv[u + 2]) * (uv[u] - uv[u + 2])) - + ((uv[u + 1] - uv[u + 3]) * (uv[u + 1] - uv[u + 3]))); - final double d2 = Math.sqrt( - ((uv[u] - uv[u + 4]) * (uv[u] - uv[u + 4])) - + ((uv[u + 1] - uv[u + 5]) * (uv[u + 1] - uv[u + 5]))); - final double d3 = Math.sqrt( - ((uv[u + 2] - uv[u + 4]) * (uv[u + 2] - uv[u + 4])) - + ((uv[u + 3] - uv[u + 5]) * (uv[u + 3] - uv[u + 5]))); - return d1 + d2 + d3; + double screenPerim(final int slot, final int tri) { + return screenPerim[slot][tri]; } /**