17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 08:39:19 +02:00
Files
maszyna/shaders/instance_cull.comp

152 lines
4.7 KiB
Plaintext

// 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 <common>
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));
}
}