The depths of the rabbit hole

- Fix mip levels being half the size they should be
- Use the next lowest po2 from the main render target size for mip 0
- Map from dst texel to src texel rather than naively multiply by 2
- Clamp the estimated mip level in the cull shader
- Use texel fetches in the cull shader (not sure if necessary?)
This commit is contained in:
Jozufozu 2024-09-05 12:38:05 -05:00
parent ec45287cfa
commit 074ee34dd4
5 changed files with 47 additions and 21 deletions

View File

@ -35,8 +35,8 @@ public class DepthPyramid {
var mainRenderTarget = Minecraft.getInstance()
.getMainRenderTarget();
int width = mainRenderTarget.width;
int height = mainRenderTarget.height;
int width = mip0Size(mainRenderTarget.width);
int height = mip0Size(mainRenderTarget.height);
int mipLevels = getImageMipLevels(width, height);
@ -53,15 +53,15 @@ public class DepthPyramid {
depthReduceProgram.bind();
for (int i = 0; i < mipLevels; i++) {
int mipWidth = Math.max(1, width >> i);
int mipHeight = Math.max(1, height >> i);
int mipWidth = mipSize(width, i);
int mipHeight = mipSize(height, i);
int srcTexture = (i == 0) ? depthBufferId : pyramidTextureId;
GL46.glBindTexture(GL32.GL_TEXTURE_2D, srcTexture);
GL46.glBindImageTexture(0, pyramidTextureId, i, false, 0, GL32.GL_WRITE_ONLY, GL32.GL_R32F);
depthReduceProgram.setUVec2("imageSize", mipWidth, mipHeight);
depthReduceProgram.setVec2("imageSize", mipWidth, mipHeight);
depthReduceProgram.setInt("lod", Math.max(0, i - 1));
GL46.glDispatchCompute(MoreMath.ceilingDiv(mipWidth, 8), MoreMath.ceilingDiv(mipHeight, 8), 1);
@ -85,20 +85,28 @@ public class DepthPyramid {
GL32.glBindTexture(GL32.GL_TEXTURE_2D, pyramidTextureId);
for (int i = 0; i < mipLevels; i++) {
int mipWidth = Math.max(1, width >> (i + 1));
int mipHeight = Math.max(1, height >> (i + 1));
int mipWidth = mipSize(width, i);
int mipHeight = mipSize(height, i);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, i, GL32.GL_R32F, mipWidth, mipHeight, 0, GL32.GL_RED, GL32.GL_FLOAT, 0);
}
}
private static int getImageMipLevels(int width, int height) {
public static int mipSize(int mip0Size, int level) {
return Math.max(1, mip0Size >> level);
}
public static int mip0Size(int screenSize) {
return Integer.highestOneBit(screenSize);
}
public static int getImageMipLevels(int width, int height) {
int result = 1;
while (width > 2 && height > 2) {
result++;
width /= 2;
height /= 2;
width >>= 1;
height >>= 1;
}
return result;

View File

@ -8,6 +8,7 @@ import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.api.RenderContext;
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
import dev.engine_room.flywheel.backend.engine.indirect.DepthPyramid;
import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
@ -17,7 +18,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public final class FrameUniforms extends UniformWriter {
private static final int SIZE = 96 + 64 * 9 + 16 * 5 + 8 * 2 + 8 + 4 * 16;
private static final int SIZE = 96 + 64 * 9 + 16 * 5 + 8 * 2 + 8 + 4 * 17;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.FRAME_INDEX, SIZE);
private static final Matrix4f VIEW = new Matrix4f();
@ -182,12 +183,20 @@ public final class FrameUniforms extends UniformWriter {
}
private static long writeCullData(long ptr) {
var mc = Minecraft.getInstance();
var mainRenderTarget = mc.getMainRenderTarget();
int pyramidWidth = DepthPyramid.mip0Size(mainRenderTarget.width);
int pyramidHeight = DepthPyramid.mip0Size(mainRenderTarget.height);
int pyramidDepth = DepthPyramid.getImageMipLevels(pyramidWidth, pyramidHeight);
ptr = writeFloat(ptr, 0.05F); // zNear
ptr = writeFloat(ptr, Minecraft.getInstance().gameRenderer.getDepthFar()); // zFar
ptr = writeFloat(ptr, mc.gameRenderer.getDepthFar()); // zFar
ptr = writeFloat(ptr, PROJECTION.m00()); // P00
ptr = writeFloat(ptr, PROJECTION.m11()); // P11
ptr = writeFloat(ptr, Minecraft.getInstance().getMainRenderTarget().width >> 1); // pyramidWidth
ptr = writeFloat(ptr, Minecraft.getInstance().getMainRenderTarget().height >> 1); // pyramidHeight
ptr = writeFloat(ptr, pyramidWidth); // pyramidWidth
ptr = writeFloat(ptr, pyramidHeight); // pyramidHeight
ptr = writeInt(ptr, pyramidDepth - 1); // pyramidLevels
ptr = writeInt(ptr, 0); // useMin
return ptr;

View File

@ -91,12 +91,18 @@ bool _flw_isVisible(uint instanceIndex, uint modelIndex) {
float width = (aabb.z - aabb.x) * _flw_cullData.pyramidWidth;
float height = (aabb.w - aabb.y) * _flw_cullData.pyramidHeight;
float level = floor(log2(max(width, height)));
int level = clamp(0, int(ceil(log2(max(width, height)))), _flw_cullData.pyramidLevels);
float depth01 = textureLod(_flw_depthPyramid, aabb.xw, level).r;
float depth11 = textureLod(_flw_depthPyramid, aabb.zw, level).r;
float depth10 = textureLod(_flw_depthPyramid, aabb.zy, level).r;
float depth00 = textureLod(_flw_depthPyramid, aabb.xy, level).r;
ivec2 levelSize = textureSize(_flw_depthPyramid, level);
ivec4 levelSizePair = ivec4(levelSize, levelSize);
ivec4 bounds = ivec4(aabb * vec4(levelSizePair));
float depth01 = texelFetch(_flw_depthPyramid, bounds.xw, level).r;
float depth11 = texelFetch(_flw_depthPyramid, bounds.zw, level).r;
float depth10 = texelFetch(_flw_depthPyramid, bounds.zy, level).r;
float depth00 = texelFetch(_flw_depthPyramid, bounds.xy, level).r;
float depth;
if (_flw_cullData.useMin == 0) {

View File

@ -3,7 +3,7 @@ layout(local_size_x = 8, local_size_y = 8) in;
layout(binding = 0, r32f) uniform writeonly image2D outImage;
layout(binding = 1) uniform sampler2D inImage;
uniform uvec2 imageSize;
uniform vec2 imageSize;
uniform int lod;
uniform int useMin = 0;
@ -11,7 +11,9 @@ uniform int useMin = 0;
void main() {
uvec2 pos = gl_GlobalInvocationID.xy;
ivec2 samplePos = ivec2(pos) * 2;
// Map the output texel to an input texel. Properly do the division because generating mip0 maps from the actual
// full resolution depth buffer and the aspect ratio may be different from our Po2 pyramid.
ivec2 samplePos = ivec2(floor(vec2(pos) * vec2(textureSize(inImage, lod)) / imageSize));
float depth01 = texelFetchOffset(inImage, samplePos, lod, ivec2(0, 1)).r;
float depth11 = texelFetchOffset(inImage, samplePos, lod, ivec2(1, 1)).r;

View File

@ -16,6 +16,7 @@ struct _FlwCullData {
float P11;
float pyramidWidth;
float pyramidHeight;
int pyramidLevels;
uint useMin;
};