From 5a485024b1dbba968cdf5020ea067194af785a70 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Sat, 11 May 2024 03:42:26 +0200 Subject: [PATCH] Zephyr: Renderer: implement more proper frustum culling (WIP) --- app/next/src/main_window.cpp | 5 +- .../renderer/backend/render_backend.hpp | 6 ++ .../src/backend/opengl/render_backend.cpp | 99 +++++++++++++------ .../src/backend/opengl/render_backend.hpp | 3 +- .../src/backend/opengl/render_geometry.hpp | 2 +- zephyr/renderer/src/render_engine.cpp | 1 + 6 files changed, 83 insertions(+), 33 deletions(-) diff --git a/app/next/src/main_window.cpp b/app/next/src/main_window.cpp index 74a5332..91fad70 100644 --- a/app/next/src/main_window.cpp +++ b/app/next/src/main_window.cpp @@ -7,7 +7,7 @@ #include "main_window.hpp" static const bool enable_validation_layers = true; -static const bool benchmark_scene_size = false; +static const bool benchmark_scene_size = true; namespace zephyr { @@ -262,6 +262,9 @@ namespace zephyr { }; std::copy_n(index_data, sizeof(index_data) / sizeof(u32), indices.begin()); + fmt::print("min: {} {} {}\n", cube_geometry->GetAABB().Min().X(), cube_geometry->GetAABB().Min().Y(), cube_geometry->GetAABB().Min().X()); + fmt::print("max: {} {} {}\n", cube_geometry->GetAABB().Max().X(), cube_geometry->GetAABB().Max().Y(), cube_geometry->GetAABB().Max().X()); + const int grid_size = 37; for(int x = -grid_size / 2; x < grid_size / 2; x++) { diff --git a/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp b/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp index a6aaf30..ffe36d7 100644 --- a/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp +++ b/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp @@ -45,6 +45,12 @@ namespace zephyr { }; struct RenderCamera { + /** + * This struct may be uploaded to a GPU buffer directly and therefore must match the following std430/std140 data layout: + * mat4 projection; + * mat4 view; + * vec4 frustum_planes[6]; + */ Matrix4 projection{}; Matrix4 view{}; Frustum frustum{}; diff --git a/zephyr/renderer/src/backend/opengl/render_backend.cpp b/zephyr/renderer/src/backend/opengl/render_backend.cpp index 092e8e2..0d96433 100644 --- a/zephyr/renderer/src/backend/opengl/render_backend.cpp +++ b/zephyr/renderer/src/backend/opengl/render_backend.cpp @@ -22,11 +22,14 @@ namespace zephyr { glCreateBuffers(1u, &m_gl_render_bundle_ssbo); glNamedBufferStorage(m_gl_render_bundle_ssbo, sizeof(RenderBundleItem) * k_max_draws_per_draw_call, nullptr, GL_DYNAMIC_STORAGE_BIT); - glCreateBuffers(1u, &m_gl_draw_list_ssbo); - glNamedBufferStorage(m_gl_draw_list_ssbo, sizeof(OpenGLDrawElementsIndirectCommand) * k_max_draws_per_draw_call, nullptr, 0); + glCreateBuffers(1u, &m_gl_draw_list_command_ssbo); + glNamedBufferStorage(m_gl_draw_list_command_ssbo, sizeof(OpenGLDrawElementsIndirectCommand) * k_max_draws_per_draw_call, nullptr, 0); + + glCreateBuffers(1u, &m_gl_draw_list_transform_ssbo); + glNamedBufferStorage(m_gl_draw_list_transform_ssbo, sizeof(Matrix4) * k_max_draws_per_draw_call, nullptr, 0); glCreateBuffers(1u, &m_gl_camera_ubo); - glNamedBufferStorage(m_gl_camera_ubo, sizeof(Matrix4), nullptr, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(m_gl_camera_ubo, sizeof(RenderCamera), nullptr, GL_DYNAMIC_STORAGE_BIT); glCreateBuffers(1u, &m_gl_draw_count_ubo); glNamedBufferStorage(m_gl_draw_count_ubo, sizeof(u32), nullptr, GL_DYNAMIC_STORAGE_BIT); @@ -45,7 +48,8 @@ namespace zephyr { glDeleteBuffers(1u, &m_gl_draw_count_out_ac); glDeleteBuffers(1u, &m_gl_draw_count_ubo); glDeleteBuffers(1u, &m_gl_camera_ubo); - glDeleteBuffers(1u, &m_gl_draw_list_ssbo); + glDeleteBuffers(1u, &m_gl_draw_list_transform_ssbo); + glDeleteBuffers(1u, &m_gl_draw_list_command_ssbo); glDeleteBuffers(1u, &m_gl_render_bundle_ssbo); glDeleteProgram(m_gl_draw_list_builder_program); glDeleteProgram(m_gl_draw_program); @@ -93,12 +97,12 @@ namespace zephyr { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - const Matrix4 view_projection = render_camera.projection * render_camera.view; - glNamedBufferSubData(m_gl_camera_ubo, 0, sizeof(Matrix4), &view_projection); + glNamedBufferSubData(m_gl_camera_ubo, 0, sizeof(RenderCamera), &render_camera); - // TODO(fleroviux): attempt to keep buffers bound throughout the entire rendering process for minimal number of OpenGL calls. + // TODO(fleroviux): attempt to keep more buffers bound throughout the entire rendering process for minimal number of OpenGL calls. glBindBufferBase(GL_UNIFORM_BUFFER, 0u, m_gl_camera_ubo); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, m_gl_draw_list_transform_ssbo); for(const auto& [key, render_bundle] : render_bundles) { const size_t render_bundle_size = render_bundle.size(); @@ -115,9 +119,9 @@ namespace zephyr { { glUseProgram(m_gl_draw_list_builder_program); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, m_render_geometry_manager->GetDrawCommandBuffer()); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1u, m_gl_render_bundle_ssbo); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2u, m_gl_draw_list_ssbo); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2u, m_render_geometry_manager->GetGeometryRenderDataBuffer()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3u, m_gl_draw_list_command_ssbo); glBindBufferBase(GL_UNIFORM_BUFFER, 1u, m_gl_draw_count_ubo); glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0u, m_gl_draw_count_out_ac); @@ -125,9 +129,9 @@ namespace zephyr { const GLuint workgroup_group_count = (number_of_draws + workgroup_size - 1u) / workgroup_size; glDispatchCompute(workgroup_group_count, 1u, 1u); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, 0u); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1u, 0u); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2u, 0u); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3u, 0u); glBindBufferBase(GL_UNIFORM_BUFFER, 1u, 0u); glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0u, 0u); } @@ -137,9 +141,8 @@ namespace zephyr { glUseProgram(m_gl_draw_program); glBindVertexArray(m_render_geometry_manager->GetVAOFromLayout(RenderGeometryLayout{key})); - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_gl_draw_list_ssbo); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_gl_draw_list_command_ssbo); glBindBuffer(GL_PARAMETER_BUFFER, m_gl_draw_count_out_ac); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, m_gl_render_bundle_ssbo); glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT); glMultiDrawElementsIndirectCount(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr, 0u, (GLsizei)number_of_draws, sizeof(OpenGLDrawElementsIndirectCommand)); @@ -147,12 +150,12 @@ namespace zephyr { glBindVertexArray(0u); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0u); glBindBuffer(GL_PARAMETER_BUFFER, 0u); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, 0u); } } } glBindBufferBase(GL_UNIFORM_BUFFER, 0u, 0u); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0u, 0u); } void OpenGLRenderBackend::SwapBuffers() { @@ -163,17 +166,13 @@ namespace zephyr { GLuint vert_shader = CreateShader(R"( #version 460 core - struct RenderBundleItem { - mat4 local_to_world; - uint draw_command_id; - }; - - layout(std430, binding = 0) readonly buffer RenderBundleBuffer { - RenderBundleItem rb_render_bundle_items[]; + layout(std430, binding = 0) readonly buffer DrawListTransformBuffer { + mat4 rb_draw_list_transform[]; }; - layout(std140, binding = 0) uniform Transform { + layout(std140, binding = 0) uniform Camera { mat4 u_projection; + mat4 u_view; }; layout(location = 0) in vec3 a_position; @@ -187,7 +186,7 @@ namespace zephyr { void main() { v_normal = a_normal; v_color = a_color; - gl_Position = u_projection * rb_render_bundle_items[gl_DrawID].local_to_world * vec4(a_position, 1.0); + gl_Position = u_projection * u_view * rb_draw_list_transform[gl_DrawID] * vec4(a_position, 1.0); } )", GL_VERTEX_SHADER); @@ -231,20 +230,26 @@ namespace zephyr { uint geometry_id; }; - layout(std430, binding = 0) readonly buffer GeometryBuffer { - RenderGeometryRenderData rb_render_geometry_render_data[]; + layout(std430, binding = 0) buffer DrawListTransformBuffer { + mat4 b_draw_list_transform[]; }; layout(std430, binding = 1) readonly buffer RenderBundleBuffer { RenderBundleItem rb_render_bundle_items[]; }; - layout(std430, binding = 2) buffer CommandBuffer { + layout(std430, binding = 2) readonly buffer GeometryBuffer { + RenderGeometryRenderData rb_render_geometry_render_data[]; + }; + + layout(std430, binding = 3) buffer CommandBuffer { DrawIndirectCommand b_command_buffer[]; }; - layout(std140, binding = 0) uniform Transform { + layout(std140, binding = 0) uniform Camera { mat4 u_projection; + mat4 u_view; + vec4 u_frustum_planes[6]; }; layout(std140, binding = 1) uniform DrawCount { @@ -265,10 +270,44 @@ namespace zephyr { RenderBundleItem render_bundle_item = rb_render_bundle_items[draw_index]; RenderGeometryRenderData render_data = rb_render_geometry_render_data[render_bundle_item.geometry_id]; - vec4 clip_aabb_center = u_projection * render_bundle_item.local_to_world * vec4((render_data.aabb_min.xyz + render_data.aabb_max.xyz) * 0.5, 1.0); - if(abs(clip_aabb_center.x) < clip_aabb_center.w && abs(clip_aabb_center.y) < clip_aabb_center.w && abs(clip_aabb_center.z) < clip_aabb_center.w) { - b_command_buffer[draw_index] = render_data.draw_command; - atomicCounterIncrement(u_draw_count_out); + bool inside_frustum = true; + vec4 model_aabb_min = render_data.aabb_min; + vec4 model_aabb_max = render_data.aabb_max; + + mat4 mv = u_view * render_bundle_item.local_to_world; + + // TODO(fleroviux): optimize this plenty! + vec4[] aabb_points = vec4[]( + mv * vec4(model_aabb_min.x, model_aabb_min.y, model_aabb_min.z, 1.0), + mv * vec4(model_aabb_min.x, model_aabb_min.y, model_aabb_max.z, 1.0), + mv * vec4(model_aabb_min.x, model_aabb_max.y, model_aabb_min.z, 1.0), + mv * vec4(model_aabb_min.x, model_aabb_max.y, model_aabb_max.z, 1.0), + mv * vec4(model_aabb_max.x, model_aabb_min.y, model_aabb_min.z, 1.0), + mv * vec4(model_aabb_max.x, model_aabb_min.y, model_aabb_max.z, 1.0), + mv * vec4(model_aabb_max.x, model_aabb_max.y, model_aabb_min.z, 1.0), + mv * vec4(model_aabb_max.x, model_aabb_max.y, model_aabb_max.z, 1.0) + ); + + vec4 view_aabb_min = min(aabb_points[0], min(aabb_points[1], min(aabb_points[2], min(aabb_points[3], min(aabb_points[4], min(aabb_points[5], min(aabb_points[6], aabb_points[7]))))))); + vec4 view_aabb_max = max(aabb_points[0], max(aabb_points[1], max(aabb_points[2], max(aabb_points[3], max(aabb_points[4], max(aabb_points[5], max(aabb_points[6], aabb_points[7]))))))); + + for(int i = 0; i < 6; i++) { + vec4 frustum_plane = u_frustum_planes[i]; + // TODO(fleroviux): can this be shortened? + vec4 aabb_corner = vec4( + frustum_plane.x > 0 ? view_aabb_max.x : view_aabb_min.x, + frustum_plane.y > 0 ? view_aabb_max.y : view_aabb_min.y, + frustum_plane.z > 0 ? view_aabb_max.z : view_aabb_min.z, + -1.0 + ); + + inside_frustum = inside_frustum && dot(aabb_corner, frustum_plane) >= 0.0; + } + + if(inside_frustum) { + uint draw_id = atomicCounterIncrement(u_draw_count_out); + b_draw_list_transform[draw_id] = render_bundle_item.local_to_world; + b_command_buffer[draw_id] = render_data.draw_command; } } } diff --git a/zephyr/renderer/src/backend/opengl/render_backend.hpp b/zephyr/renderer/src/backend/opengl/render_backend.hpp index 644b4ae..18532a7 100644 --- a/zephyr/renderer/src/backend/opengl/render_backend.hpp +++ b/zephyr/renderer/src/backend/opengl/render_backend.hpp @@ -51,7 +51,8 @@ namespace zephyr { GLuint m_gl_draw_program{}; GLuint m_gl_draw_list_builder_program{}; GLuint m_gl_render_bundle_ssbo{}; - GLuint m_gl_draw_list_ssbo{}; + GLuint m_gl_draw_list_command_ssbo{}; + GLuint m_gl_draw_list_transform_ssbo{}; GLuint m_gl_camera_ubo{}; GLuint m_gl_draw_count_ubo{}; GLuint m_gl_draw_count_out_ac{}; diff --git a/zephyr/renderer/src/backend/opengl/render_geometry.hpp b/zephyr/renderer/src/backend/opengl/render_geometry.hpp index b1ac3b8..fbee26f 100644 --- a/zephyr/renderer/src/backend/opengl/render_geometry.hpp +++ b/zephyr/renderer/src/backend/opengl/render_geometry.hpp @@ -150,7 +150,7 @@ namespace zephyr { return bucket.vao; } - GLuint GetDrawCommandBuffer() { + GLuint GetGeometryRenderDataBuffer() { return m_geometry_render_data->GetBufferHandle(); } diff --git a/zephyr/renderer/src/render_engine.cpp b/zephyr/renderer/src/render_engine.cpp index 803add1..28973d8 100644 --- a/zephyr/renderer/src/render_engine.cpp +++ b/zephyr/renderer/src/render_engine.cpp @@ -48,6 +48,7 @@ namespace zephyr { const PerspectiveCameraComponent& camera_component = node->GetComponent(); m_render_camera[1].projection = camera_component.GetProjectionMatrix(); m_render_camera[1].view = node->GetTransform().GetWorld().Inverse(); + m_render_camera[1].frustum = camera_component.GetFrustum(); } return true;