Rapid descent

- Implement single (but actually 2) pass downsampling
This commit is contained in:
Jozufozu 2024-09-12 21:32:13 -07:00
parent 0bfaac7154
commit 861009ed11
6 changed files with 382 additions and 4 deletions

View File

@ -34,7 +34,9 @@ public class IndirectPrograms extends AtomicReferenceCounted {
private static final ResourceLocation DEPTH_REDUCE_SHADER_MAIN = Flywheel.rl("internal/indirect/depth_reduce.glsl");
private static final ResourceLocation READ_VISIBILITY_SHADER_MAIN = Flywheel.rl("internal/indirect/read_visibility.glsl");
private static final ResourceLocation ZERO_MODELS_SHADER_MAIN = Flywheel.rl("internal/indirect/zero_models.glsl");
public static final List<ResourceLocation> UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DEPTH_REDUCE_SHADER_MAIN, READ_VISIBILITY_SHADER_MAIN, ZERO_MODELS_SHADER_MAIN);
private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl");
private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.rl("internal/indirect/downsample_second.glsl");
public static final List<ResourceLocation> UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DEPTH_REDUCE_SHADER_MAIN, READ_VISIBILITY_SHADER_MAIN, ZERO_MODELS_SHADER_MAIN, DOWNSAMPLE_FIRST, DOWNSAMPLE_SECOND);
private static final Compile<InstanceType<?>> CULL = new Compile<>();
private static final Compile<ResourceLocation> UTIL = new Compile<>();
@ -205,6 +207,14 @@ public class IndirectPrograms extends AtomicReferenceCounted {
return utils.get(DEPTH_REDUCE_SHADER_MAIN);
}
public GlProgram getDownsampleFirstProgram() {
return utils.get(DOWNSAMPLE_FIRST);
}
public GlProgram getDownsampleSecondProgram() {
return utils.get(DOWNSAMPLE_SECOND);
}
public GlProgram getReadVisibilityProgram() {
return utils.get(READ_VISIBILITY_SHADER_MAIN);
}

View File

