From c0f45184b88b0fff07865d030c4c2afa4eb3d476 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Thu, 15 Oct 2026 13:20:59 +0200 Subject: [PATCH] Move light data from UBO to std430 SSBO --- gl/glsl_common.cpp | 8 +++- gl/shader.cpp | 3 -- gl/ubo.h | 38 +++++++++++++-- rendering/opengl46/opengl46renderer.cpp | 63 +++++++++++++------------ rendering/opengl46/opengl46renderer.h | 5 +- 5 files changed, 74 insertions(+), 43 deletions(-) diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 26c31000..00656939 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -15,6 +15,7 @@ void gl::glsl_common_setup() "#define MAX_CASCADES " + std::to_string(MAX_CASCADES) + "U\n" + "#define MAX_PARAMS " + std::to_string(MAX_PARAMS) + "U\n" + "#define MAX_INSTANCES_PER_BATCH " + std::to_string(MAX_INSTANCES_PER_BATCH) + "U\n" + + "#define LIGHT_SSBO_BINDING " + std::to_string(LIGHT_SSBO_BINDING) + "\n" + R"STRING( const uint LIGHT_SPOT = 0U; const uint LIGHT_POINT = 1U; @@ -42,15 +43,18 @@ void gl::glsl_common_setup() vec4 headlight_weights; }; - layout(std140) uniform light_ubo + // storage blocks are guaranteed only in fragment and compute shaders (GL 4.6 minimum for the other stages is 0) + #if defined(STAGE_FRAGMENT) || defined(STAGE_COMPUTE) + layout(std430, binding = LIGHT_SSBO_BINDING) readonly buffer light_ssbo { vec3 ambient; vec3 fog_color; uint lights_count; - light_s lights[MAX_LIGHTS]; + light_s lights[]; }; + #endif layout (std140) uniform model_ubo { diff --git a/gl/shader.cpp b/gl/shader.cpp index ca073433..a90a4f2f 100644 --- a/gl/shader.cpp +++ b/gl/shader.cpp @@ -283,9 +283,6 @@ void gl::program::init() if ((index = glGetUniformBlockIndex(*this, "model_ubo")) != GL_INVALID_INDEX) glUniformBlockBinding(*this, index, 1); - if ((index = glGetUniformBlockIndex(*this, "light_ubo")) != GL_INVALID_INDEX) - glUniformBlockBinding(*this, index, 2); - if ((index = glGetUniformBlockIndex(*this, "instance_ubo")) != GL_INVALID_INDEX) glUniformBlockBinding(*this, index, 3); } diff --git a/gl/ubo.h b/gl/ubo.h index d5335b21..d41d0c0b 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -4,6 +4,7 @@ #include "bindable.h" #include "buffer.h" #include +#include #define UBS_PAD(x) uint8_t PAD[x] @@ -103,7 +104,14 @@ namespace gl static_assert(sizeof(instance_ubs) == 64 * MAX_INSTANCES_PER_BATCH, "bad size of instance_ubs"); - struct light_element_ubs + // layout std430 (shader storage buffers) + // structs must match with GLSL, offsets checked below + + // binding points of the shader storage blocks: layout(std430, binding = ...) in glsl_common + const GLuint LIGHT_SSBO_BINDING = 0; + + // one light source, element of light_ssbo.lights[] (GLSL: light_s) + struct light_element { enum type_e { @@ -132,11 +140,27 @@ namespace gl glm::vec4 headlight_weights; }; - static_assert(sizeof(light_element_ubs) == 144, "bad size of ubs"); + // std430 array stride of a struct is its size rounded up to its alignment (16, from mat4/vec4) + static_assert(sizeof(light_element) == 144, "bad size of light_element"); + static_assert(sizeof(light_element) % 16 == 0, "light_element must be a whole std430 array stride"); + static_assert(offsetof(light_element, pos) == 0, "bad layout of light_element"); + static_assert(offsetof(light_element, type) == 12, "bad layout of light_element"); + static_assert(offsetof(light_element, dir) == 16, "bad layout of light_element"); + static_assert(offsetof(light_element, in_cutoff) == 28, "bad layout of light_element"); + static_assert(offsetof(light_element, color) == 32, "bad layout of light_element"); + static_assert(offsetof(light_element, out_cutoff) == 44, "bad layout of light_element"); + static_assert(offsetof(light_element, linear) == 48, "bad layout of light_element"); + static_assert(offsetof(light_element, quadratic) == 52, "bad layout of light_element"); + static_assert(offsetof(light_element, intensity) == 56, "bad layout of light_element"); + static_assert(offsetof(light_element, ambient) == 60, "bad layout of light_element"); + static_assert(offsetof(light_element, headlight_projection) == 64, "bad layout of light_element"); + static_assert(offsetof(light_element, headlight_weights) == 128, "bad layout of light_element"); + // capacity of light_data.lights; slot 0 is the sun const size_t MAX_LIGHTS = 8; - struct light_ubs + // contents of light_ssbo: global lighting header followed by the light array + struct light_data { packed_vec3 ambient; UBS_PAD(4); @@ -144,10 +168,14 @@ namespace gl packed_vec3 fog_color; uint32_t lights_count; - light_element_ubs lights[MAX_LIGHTS]; + light_element lights[MAX_LIGHTS]; }; - static_assert(sizeof(light_ubs) == 32 + sizeof(light_element_ubs) * MAX_LIGHTS, "bad size of ubs"); + static_assert(offsetof(light_data, ambient) == 0, "bad layout of light_data"); + static_assert(offsetof(light_data, fog_color) == 16, "bad layout of light_data"); + static_assert(offsetof(light_data, lights_count) == 28, "bad layout of light_data"); + static_assert(offsetof(light_data, lights) == 32, "bad layout of light_data"); + static_assert(sizeof(light_data) == 32 + sizeof(light_element) * MAX_LIGHTS, "bad size of light_data"); #pragma pack(pop) } diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index d73f33af..1402a221 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -179,16 +179,17 @@ bool opengl46_renderer::Init(GLFWwindow *Window) scene_ubo = std::make_unique(sizeof(gl::scene_ubs), 0); model_ubo = std::make_unique(sizeof(gl::model_ubs), 1, GL_STREAM_DRAW); - light_ubo = std::make_unique(sizeof(gl::light_ubs), 2); + light_ssbo = std::make_unique(sizeof(gl::light_data), gl::LIGHT_SSBO_BINDING); + light_ssbo->bind_base(); instance_ubo = std::make_unique(sizeof(gl::instance_ubs), 3, GL_STREAM_DRAW); // better initialize with 0 to not crash driver/whole system // when we forget - memset(&light_ubs, 0, sizeof(light_ubs)); + memset(&light_data, 0, sizeof(light_data)); memset(&model_ubs, 0, sizeof(model_ubs)); memset(&scene_ubs, 0, sizeof(scene_ubs)); - light_ubo->update(light_ubs); + light_ssbo->update(light_data); model_ubo->update(model_ubs); scene_ubo->update(scene_ubs); @@ -453,7 +454,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp) model_ubo->bind_uniform(); scene_ubo->bind_uniform(); - light_ubo->bind_uniform(); + light_ssbo->bind_base(); if (!Global.gfx_skippipeline) { @@ -1868,9 +1869,9 @@ void opengl46_renderer::setup_environment_light(TEnvironmentType const Environme void opengl46_renderer::setup_sunlight_intensity( float const Factor ) { m_sunlight.apply_intensity( Factor ); - light_ubs.lights[ 0 ].intensity = m_sunlight.factor * (1.0f - Global.Overcast * 0.33f); - light_ubs.ambient = m_sunlight.ambient * m_sunlight.factor; - light_ubo->update( light_ubs ); + light_data.lights[ 0 ].intensity = m_sunlight.factor * (1.0f - Global.Overcast * 0.33f); + light_data.ambient = m_sunlight.ambient * m_sunlight.factor; + light_ssbo->update( light_data ); } bool opengl46_renderer::Render(world_environment *Environment) @@ -3831,12 +3832,12 @@ bool opengl46_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li setup_sunlight_intensity(Dynamic->fShade); } - auto const old_ambient { light_ubs.ambient }; + auto const old_ambient { light_data.ambient }; auto const luminance { Global.fLuminance * ( Dynamic->fShade > 0.0f ? Dynamic->fShade : 1.0f ) }; if( Lightlevel > 0.f ) { // crude way to light the cabin, until we have something more complete in place - light_ubs.ambient += packed_vec3( ( Dynamic->InteriorLight * Lightlevel ) * std::clamp( 1.25f - (float)luminance, 0.f, 1.f ) ); - light_ubo->update( light_ubs ); + light_data.ambient += packed_vec3( ( Dynamic->InteriorLight * Lightlevel ) * std::clamp( 1.25f - (float)luminance, 0.f, 1.f ) ); + light_ssbo->update( light_data ); } // render @@ -3851,7 +3852,7 @@ bool opengl46_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li Render(Dynamic->mdKabina, Dynamic->Material(), 0.0); } // post-render restore - light_ubs.ambient = old_ambient; + light_data.ambient = old_ambient; setup_sunlight_intensity(); break; @@ -5564,15 +5565,15 @@ void opengl46_renderer::Preview_Color(TAnimModel *Instance, float const Squaredi scene_ubo->bind_uniform(); // the sun is the only light, and there's no fog auto const &modelview{OpenGLMatrices.data(GL_MODELVIEW)}; - light_ubs.ambient = m_sunlight.ambient; - light_ubs.fog_color = glm::vec3{1.f}; - light_ubs.lights_count = 1; - light_ubs.lights[0].type = gl::light_element_ubs::DIR; - light_ubs.lights[0].dir = modelview * glm::vec4(m_sunlight.direction, 0.0f); - light_ubs.lights[0].color = m_sunlight.diffuse; - light_ubs.lights[0].ambient = 0.0f; - light_ubs.lights[0].intensity = 1.0f; - light_ubo->update(light_ubs); + light_data.ambient = m_sunlight.ambient; + light_data.fog_color = glm::vec3{1.f}; + light_data.lights_count = 1; + light_data.lights[0].type = gl::light_element::DIR; + light_data.lights[0].dir = modelview * glm::vec4(m_sunlight.direction, 0.0f); + light_data.lights[0].color = m_sunlight.diffuse; + light_data.lights[0].ambient = 0.0f; + light_data.lights[0].intensity = 1.0f; + light_ssbo->update(light_data); model_ubs.fog_density = 0.f; model_ubs.future = glm::mat4(); model_ubs.emission = 0.f; @@ -5914,11 +5915,11 @@ void opengl46_renderer::Update_Lights(light_array &Lights) renderlight->apply_intensity( ( scenelight.count * 0.5 ) * ( scenelight.owner->DimHeadlights ? 0.5 : 1.0 ) ); renderlight->apply_angle(); - gl::light_element_ubs *light = &light_ubs.lights[light_i]; + gl::light_element *light = &light_data.lights[light_i]; light->pos = mv * glm::vec4(renderlight->position, 1.0f); light->dir = mv * glm::vec4(renderlight->direction, 0.0f); - light->type = gl::light_element_ubs::HEADLIGHTS; + light->type = gl::light_element::HEADLIGHTS; light->in_cutoff = headlight_config.in_cutoff; light->out_cutoff = headlight_config.out_cutoff; light->color = renderlight->diffuse * renderlight->factor; @@ -5979,16 +5980,16 @@ void opengl46_renderer::Update_Lights(light_array &Lights) ++light_i; ++renderlight; } - light_ubs.lights_count = light_i; + light_data.lights_count = light_i; // fill sunlight data - light_ubs.ambient = m_sunlight.ambient * m_sunlight.factor;// *simulation::Environment.light_intensity(); - light_ubs.lights[0].type = gl::light_element_ubs::DIR; - light_ubs.lights[0].dir = mv * glm::vec4(m_sunlight.direction, 0.0f); - light_ubs.lights[0].color = m_sunlight.diffuse * m_sunlight.factor * simulation::Environment.light_intensity(); - light_ubs.lights[0].ambient = 0.0f; - light_ubs.lights[0].intensity = 1.0f; + light_data.ambient = m_sunlight.ambient * m_sunlight.factor;// *simulation::Environment.light_intensity(); + light_data.lights[0].type = gl::light_element::DIR; + light_data.lights[0].dir = mv * glm::vec4(m_sunlight.direction, 0.0f); + light_data.lights[0].color = m_sunlight.diffuse * m_sunlight.factor * simulation::Environment.light_intensity(); + light_data.lights[0].ambient = 0.0f; + light_data.lights[0].intensity = 1.0f; // fill fog data - light_ubs.fog_color = Global.FogColor; + light_data.fog_color = Global.FogColor; if (Global.fFogEnd > 0) { m_fogrange = Global.fFogEnd / std::max(1.f, Global.Overcast * 2.f); @@ -6000,7 +6001,7 @@ void opengl46_renderer::Update_Lights(light_array &Lights) } // ship config data to the gpu model_ubo->update(model_ubs); - light_ubo->update(light_ubs); + light_ssbo->update(light_data); } bool opengl46_renderer::Init_caps() diff --git a/rendering/opengl46/opengl46renderer.h b/rendering/opengl46/opengl46renderer.h index f8c37f39..af3c13e1 100644 --- a/rendering/opengl46/opengl46renderer.h +++ b/rendering/opengl46/opengl46renderer.h @@ -21,6 +21,7 @@ http://mozilla.org/MPL/2.0/. #include "rendering/lightarray.h" #include "vr/vr_interface.h" #include "gl/ubo.h" +#include "gl/ssbo.h" #include "gl/framebuffer.h" #include "gl/renderbuffer.h" #include "gl/postfx.h" @@ -450,7 +451,7 @@ class opengl46_renderer : public gfx_renderer { std::unique_ptr scene_ubo; std::unique_ptr model_ubo; - std::unique_ptr light_ubo; + std::unique_ptr light_ssbo; // per-instance modelview UBO for GPU-instanced draws. // Slot 0 is initialized once to identity and never overwritten by non-instanced // rendering, so existing draws (where gl_InstanceID==0) compute identity*modelview @@ -486,7 +487,7 @@ class opengl46_renderer : public gfx_renderer { std::vector m_instance_banddistances; gl::scene_ubs scene_ubs; gl::model_ubs model_ubs; - gl::light_ubs light_ubs; + gl::light_data light_data; std::unordered_map> m_shaders;