// culls the model instances of the buckets queued in a render pass and assigns them their lod bands, with the tests // opengl46_renderer::Render_Instanced() makes on the cpu. one invocation per record of a queued bucket. // stage 0 counts the instances which pass, per entry (lod band of a group); after instance_cull_offsets.comp turned the // counts into the first slots of the entries, stage 1 writes their modelview matrices there, in no particular order #include layout(local_size_x = INSTANCE_CULL_GROUP_SIZE) in; // scene::basic_cell::instance_record struct instance_record { vec3 position; // relative to the origin of the bucket float radius; float rangesquaredmin; float rangesquaredmax; uint flags; uint pad; vec4 rotationscale[3]; }; struct instance_cull_job { vec3 origin; uint first; vec3 distanceorigin; uint count; uint group; uint candidatestart; uint pad0; uint pad1; }; struct instance_cull_group { uint lodfirst; uint lodcount; uint entryfirst; uint pad; }; layout(std430, binding = INSTANCE_RECORD_SSBO_BINDING) readonly buffer instance_record_ssbo { instance_record records[]; }; layout(std430, binding = INSTANCE_JOB_SSBO_BINDING) readonly buffer instance_job_ssbo { instance_cull_job jobs[]; }; layout(std430, binding = INSTANCE_GROUP_SSBO_BINDING) readonly buffer instance_group_ssbo { instance_cull_group groups[]; }; layout(std430, binding = INSTANCE_LOD_SSBO_BINDING) readonly buffer instance_lod_ssbo { float lodbounds[]; }; layout(std430, binding = INSTANCE_COUNTER_SSBO_BINDING) buffer instance_counter_ssbo { uint counters[]; }; layout(std430, binding = INSTANCE_OUTPUT_SSBO_BINDING) writeonly buffer instance_output_ssbo { mat4 modelviews[]; }; // rotation of the view of the pass: the instances are placed relative to its camera layout(location = 0) uniform mat4 view; // planes of the frustum of the pass, relative to its camera layout(location = 1) uniform vec4 planes[6]; // x: scale of the squared distance, y: added to it past the range test, z: range of the scaled distance before the // addition (reflections), w: squared draw range layout(location = 7) uniform vec4 distance_params; // x: zoom factor layout(location = 8) uniform vec4 size_params; // x: jobs, y: records in all jobs, z: entries, w: stage layout(location = 9) uniform uvec4 counts; const uint INSTANCE_VISIBLE = 1u; const uint INSTANCE_INSTANCEABLE = 2u; // smallest part of the view an instance has to take const float MIN_SCREEN_SIZE = 0.003; // the job which holds the given record: the last one starting at it or before it uint find_job(uint candidate) { uint low = 0u; uint high = counts.x - 1u; while (low < high) { uint middle = (low + high + 1u) / 2u; if (jobs[middle].candidatestart <= candidate) low = middle; else high = middle - 1u; } return low; } void main() { // the dispatch can be two dimensional when the records don't fit in the work group count limit of one uint candidate = (gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x) * INSTANCE_CULL_GROUP_SIZE + gl_LocalInvocationIndex; if (candidate >= counts.y) return; instance_cull_job job = jobs[find_job(candidate)]; instance_record record = records[job.first + (candidate - job.candidatestart)]; // instances hidden, or drawn on their own after getting animations or skins from events if ((record.flags & (INSTANCE_VISIBLE | INSTANCE_INSTANCEABLE)) != (INSTANCE_VISIBLE | INSTANCE_INSTANCEABLE)) return; vec3 location = job.origin + record.position; for (int plane = 0; plane < 6; ++plane) { if (dot(planes[plane].xyz, location) + planes[plane].w <= -record.radius) return; } vec3 offset = job.distanceorigin + record.position; float distancesquared = dot(offset, offset) * distance_params.x; if (distancesquared > distance_params.z) return; distancesquared += distance_params.y; if ((distancesquared < record.rangesquaredmin) || (distancesquared >= record.rangesquaredmax)) return; if (distancesquared > distance_params.w) return; if (record.radius * record.radius * size_params.x / distancesquared < MIN_SCREEN_SIZE * MIN_SCREEN_SIZE) return; // band: the number of lod bounds the distance reached, like upper_bound over the sorted bounds instance_cull_group group = groups[job.group]; uint band = 0u; for (uint bound = 0u; bound < group.lodcount; ++bound) { if (lodbounds[group.lodfirst + bound] <= distancesquared) band = bound + 1u; } uint entry = group.entryfirst + band; if (counts.w == 0u) { atomicAdd(counters[INSTANCE_COUNTER_HEADER + entry], 1u); } else { uint slot = atomicAdd(counters[INSTANCE_COUNTER_HEADER + counts.z + entry], 1u); modelviews[slot] = view * mat4(record.rotationscale[0], record.rotationscale[1], record.rotationscale[2], vec4(location, 1.0)); } }