@ -14,14 +14,18 @@ public class DepthPyramid {
private static final int GROUP_SIZE = 16;
private final GlProgram depthReduceProgram;
private final GlProgram downsampleFirstProgram;
private final GlProgram downsampleSecondProgram;
public int pyramidTextureId = -1;
private int lastWidth = -1;
private int lastHeight = -1;
public DepthPyramid(GlProgram depthReduceProgram) {
public DepthPyramid(GlProgram depthReduceProgram, GlProgram downsampleFirstProgram, GlProgram downsampleSecondProgram) {
this.depthReduceProgram = depthReduceProgram;
this.downsampleFirstProgram = downsampleFirstProgram;
this.downsampleSecondProgram = downsampleSecondProgram;
}
public void generate() {
@ -61,6 +65,54 @@ public class DepthPyramid {
}
}
public void generateSPD() {
var mainRenderTarget = Minecraft.getInstance()
.getMainRenderTarget();
int width = mip0Size(mainRenderTarget.width);
int height = mip0Size(mainRenderTarget.height);
int mipLevels = getImageMipLevels(width, height);
createPyramidMips(mipLevels, width, height);
int depthBufferId = mainRenderTarget.getDepthTextureId();
GL46.glMemoryBarrier(GL46.GL_FRAMEBUFFER_BARRIER_BIT);
GlTextureUnit.T0.makeActive();
GlStateManager._bindTexture(depthBufferId);
downsampleFirstProgram.bind();
downsampleFirstProgram.setUInt("max_mip_level", mipLevels);
for (int i = 0; i < Math.min(6, mipLevels); i++) {
GL46.glBindImageTexture(i + 1, pyramidTextureId, i, false, 0, GL32.GL_WRITE_ONLY, GL32.GL_R32F);
}
GL46.glDispatchCompute(MoreMath.ceilingDiv(width << 1, 64), MoreMath.ceilingDiv(height << 1, 64), 1);
if (mipLevels < 7) {
GL46.glMemoryBarrier(GL46.GL_TEXTURE_FETCH_BARRIER_BIT);
return;
}
GL46.glMemoryBarrier(GL46.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
downsampleSecondProgram.bind();
downsampleSecondProgram.setUInt("max_mip_level", mipLevels);
GL46.glBindImageTexture(0, pyramidTextureId, 5, false, 0, GL32.GL_READ_ONLY, GL32.GL_R32F);
for (int i = 6; i < Math.min(12, mipLevels); i++) {
GL46.glBindImageTexture(i - 5, pyramidTextureId, i, false, 0, GL32.GL_WRITE_ONLY, GL32.GL_R32F);
}
GL46.glDispatchCompute(1, 1, 1);
GL46.glMemoryBarrier(GL46.GL_TEXTURE_FETCH_BARRIER_BIT);
}
public void delete() {
if (pyramidTextureId != -1) {
GL32.glDeleteTextures(pyramidTextureId);

View File

@ -4,6 +4,7 @@ import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL30.glBindBufferRange;
import static org.lwjgl.opengl.GL40.glDrawElementsIndirect;
import static org.lwjgl.opengl.GL42.GL_BUFFER_UPDATE_BARRIER_BIT;
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
@ -67,7 +68,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
lightBuffers = new LightBuffers();
matrixBuffer = new MatrixBuffer();
depthPyramid = new DepthPyramid(programs.getDepthReduceProgram());
depthPyramid = new DepthPyramid(programs.getDepthReduceProgram(), programs.getDownsampleFirstProgram(), programs.getDownsampleSecondProgram());
visibilityBuffer = new VisibilityBuffer(programs.getReadVisibilityProgram());
}
@ -124,7 +125,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
submitDraws();
depthPyramid.generate();
depthPyramid.generateSPD();
programs.getZeroModelProgram()
.bind();

View File

@ -0,0 +1,31 @@
layout(local_size_x = 256) in;
uniform uint max_mip_level;
/// Generates a hierarchical depth buffer.
/// Based on FidelityFX SPD v2.1 https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/blob/d7531ae47d8b36a5d4025663e731a47a38be882f/sdk/include/FidelityFX/gpu/spd/ffx_spd.h#L528
/// Based on Bevy's more readable implementation https://github.com/JMS55/bevy/blob/ca2c8e63b9562f88c8cd7e1d88a17a4eea20aaf4/crates/bevy_pbr/src/meshlet/downsample_depth.wgsl
shared float[16][16] intermediate_memory;
uint extractBits(uint e, uint offset, uint count) {
return (e >> offset) & ((1u << count) - 1u);
}
uint insertBits(uint e, uint newbits, uint offset, uint count) {
uint countMask = ((1u << count) - 1u);
// zero out the bits we're going to replace first
return (e & ~(countMask << offset)) | ((newbits & countMask) << offset);
}
uvec2 remap_for_wave_reduction(uint a) {
return uvec2(
insertBits(extractBits(a, 2u, 3u), a, 0u, 1u),
insertBits(extractBits(a, 3u, 3u), extractBits(a, 1u, 2u), 0u, 2u)
);
}
float reduce_4(vec4 v) {
return max(max(v.x, v.y), max(v.z, v.w));
}

View File

@ -0,0 +1,150 @@
#include "flywheel:internal/indirect/downsample.glsl"
layout(binding = 0) uniform sampler2D mip_0;
layout(binding = 1, r32f) uniform writeonly image2D mip_1;
layout(binding = 2, r32f) uniform writeonly image2D mip_2;
layout(binding = 3, r32f) uniform writeonly image2D mip_3;
layout(binding = 4, r32f) uniform writeonly image2D mip_4;
layout(binding = 5, r32f) uniform writeonly image2D mip_5;
layout(binding = 6, r32f) uniform writeonly image2D mip_6;
float reduce_load_mip_0(uvec2 tex) {
vec2 uv = (vec2(tex) + 0.5) / vec2(imageSize(mip_1)) * 0.5;
return reduce_4(textureGather(mip_0, uv));
}
void downsample_mips_0_and_1(uint x, uint y, ivec2 workgroup_id, uint local_invocation_index) {
vec4 v;
ivec2 tex = workgroup_id * 64 + ivec2(x * 2u, y * 2u);
ivec2 pix = workgroup_id * 32 + ivec2(x, y);
v[0] = reduce_load_mip_0(tex);
imageStore(mip_1, pix, vec4(v[0]));
tex = workgroup_id * 64 + ivec2(x * 2u + 32u, y * 2u);
pix = workgroup_id * 32 + ivec2(x + 16u, y);
v[1] = reduce_load_mip_0(tex);
imageStore(mip_1, pix, vec4(v[1]));
tex = workgroup_id * 64 + ivec2(x * 2u, y * 2u + 32u);
pix = workgroup_id * 32 + ivec2(x, y + 16u);
v[2] = reduce_load_mip_0(tex);
imageStore(mip_1, pix, vec4(v[2]));
tex = workgroup_id * 64 + ivec2(x * 2u + 32u, y * 2u + 32u);
pix = workgroup_id * 32 + ivec2(x + 16u, y + 16u);
v[3] = reduce_load_mip_0(tex);
imageStore(mip_1, pix, vec4(v[3]));
if (max_mip_level <= 1u) { return; }
for (uint i = 0u; i < 4u; i++) {
intermediate_memory[x][y] = v[i];
barrier();
if (local_invocation_index < 64u) {
v[i] = reduce_4(vec4(
intermediate_memory[x * 2u + 0u][y * 2u + 0u],
intermediate_memory[x * 2u + 1u][y * 2u + 0u],
intermediate_memory[x * 2u + 0u][y * 2u + 1u],
intermediate_memory[x * 2u + 1u][y * 2u + 1u]
));
pix = (workgroup_id * 16) + ivec2(
x + (i % 2u) * 8u,
y + (i / 2u) * 8u
);
imageStore(mip_2, pix, vec4(v[i]));
}
barrier();
}
if (local_invocation_index < 64u) {
intermediate_memory[x + 0u][y + 0u] = v[0];
intermediate_memory[x + 8u][y + 0u] = v[1];
intermediate_memory[x + 0u][y + 8u] = v[2];
intermediate_memory[x + 8u][y + 8u] = v[3];
}
}
void downsample_mip_2(uint x, uint y, ivec2 workgroup_id, uint local_invocation_index) {
if (local_invocation_index < 64u) {
float v = reduce_4(vec4(
intermediate_memory[x * 2u + 0u][y * 2u + 0u],
intermediate_memory[x * 2u + 1u][y * 2u + 0u],
intermediate_memory[x * 2u + 0u][y * 2u + 1u],
intermediate_memory[x * 2u + 1u][y * 2u + 1u]
));
imageStore(mip_3, (workgroup_id * 8) + ivec2(x, y), vec4(v));
intermediate_memory[x * 2u + y % 2u][y * 2u] = v;
}
}
void downsample_mip_3(uint x, uint y, ivec2 workgroup_id, uint local_invocation_index) {
if (local_invocation_index < 16u) {
float v = reduce_4(vec4(
intermediate_memory[x * 4u + 0u + 0u][y * 4u + 0u],
intermediate_memory[x * 4u + 2u + 0u][y * 4u + 0u],
intermediate_memory[x * 4u + 0u + 1u][y * 4u + 2u],
intermediate_memory[x * 4u + 2u + 1u][y * 4u + 2u]
));
imageStore(mip_4, (workgroup_id * 4) + ivec2(x, y), vec4(v));
intermediate_memory[x * 4u + y][y * 4u] = v;
}
}
void downsample_mip_4(uint x, uint y, ivec2 workgroup_id, uint local_invocation_index) {
if (local_invocation_index < 4u) {
float v = reduce_4(vec4(
intermediate_memory[x * 8u + 0u + 0u + y * 2u][y * 8u + 0u],
intermediate_memory[x * 8u + 4u + 0u + y * 2u][y * 8u + 0u],
intermediate_memory[x * 8u + 0u + 1u + y * 2u][y * 8u + 4u],
intermediate_memory[x * 8u + 4u + 1u + y * 2u][y * 8u + 4u]
));
imageStore(mip_5, (workgroup_id * 2) + ivec2(x, y), vec4(v));
intermediate_memory[x + y * 2u][0u] = v;
}
}
void downsample_mip_5(ivec2 workgroup_id, uint local_invocation_index) {
if (local_invocation_index < 1u) {
float v = reduce_4(vec4(
intermediate_memory[0u][0u],
intermediate_memory[1u][0u],
intermediate_memory[2u][0u],
intermediate_memory[3u][0u]
));
imageStore(mip_6, workgroup_id, vec4(v));
}
}
void downsample_mips_2_to_5(uint x, uint y, ivec2 workgroup_id, uint local_invocation_index) {
if (max_mip_level <= 2u) { return; }
barrier();
downsample_mip_2(x, y, workgroup_id, local_invocation_index);
if (max_mip_level <= 3u) { return; }
barrier();
downsample_mip_3(x, y, workgroup_id, local_invocation_index);
if (max_mip_level <= 4u) { return; }
barrier();
downsample_mip_4(x, y, workgroup_id, local_invocation_index);
if (max_mip_level <= 5u) { return; }
barrier();
downsample_mip_5(workgroup_id, local_invocation_index);
}
void downsample_depth_first() {
uvec2 sub_xy = remap_for_wave_reduction(gl_LocalInvocationIndex % 64u);
uint x = sub_xy.x + 8u * ((gl_LocalInvocationIndex >> 6u) % 2u);
uint y = sub_xy.y + 8u * (gl_LocalInvocationIndex >> 7u);
downsample_mips_0_and_1(x, y, ivec2(gl_WorkGroupID.xy), gl_LocalInvocationIndex);
downsample_mips_2_to_5(x, y, ivec2(gl_WorkGroupID.xy), gl_LocalInvocationIndex);
}
void main() {
downsample_depth_first();
}

View File

@ -0,0 +1,134 @@
#include "flywheel:internal/indirect/downsample.glsl"
layout(binding = 0, r32f) uniform readonly image2D mip_6;
layout(binding = 1, r32f) uniform writeonly image2D mip_7;
layout(binding = 2, r32f) uniform writeonly image2D mip_8;
layout(binding = 3, r32f) uniform writeonly image2D mip_9;
layout(binding = 4, r32f) uniform writeonly image2D mip_10;
layout(binding = 5, r32f) uniform writeonly image2D mip_11;
layout(binding = 6, r32f) uniform writeonly image2D mip_12;
float reduce_load_mip_6(ivec2 tex) {
return reduce_4(vec4(
imageLoad(mip_6, tex + ivec2(0u, 0u)).r,
imageLoad(mip_6, tex + ivec2(0u, 1u)).r,
imageLoad(mip_6, tex + ivec2(1u, 0u)).r,
imageLoad(mip_6, tex + ivec2(1u, 1u)).r
));
}
void downsample_mips_6_and_7(uint x, uint y) {
vec4 v;
ivec2 tex = ivec2(x * 4u + 0u, y * 4u + 0u);
ivec2 pix = ivec2(x * 2u + 0u, y * 2u + 0u);
v[0] = reduce_load_mip_6(tex);
imageStore(mip_7, pix, vec4(v[0]));
tex = ivec2(x * 4u + 2u, y * 4u + 0u);
pix = ivec2(x * 2u + 1u, y * 2u + 0u);
v[1] = reduce_load_mip_6(tex);
imageStore(mip_7, pix, vec4(v[1]));
tex = ivec2(x * 4u + 0u, y * 4u + 2u);
pix = ivec2(x * 2u + 0u, y * 2u + 1u);
v[2] = reduce_load_mip_6(tex);
imageStore(mip_7, pix, vec4(v[2]));
tex = ivec2(x * 4u + 2u, y * 4u + 2u);
pix = ivec2(x * 2u + 1u, y * 2u + 1u);
v[3] = reduce_load_mip_6(tex);
imageStore(mip_7, pix, vec4(v[3]));
if (max_mip_level <= 7u) { return; }
float vr = reduce_4(v);
imageStore(mip_8, ivec2(x, y), vec4(vr));
intermediate_memory[x][y] = vr;
}
void downsample_mip_8(uint x, uint y, uint local_invocation_index) {
if (local_invocation_index < 64u) {
float v = reduce_4(vec4(
intermediate_memory[x * 2u + 0u][y * 2u + 0u],
intermediate_memory[x * 2u + 1u][y * 2u + 0u],
intermediate_memory[x * 2u + 0u][y * 2u + 1u],
intermediate_memory[x * 2u + 1u][y * 2u + 1u]
));
imageStore(mip_9, ivec2(x, y), vec4(v));
intermediate_memory[x * 2u + y % 2u][y * 2u] = v;
}
}
void downsample_mip_9(uint x, uint y, uint local_invocation_index) {
if (local_invocation_index < 16u) {
float v = reduce_4(vec4(
intermediate_memory[x * 4u + 0u + 0u][y * 4u + 0u],
intermediate_memory[x * 4u + 2u + 0u][y * 4u + 0u],
intermediate_memory[x * 4u + 0u + 1u][y * 4u + 2u],
intermediate_memory[x * 4u + 2u + 1u][y * 4u + 2u]
));
imageStore(mip_10, ivec2(x, y), vec4(v));
intermediate_memory[x * 4u + y][y * 4u] = v;
}
}
void downsample_mip_10(uint x, uint y, uint local_invocation_index) {
if (local_invocation_index < 4u) {
float v = reduce_4(vec4(
intermediate_memory[x * 8u + 0u + 0u + y * 2u][y * 8u + 0u],
intermediate_memory[x * 8u + 4u + 0u + y * 2u][y * 8u + 0u],
intermediate_memory[x * 8u + 0u + 1u + y * 2u][y * 8u + 4u],
intermediate_memory[x * 8u + 4u + 1u + y * 2u][y * 8u + 4u]
));
imageStore(mip_11, ivec2(x, y), vec4(v));
intermediate_memory[x + y * 2u][0u] = v;
}
}
void downsample_mip_11(uint local_invocation_index) {
if (local_invocation_index < 1u) {
float v = reduce_4(vec4(
intermediate_memory[0u][0u],
intermediate_memory[1u][0u],
intermediate_memory[2u][0u],
intermediate_memory[3u][0u]
));
imageStore(mip_12, ivec2(0u, 0u), vec4(v));
}
}
void downsample_mips_8_to_11(uint x, uint y, uint local_invocation_index) {
if (max_mip_level <= 8u) { return; }
barrier();
downsample_mip_8(x, y, local_invocation_index);
if (max_mip_level <= 9u) { return; }
barrier();
downsample_mip_9(x, y, local_invocation_index);
if (max_mip_level <= 10u) { return; }
barrier();
downsample_mip_10(x, y, local_invocation_index);
if (max_mip_level <= 11u) { return; }
barrier();
downsample_mip_11(local_invocation_index);
}
void downsample_depth_second() {
uvec2 sub_xy = remap_for_wave_reduction(gl_LocalInvocationIndex % 64u);
uint x = sub_xy.x + 8u * ((gl_LocalInvocationIndex >> 6u) % 2u);
uint y = sub_xy.y + 8u * (gl_LocalInvocationIndex >> 7u);
downsample_mips_6_and_7(x, y);
downsample_mips_8_to_11(x, y, gl_LocalInvocationIndex);
}
void main() {
downsample_depth_second();
}