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 01a7936a05
commit 6c1fbf610d
5 changed files with 47 additions and 21 deletions

View file

@ -35,8 +35,8 @@ public class DepthPyramid {
var mainRenderTarget = Minecraft.getInstance() var mainRenderTarget = Minecraft.getInstance()
.getMainRenderTarget(); .getMainRenderTarget();
int width = mainRenderTarget.width; int width = mip0Size(mainRenderTarget.width);
int height = mainRenderTarget.height; int height = mip0Size(mainRenderTarget.height);
int mipLevels = getImageMipLevels(width, height); int mipLevels = getImageMipLevels(width, height);
@ -53,15 +53,15 @@ public class DepthPyramid {
depthReduceProgram.bind(); depthReduceProgram.bind();
for (int i = 0; i < mipLevels; i++) { for (int i = 0; i < mipLevels; i++) {
int mipWidth = Math.max(1, width >> i); int mipWidth = mipSize(width, i);
int mipHeight = Math.max(1, height >> i); int mipHeight = mipSize(height, i);
int srcTexture = (i == 0) ? depthBufferId : pyramidTextureId; int srcTexture = (i == 0) ? depthBufferId : pyramidTextureId;
GL46.glBindTexture(GL32.GL_TEXTURE_2D, srcTexture); GL46.glBindTexture(GL32.GL_TEXTURE_2D, srcTexture);
GL46.glBindImageTexture(0, pyramidTextureId, i, false, 0, GL32.GL_WRITE_ONLY, GL32.GL_R32F); 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)); depthReduceProgram.setInt("lod", Math.max(0, i - 1));
GL46.glDispatchCompute(MoreMath.ceilingDiv(mipWidth, 8), MoreMath.ceilingDiv(mipHeight, 8), 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); GL32.glBindTexture(GL32.GL_TEXTURE_2D, pyramidTextureId);
for (int i = 0; i < mipLevels; i++) { for (int i = 0; i < mipLevels; i++) {
int mipWidth = Math.max(1, width >> (i + 1)); int mipWidth = mipSize(width, i);
int mipHeight = Math.max(1, height >> (i + 1)); 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); 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; int result = 1;
while (width > 2 && height > 2) { while (width > 2 && height > 2) {
result++; result++;
width /= 2; width >>= 1;
height /= 2; height >>= 1;
} }
return result; 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.RenderContext;
import dev.engine_room.flywheel.api.visualization.VisualizationManager; 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 dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -17,7 +18,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public final class FrameUniforms extends UniformWriter { 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); static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.FRAME_INDEX, SIZE);
private static final Matrix4f VIEW = new Matrix4f(); private static final Matrix4f VIEW = new Matrix4f();
@ -182,12 +183,20 @@ public final class FrameUniforms extends UniformWriter {
} }
private static long writeCullData(long ptr) { 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, 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.m00()); // P00
ptr = writeFloat(ptr, PROJECTION.m11()); // P11 ptr = writeFloat(ptr, PROJECTION.m11()); // P11
ptr = writeFloat(ptr, Minecraft.getInstance().getMainRenderTarget().width >> 1); // pyramidWidth ptr = writeFloat(ptr, pyramidWidth); // pyramidWidth
ptr = writeFloat(ptr, Minecraft.getInstance().getMainRenderTarget().height >> 1); // pyramidHeight ptr = writeFloat(ptr, pyramidHeight); // pyramidHeight
ptr = writeInt(ptr, pyramidDepth - 1); // pyramidLevels
ptr = writeInt(ptr, 0); // useMin ptr = writeInt(ptr, 0); // useMin
return ptr; 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 width = (aabb.z - aabb.x) * _flw_cullData.pyramidWidth;
float height = (aabb.w - aabb.y) * _flw_cullData.pyramidHeight; 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; ivec2 levelSize = textureSize(_flw_depthPyramid, level);
float depth11 = textureLod(_flw_depthPyramid, aabb.zw, level).r;
float depth10 = textureLod(_flw_depthPyramid, aabb.zy, level).r; ivec4 levelSizePair = ivec4(levelSize, levelSize);
float depth00 = textureLod(_flw_depthPyramid, aabb.xy, level).r;
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; float depth;
if (_flw_cullData.useMin == 0) { 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 = 0, r32f) uniform writeonly image2D outImage;
layout(binding = 1) uniform sampler2D inImage; layout(binding = 1) uniform sampler2D inImage;
uniform uvec2 imageSize; uniform vec2 imageSize;
uniform int lod; uniform int lod;
uniform int useMin = 0; uniform int useMin = 0;
@ -11,7 +11,9 @@ uniform int useMin = 0;
void main() { void main() {
uvec2 pos = gl_GlobalInvocationID.xy; 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 depth01 = texelFetchOffset(inImage, samplePos, lod, ivec2(0, 1)).r;
float depth11 = texelFetchOffset(inImage, samplePos, lod, ivec2(1, 1)).r; float depth11 = texelFetchOffset(inImage, samplePos, lod, ivec2(1, 1)).r;

View file

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