Precalculate animation data to avoid recalculating it a million times in the fragment shader.

Improves performance by about 1 ms in a test scene.
This commit is contained in:
IntegratedQuantum 2023-10-02 23:49:24 +02:00
parent 657aa8dc9d
commit 045ba9c65a
9 changed files with 107 additions and 45 deletions

View File

@ -0,0 +1,42 @@
#version 430
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
struct AnimationData {
int frames;
int time;
};
struct TextureData {
int textureIndices[6];
uint absorption;
float reflectivity;
float fogDensity;
uint fogColor;
};
layout(std430, binding = 0) buffer _animation
{
AnimationData animation[];
};
layout(std430, binding = 6) buffer _textureDataIn
{
TextureData textureDataIn[];
};
layout(std430, binding = 1) buffer _textureDataOut
{
TextureData textureDataOut[];
};
uniform int time;
uniform int size;
void main() {
uint index = gl_GlobalInvocationID.x;
if(index >= size) return;
for(int i = 0; i < 6; i++) {
int textureIndex = textureDataIn[index].textureIndices[i];
textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames;
textureDataOut[index].textureIndices[i] = textureIndex;
}
}

View File

@ -10,18 +10,12 @@ flat in int ditherSeed;
in vec3 startPosition; in vec3 startPosition;
in vec3 direction; in vec3 direction;
uniform int time;
uniform vec3 ambientLight; uniform vec3 ambientLight;
uniform sampler2DArray texture_sampler; uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler; uniform sampler2DArray emissionSampler;
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
struct AnimationData {
int frames;
int time;
};
#define modelSize 16 #define modelSize 16
struct VoxelModel { struct VoxelModel {
ivec4 minimum; ivec4 minimum;
@ -37,10 +31,6 @@ struct TextureData {
uint fogColor; uint fogColor;
}; };
layout(std430, binding = 0) buffer _animation
{
AnimationData animation[];
};
layout(std430, binding = 1) buffer _textureData layout(std430, binding = 1) buffer _textureData
{ {
TextureData textureData[]; TextureData textureData[];
@ -237,7 +227,6 @@ void main() {
} }
if(!result.hitAThing) discard; if(!result.hitAThing) discard;
int textureIndex = textureData[blockType].textureIndices[result.textureDir]; int textureIndex = textureData[blockType].textureIndices[result.textureDir];
textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames;
float normalVariation = normalVariations[result.normal]; float normalVariation = normalVariations[result.normal];
float lod = getLod(result.voxelPosition, result.normal, direction, variance); float lod = getLod(result.voxelPosition, result.normal, direction, variance);
ivec2 textureCoords = getTextureCoords(result.voxelPosition, result.textureDir); ivec2 textureCoords = getTextureCoords(result.voxelPosition, result.textureDir);

View File

@ -9,7 +9,6 @@ flat in int ditherSeed;
in vec3 startPosition; in vec3 startPosition;
in vec3 direction; in vec3 direction;
uniform int time;
uniform vec3 ambientLight; uniform vec3 ambientLight;
uniform sampler2DArray texture_sampler; uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler; uniform sampler2DArray emissionSampler;
@ -26,11 +25,6 @@ struct Fog {
float density; float density;
}; };
struct AnimationData {
int frames;
int time;
};
struct TextureData { struct TextureData {
int textureIndices[6]; int textureIndices[6];
uint absorption; uint absorption;
@ -39,10 +33,6 @@ struct TextureData {
uint fogColor; uint fogColor;
}; };
layout(std430, binding = 0) buffer _animation
{
AnimationData animation[];
};
layout(std430, binding = 1) buffer _textureData layout(std430, binding = 1) buffer _textureData
{ {
TextureData textureData[]; TextureData textureData[];
@ -143,7 +133,6 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012
void main() { void main() {
int textureIndex = textureData[blockType].textureIndices[faceNormal]; int textureIndex = textureData[blockType].textureIndices[faceNormal];
textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames;
vec3 textureCoords = vec3(getTextureCoordsNormal(startPosition/16, faceNormal), textureIndex); vec3 textureCoords = vec3(getTextureCoordsNormal(startPosition/16, faceNormal), textureIndex);
float normalVariation = normalVariations[faceNormal]; float normalVariation = normalVariations[faceNormal];
float densityAdjustment = sqrt(dot(mvVertexPos, mvVertexPos))/abs(mvVertexPos.z); float densityAdjustment = sqrt(dot(mvVertexPos, mvVertexPos))/abs(mvVertexPos.z);

View File

@ -3,10 +3,12 @@ const std = @import("std");
const main = @import("root"); const main = @import("root");
const JsonElement = @import("json.zig").JsonElement; const JsonElement = @import("json.zig").JsonElement;
const Neighbors = @import("chunk.zig").Neighbors; const Neighbors = @import("chunk.zig").Neighbors;
const SSBO = @import("graphics.zig").SSBO; const graphics = @import("graphics.zig");
const Image = @import("graphics.zig").Image; const Shader = graphics.Shader;
const Color = @import("graphics.zig").Color; const SSBO = graphics.SSBO;
const TextureArray = @import("graphics.zig").TextureArray; const Image = graphics.Image;
const Color = graphics.Color;
const TextureArray = graphics.TextureArray;
const items = @import("items.zig"); const items = @import("items.zig");
const models = @import("models.zig"); const models = @import("models.zig");
const rotation = @import("rotation.zig"); const rotation = @import("rotation.zig");
@ -344,7 +346,14 @@ pub const meshes = struct {
}; };
var animationSSBO: SSBO = undefined; var animationSSBO: SSBO = undefined;
var textureIndexSSBO: SSBO = undefined; var textureDataSSBO: SSBO = undefined;
var animatedTextureDataSSBO: SSBO = undefined;
var animationShader: Shader = undefined;
var animationUniforms: struct {
time: c_int,
size: c_int,
} = undefined;
pub var blockTextureArray: TextureArray = undefined; pub var blockTextureArray: TextureArray = undefined;
pub var emissionTextureArray: TextureArray = undefined; pub var emissionTextureArray: TextureArray = undefined;
@ -356,11 +365,14 @@ pub const meshes = struct {
var emptyTexture = [_]Color {black}; var emptyTexture = [_]Color {black};
const emptyImage = Image{.width = 1, .height = 1, .imageData = emptyTexture[0..]}; const emptyImage = Image{.width = 1, .height = 1, .imageData = emptyTexture[0..]};
pub fn init() void { pub fn init() !void {
animationSSBO = SSBO.init(); animationSSBO = SSBO.init();
animationSSBO.bind(0); animationSSBO.bind(0);
textureIndexSSBO = SSBO.init(); textureDataSSBO = SSBO.init();
textureIndexSSBO.bind(1); textureDataSSBO.bind(6);
animatedTextureDataSSBO = SSBO.init();
animatedTextureDataSSBO.bind(1);
animationShader = try Shader.initComputeAndGetUniforms("assets/cubyz/shaders/animation_pre_processing.glsl", &animationUniforms);
blockTextureArray = TextureArray.init(); blockTextureArray = TextureArray.init();
emissionTextureArray = TextureArray.init(); emissionTextureArray = TextureArray.init();
arenaForArrayLists = std.heap.ArenaAllocator.init(main.globalAllocator); arenaForArrayLists = std.heap.ArenaAllocator.init(main.globalAllocator);
@ -373,7 +385,9 @@ pub const meshes = struct {
pub fn deinit() void { pub fn deinit() void {
animationSSBO.deinit(); animationSSBO.deinit();
textureIndexSSBO.deinit(); textureDataSSBO.deinit();
animatedTextureDataSSBO.deinit();
animationShader.deinit();
blockTextureArray.deinit(); blockTextureArray.deinit();
emissionTextureArray.deinit(); emissionTextureArray.deinit();
arenaForArrayLists.deinit(); arenaForArrayLists.deinit();
@ -549,12 +563,21 @@ pub const meshes = struct {
// } // }
// } // }
pub fn preProcessAnimationData(time: u32) void {
animationShader.bind();
graphics.c.glUniform1i(animationUniforms.time, @bitCast(time));
graphics.c.glUniform1i(animationUniforms.size, @intCast(meshes.size));
graphics.c.glDispatchCompute(@divFloor(meshes.size + 63, 64), 1, 1); // TODO: Replace with @divCeil once available
graphics.c.glMemoryBarrier(graphics.c.GL_SHADER_STORAGE_BARRIER_BIT);
}
pub fn generateTextureArray() !void { pub fn generateTextureArray() !void {
try blockTextureArray.generate(blockTextures.items, true); try blockTextureArray.generate(blockTextures.items, true);
try emissionTextureArray.generate(emissionTextures.items, true); try emissionTextureArray.generate(emissionTextures.items, true);
// Also generate additional buffers: // Also generate additional buffers:
animationSSBO.bufferData(AnimationData, animation.items); animationSSBO.bufferData(AnimationData, animation.items);
textureIndexSSBO.bufferData(TextureData, textureData[0..meshes.size]); textureDataSSBO.bufferData(TextureData, textureData[0..meshes.size]);
animatedTextureDataSSBO.bufferData(TextureData, textureData[0..meshes.size]);
} }
}; };

View File

@ -399,7 +399,6 @@ pub const meshing = struct {
emissionSampler: c_int, emissionSampler: c_int,
reflectionMap: c_int, reflectionMap: c_int,
reflectionMapSize: c_int, reflectionMapSize: c_int,
time: c_int,
visibilityMask: c_int, visibilityMask: c_int,
voxelSize: c_int, voxelSize: c_int,
zNear: c_int, zNear: c_int,
@ -444,7 +443,7 @@ pub const meshing = struct {
faceBuffer.deinit(); faceBuffer.deinit();
} }
pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void { pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f) void {
shader.bind(); shader.bind();
c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast(&projMatrix)); c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_FALSE, @ptrCast(&projMatrix));
@ -458,15 +457,13 @@ pub const meshing = struct {
c.glUniform3f(uniforms.ambientLight, ambient[0], ambient[1], ambient[2]); c.glUniform3f(uniforms.ambientLight, ambient[0], ambient[1], ambient[2]);
c.glUniform1i(uniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(uniforms.zNear, renderer.zNear); c.glUniform1f(uniforms.zNear, renderer.zNear);
c.glUniform1f(uniforms.zFar, renderer.zFar); c.glUniform1f(uniforms.zFar, renderer.zFar);
c.glBindVertexArray(vao); c.glBindVertexArray(vao);
} }
pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, time: u32) void { pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f) void {
transparentShader.bind(); transparentShader.bind();
c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.color)); c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.fog.color));
@ -483,8 +480,6 @@ pub const meshing = struct {
c.glUniform3f(transparentUniforms.ambientLight, ambient[0], ambient[1], ambient[2]); c.glUniform3f(transparentUniforms.ambientLight, ambient[0], ambient[1], ambient[2]);
c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(transparentUniforms.zNear, renderer.zNear); c.glUniform1f(transparentUniforms.zNear, renderer.zNear);
c.glUniform1f(transparentUniforms.zFar, renderer.zFar); c.glUniform1f(transparentUniforms.zFar, renderer.zFar);

View File

@ -1110,6 +1110,23 @@ pub const Shader = struct {
return self; return self;
} }
pub fn initCompute(compute: []const u8) !Shader {
var shader = Shader{.id = c.glCreateProgram()};
try shader.addShader(compute, c.GL_COMPUTE_SHADER);
try shader.link();
return shader;
}
pub fn initComputeAndGetUniforms(compute: []const u8, ptrToUniformStruct: anytype) !Shader {
const self = try Shader.initCompute(compute);
inline for(@typeInfo(@TypeOf(ptrToUniformStruct.*)).Struct.fields) |field| {
if(field.type == c_int) {
@field(ptrToUniformStruct, field.name) = c.glGetUniformLocation(self.id, field.name[0..] ++ "\x00"); // TODO: #16072
}
}
return self;
}
pub fn bind(self: *const Shader) void { pub fn bind(self: *const Shader) void {
c.glUseProgram(self.id); c.glUseProgram(self.id);
} }
@ -1753,9 +1770,9 @@ pub fn generateBlockTexture(blockType: u16) !Texture {
if(block.transparent()) { if(block.transparent()) {
c.glBlendEquation(c.GL_FUNC_ADD); c.glBlendEquation(c.GL_FUNC_ADD);
c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR);
main.chunk.meshing.bindTransparentShaderAndUniforms(projMatrix, .{1, 1, 1}, 0); main.chunk.meshing.bindTransparentShaderAndUniforms(projMatrix, .{1, 1, 1});
} else { } else {
main.chunk.meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1}, 0); main.chunk.meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1});
} }
const uniforms = if(block.transparent()) &main.chunk.meshing.transparentUniforms else &main.chunk.meshing.uniforms; const uniforms = if(block.transparent()) &main.chunk.meshing.transparentUniforms else &main.chunk.meshing.uniforms;

View File

@ -15,6 +15,7 @@ const GuiComponent = gui.GuiComponent;
pub const Samples = enum(u8) { pub const Samples = enum(u8) {
screenbuffer_clear, screenbuffer_clear,
clear, clear,
animation,
chunk_rendering, chunk_rendering,
entity_rendering, entity_rendering,
transparent_rendering, transparent_rendering,
@ -29,6 +30,7 @@ pub const Samples = enum(u8) {
const names = [_][]const u8 { const names = [_][]const u8 {
"Screenbuffer clear", "Screenbuffer clear",
"Clear", "Clear",
"Pre-processing Block Animations",
"Chunk Rendering", "Chunk Rendering",
"Entity Rendering", "Entity Rendering",
"Transparent Rendering", "Transparent Rendering",

View File

@ -686,7 +686,7 @@ pub fn main() !void {
try assets.init(); try assets.init();
defer assets.deinit(); defer assets.deinit();
blocks.meshes.init(); try blocks.meshes.init();
defer blocks.meshes.deinit(); defer blocks.meshes.deinit();
try chunk.meshing.init(); try chunk.meshing.init();

View File

@ -173,8 +173,13 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
const time: u32 = @intCast(std.time.milliTimestamp() & std.math.maxInt(u32)); const time: u32 = @intCast(std.time.milliTimestamp() & std.math.maxInt(u32));
gpu_performance_measuring.startQuery(.animation);
blocks.meshes.preProcessAnimationData(time);
gpu_performance_measuring.stopQuery();
// Update the uniforms. The uniforms are needed to render the replacement meshes. // Update the uniforms. The uniforms are needed to render the replacement meshes.
chunk.meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight, time); chunk.meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight);
reflectionCubeMap.bindTo(2); reflectionCubeMap.bindTo(2);
c.glActiveTexture(c.GL_TEXTURE0); c.glActiveTexture(c.GL_TEXTURE0);
@ -202,7 +207,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
MeshSelection.select(playerPos, game.camera.direction); MeshSelection.select(playerPos, game.camera.direction);
MeshSelection.render(game.projectionMatrix, game.camera.viewMatrix, playerPos); MeshSelection.render(game.projectionMatrix, game.camera.viewMatrix, playerPos);
chunk.meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight, time); chunk.meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight);
for(meshes) |mesh| { for(meshes) |mesh| {
mesh.render(playerPos); mesh.render(playerPos);
@ -225,7 +230,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
gpu_performance_measuring.startQuery(.transparent_rendering); gpu_performance_measuring.startQuery(.transparent_rendering);
c.glTextureBarrier(); c.glTextureBarrier();
chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time); chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight);
c.glBlendEquation(c.GL_FUNC_ADD); c.glBlendEquation(c.GL_FUNC_ADD);
c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR);