mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-22 05:16:25 +02:00
feat(gl46): leave out instance buckets beyond their range before the culling
This commit is contained in:
@@ -282,6 +282,7 @@ cab and other views go as before.
|
||||
| `gfx.gpucull.shadows.lamps yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the models of the scenery part of the shadow maps of scenery lamps too (each map tested against the reach of its lamp, as before); the part with the vehicles has no models of the scenery |
|
||||
| `gfx.gpucull.sleepers yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the sleepers of the tracks (`sleepermodel`) of the main view too, each one tested against the view and given its level of detail by its distance, as before (the tracks within `sleeperdistance` are chosen as before); they also take the occlusion test, the depth prepass and the two phases of the models |
|
||||
| `gfx.gpucull.models yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the models drawn on their own too (scenery models with lights, animations, or skins and animations given by events, which the instanced draws leave out), in every pass the instances are culled in: each such model is tested on its own like the others (visibility, the view, its range, the draw range, its size on the screen) and given the level of detail its distance picks from the `mindistance`/`maxdistance` of its submodels; the processor animates and prepares the model only to record what it draws. Models with billboards turned to the camera stay on the processor, and so do vehicles |
|
||||
| `gfx.gpucull.bucketrange yes/no` | `yes` | the models of one kind in a scene cell lying wholly beyond their range (the largest visibility range of their nodes, and of the submodels of the model) are left out of each pass before the culling, the one on the graphics card included, which would leave out each of them anyway; the picture stays the same. Matters with many small models of a short range (grass): without it they are tested and sent to the graphics card in every pass up to the draw range |
|
||||
| `gfx.depthprepass.scene yes/no` | `no` | the depth of the whole opaque scene of the main view (terrain, tracks, buildings, models, vehicles, the cab) is drawn before its colour, see below |
|
||||
| `gfx.depthprepass.replay yes/no` | `yes` | the scene is gone through once for its depth and its colour: what the colour pass draws is recorded and both are drawn from the record, see below. `no`: twice, as before |
|
||||
|
||||
@@ -417,6 +418,10 @@ editor goes through the first `dynamiclights` headlights, the reflections throug
|
||||
`gpu cull:` is followed by how many sleepers of how many tracks were queued (they are counted among the models). With
|
||||
`gfx.gpucull.models` each `gpu cull` row is followed by how many models drawn on their own were queued (also counted
|
||||
among the models).
|
||||
Each `gpu cull` row is also followed by how many models the pass tested in how many buckets (models of one kind in one
|
||||
cell), with `gfx.gpucull.bucketrange` how many buckets and models were left out beyond their range, how many records of
|
||||
models were sent to the graphics card, and whether the buffer of the records was started over (all buckets of the pass
|
||||
sent anew).
|
||||
- With `gfx.depthprepass.scene yes` Shift+F12 shows the `scene depth:` row (draws of the depth pass and its processor time)
|
||||
and the timing `scene depth:` (its GPU time). With `gfx.depthprepass.replay yes` the row tells how many of the recorded
|
||||
parts (of models, of the scenery; neighbouring tracks count one by one) had their depth drawn, in how many calls, how many
|
||||
|
||||
@@ -83,6 +83,7 @@ void opengl46_instance_cull::begin_pass(pass_setup const &Setup)
|
||||
m_sleepertracks = 0;
|
||||
m_sleepers = 0;
|
||||
m_models = 0;
|
||||
m_counts = {};
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area)
|
||||
@@ -90,6 +91,14 @@ void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key c
|
||||
add(Key, Bucket, Area, false);
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::skip_bucket(scene::basic_cell::instance_bucket const &Bucket)
|
||||
{
|
||||
if (!m_inpass)
|
||||
return;
|
||||
++m_counts.skippedbuckets;
|
||||
m_counts.skippedinstances += Bucket.records.size();
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::add_sleepers(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area)
|
||||
{
|
||||
add(Key, Bucket, Area, true);
|
||||
@@ -213,12 +222,13 @@ void opengl46_instance_cull::place(scene::basic_cell::instance_bucket const &Buc
|
||||
if (current && count <= Bucket.gpucapacity)
|
||||
{
|
||||
shared.records->update(Bucket.records.data(), static_cast<GLintptr>(Bucket.gpufirst) * RECORD_SIZE, static_cast<GLsizeiptr>(count) * RECORD_SIZE);
|
||||
m_counts.sentrecords += count;
|
||||
Bucket.gpustale = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// a new place, with some room for instances the editor adds
|
||||
auto const allocate = [&shared](scene::basic_cell::instance_bucket const &Target) {
|
||||
auto const allocate = [this, &shared](scene::basic_cell::instance_bucket const &Target) {
|
||||
auto const records{static_cast<std::uint32_t>(Target.records.size())};
|
||||
Target.gpufirst = shared.recordfill;
|
||||
Target.gpucapacity = records + records / 4;
|
||||
@@ -226,6 +236,7 @@ void opengl46_instance_cull::place(scene::basic_cell::instance_bucket const &Buc
|
||||
Target.gpustale = false;
|
||||
shared.recordfill += Target.gpucapacity;
|
||||
shared.records->update(Target.records.data(), static_cast<GLintptr>(Target.gpufirst) * RECORD_SIZE, static_cast<GLsizeiptr>(records) * RECORD_SIZE);
|
||||
m_counts.sentrecords += records;
|
||||
};
|
||||
|
||||
auto const capacity{static_cast<std::uint32_t>(shared.records->size() / RECORD_SIZE)};
|
||||
@@ -243,6 +254,7 @@ void opengl46_instance_cull::place(scene::basic_cell::instance_bucket const &Buc
|
||||
shared.records->resize(std::max(2 * live * RECORD_SIZE, 2 * shared.records->size()));
|
||||
shared.restarted = true;
|
||||
shared.restartframe = m_setup.frame;
|
||||
m_counts.restarted = true;
|
||||
++shared.recordgeneration;
|
||||
shared.recordfill = 0;
|
||||
for (std::size_t job = 0; job < m_buckets.size(); ++job)
|
||||
@@ -461,6 +473,9 @@ void opengl46_instance_cull::end_pass()
|
||||
m_lastsleepertracks = m_sleepertracks;
|
||||
m_lastsleepers = m_sleepers;
|
||||
m_lastmodels = m_models;
|
||||
m_counts.jobs = static_cast<std::uint32_t>(m_joblist.size());
|
||||
m_counts.candidates = m_candidates;
|
||||
m_lastcounts = m_counts;
|
||||
m_inpass = false;
|
||||
m_dispatched = false;
|
||||
}
|
||||
@@ -516,6 +531,11 @@ std::string opengl46_instance_cull::info(std::string const &Label, bool const Oc
|
||||
// and so are the models drawn on their own
|
||||
if (m_lastmodels > 0)
|
||||
sleepers += "\n" + Label + ": " + std::to_string(m_lastmodels) + " models drawn on their own queued";
|
||||
// what the pass tested and sent to the gpu
|
||||
sleepers += "\n" + Label + ": " + std::to_string(m_lastcounts.candidates) + " instances tested in " + std::to_string(m_lastcounts.jobs) + " buckets";
|
||||
if (Global.gfx_gpucull_bucketrange)
|
||||
sleepers += ", " + std::to_string(m_lastcounts.skippedbuckets) + " buckets (" + std::to_string(m_lastcounts.skippedinstances) + " instances) left out beyond their range";
|
||||
sleepers += ", " + std::to_string(m_lastcounts.sentrecords) + " records sent" + (m_lastcounts.restarted ? ", record buffer started over" : "");
|
||||
if (Global.gfx_gpucull == 1)
|
||||
{
|
||||
if (m_last.frame == 0)
|
||||
|
||||
@@ -114,6 +114,9 @@ class opengl46_instance_cull
|
||||
// the caller, the culling tests each sleeper against the frustum and gives it the lod band of its plain distance from the
|
||||
// camera of the pass (Render_Sleepers())
|
||||
void add_sleepers(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area);
|
||||
// a bucket the renderer left out of the pass, as it lies wholly beyond its range (Global.gfx_gpucull_bucketrange): only counted,
|
||||
// for the panel
|
||||
void skip_bucket(scene::basic_cell::instance_bucket const &Bucket);
|
||||
// runs the culling passes for the buckets added. View: rotation of the view of the pass. Occlusion: the depth pyramid
|
||||
// to test the instances against, or null. Twophase: with the occlusion test, only the instances seen in the previous
|
||||
// frame pass for now, dispatch_second() takes the others
|
||||
@@ -283,6 +286,19 @@ class opengl46_instance_cull
|
||||
// model instances drawn on their own queued in the pass, and in the last pass, for the panel
|
||||
std::uint32_t m_models{0};
|
||||
std::uint32_t m_lastmodels{0};
|
||||
// what the pass held and sent, and the same of the last pass, for the panel: jobs (buckets queued), buckets left out beyond
|
||||
// their range and their instances, records sent to the gpu, the record buffer started over
|
||||
struct pass_counts
|
||||
{
|
||||
std::uint32_t jobs{0};
|
||||
std::uint32_t candidates{0};
|
||||
std::uint32_t skippedbuckets{0};
|
||||
std::uint64_t skippedinstances{0};
|
||||
std::uint64_t sentrecords{0};
|
||||
bool restarted{false};
|
||||
};
|
||||
pass_counts m_counts;
|
||||
pass_counts m_lastcounts;
|
||||
bool m_dispatched{false};
|
||||
|
||||
std::vector<std::uint32_t> m_cpucounts; // of the current pass, by entry
|
||||
|
||||
@@ -1103,6 +1103,7 @@ void opengl46_renderer::draw_debug_ui()
|
||||
ImGui::Checkbox("the same for the scenery of the shadow maps of lamps, when it's drawn next (gfx.gpucull.shadows.lamps)", &Global.gfx_gpucull_shadows_lamps);
|
||||
ImGui::Checkbox("the same for the sleepers of the tracks of the view (gfx.gpucull.sleepers)", &Global.gfx_gpucull_sleepers);
|
||||
ImGui::Checkbox("the same for the models drawn on their own, with lights, animations or skins of their own (gfx.gpucull.models)", &Global.gfx_gpucull_models);
|
||||
ImGui::Checkbox("leave out the models of a cell lying wholly beyond their range before the culling (gfx.gpucull.bucketrange)", &Global.gfx_gpucull_bucketrange);
|
||||
}
|
||||
ImGui::Checkbox("draw the depth of the whole scene before its colour (gfx.depthprepass.scene)", &Global.gfx_depthprepass_scene);
|
||||
if (Global.gfx_contactshadows > 0)
|
||||
@@ -3954,6 +3955,12 @@ void opengl46_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
auto const distancelimit { slack * ( reflections ? sq( static_cast<double>( Global.reflectiontune.range_instances ) ) : sq( static_cast<double>( m_renderpass.draw_range ) + 250.0 ) ) };
|
||||
// up close the rounding of the records is too large a part of the distance for the slack to cover it
|
||||
auto const distancetested { sq( 64.0 ) };
|
||||
// the nearest point of the cell, measured as the tests below and the gpu culling measure the instances: a bucket whose range
|
||||
// ends before it has none of its instances drawn, and is left out of the pass before the culling. basic_cell::enclose_area
|
||||
// keeps the locations of all instances of the cell within the radius of its area
|
||||
auto const cellnearest { std::max( 0.0, glm::length( Cell->m_area.center - cameraposition ) - static_cast<double>( Cell->m_area.radius ) ) };
|
||||
auto const cellscaledsquared { cellnearest * cellnearest * distancescale + distanceoffset };
|
||||
auto const bucketrange { Global.gfx_gpucull_bucketrange && userecords && ( cellnearest * cellnearest > distancetested ) };
|
||||
|
||||
for( auto const &bucket : Cell->m_instancebuckets_opaque ) {
|
||||
auto const &instancebucket { bucket.second };
|
||||
@@ -3961,6 +3968,12 @@ void opengl46_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
|| ( instancebucket.records.size() != instancebucket.instances.size() ) ) {
|
||||
Update_Instance_Records( bucket.first.pModel, instancebucket, Cell->m_area.center );
|
||||
}
|
||||
if( bucketrange && ( cellscaledsquared >= slack * instancebucket.rangesquared ) ) {
|
||||
if( m_instancecullpass ) {
|
||||
m_passcull->skip_bucket( instancebucket );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( m_instancecullpass ) {
|
||||
// the gpu tests every instance of the bucket, the way Render_Instanced() does. models with nothing to draw in the
|
||||
// sun shadows are left out of them, as Render_Instanced() leaves them
|
||||
@@ -4070,6 +4083,8 @@ void opengl46_renderer::Update_Instance_Records( TModel3d const *Model, scene::b
|
||||
|
||||
Bucket.records.clear();
|
||||
Bucket.records.reserve( Bucket.instances.size() );
|
||||
// the range of the bucket: past it none of its instances is drawn, by its own range or by the submodels of the model
|
||||
auto rangesquared { 0.f };
|
||||
for( auto *instance : Bucket.instances ) {
|
||||
scene::basic_cell::instance_record record;
|
||||
if( instance != nullptr ) {
|
||||
@@ -4077,6 +4092,7 @@ void opengl46_renderer::Update_Instance_Records( TModel3d const *Model, scene::b
|
||||
record.radius = instance->radius();
|
||||
record.rangesquaredmin = tofloat( instance->m_rangesquaredmin );
|
||||
record.rangesquaredmax = tofloat( instance->m_rangesquaredmax );
|
||||
rangesquared = std::max( rangesquared, record.rangesquaredmax );
|
||||
record.flags =
|
||||
( instance->visible() ? scene::basic_cell::instance_visible : 0u )
|
||||
| ( instance->m_instanceable ? scene::basic_cell::instance_instanceable : 0u );
|
||||
@@ -4088,6 +4104,7 @@ void opengl46_renderer::Update_Instance_Records( TModel3d const *Model, scene::b
|
||||
// an empty slot keeps its record without flags, and isn't drawn
|
||||
Bucket.records.emplace_back( record );
|
||||
}
|
||||
Bucket.rangesquared = std::min( rangesquared, lodrangesquared );
|
||||
Bucket.recordsversion = m_instancerecordsversion;
|
||||
Bucket.gpustale = true;
|
||||
}
|
||||
|
||||
@@ -268,6 +268,7 @@ public:
|
||||
mutable std::vector<instance_record> records; // copies for the instances, in the same order
|
||||
mutable glm::dvec3 recordorigin { 0.0 }; // the records hold locations relative to it
|
||||
mutable float lodrangesquared { 0.f }; // past it no submodel of the model of the bucket is drawn
|
||||
mutable float rangesquared { std::numeric_limits<float>::max() }; // past it no instance of the bucket is drawn: the largest range of its instances, within lodrangesquared
|
||||
mutable unsigned int recordsversion { 0 };
|
||||
// place of the records in the record buffer of the gpu culling: first record, room, and the generation of
|
||||
// the buffer it is good for. gpustale: the records changed since they were sent
|
||||
|
||||
@@ -1518,6 +1518,11 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_gpucull_models;
|
||||
}
|
||||
else if (Token == "gfx.gpucull.bucketrange")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_gpucull_bucketrange;
|
||||
}
|
||||
else if (Token == "gfx.depthprepass.scene")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
@@ -2062,6 +2067,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.gpucull.shadows.lamps", gfx_gpucull_shadows_lamps );
|
||||
export_as_text( Output, "gfx.gpucull.sleepers", gfx_gpucull_sleepers );
|
||||
export_as_text( Output, "gfx.gpucull.models", gfx_gpucull_models );
|
||||
export_as_text( Output, "gfx.gpucull.bucketrange", gfx_gpucull_bucketrange );
|
||||
export_as_text( Output, "gfx.depthprepass.scene", gfx_depthprepass_scene );
|
||||
export_as_text( Output, "gfx.depthprepass.replay", gfx_depthprepass_replay );
|
||||
export_as_text( Output, "gfx.grass", gfx_grass );
|
||||
|
||||
@@ -315,6 +315,10 @@ struct global_settings {
|
||||
// keep them out of the instanced draws) are culled with the instances as well, each one a group of its own, in every pass
|
||||
// culled on the gpu, and with 1 drawn from the result. models with billboards stay on the cpu
|
||||
bool gfx_gpucull_models = true;
|
||||
// the buckets of instances of a scene cell lying wholly beyond their range (the largest range of their instances and of
|
||||
// the submodels of their model) are left out of a pass before the culling, the gpu one included, which would leave out
|
||||
// each of their instances anyway
|
||||
bool gfx_gpucull_bucketrange = true;
|
||||
// (opengl 4.6) the depth of the whole opaque scene of the main view is drawn before its colour, the instances included
|
||||
bool gfx_depthprepass_scene = false;
|
||||
// (opengl 4.6) the depth pass of the scene (gfx_depthprepass_scene, or the one of the contact shadows) walks the scene for
|
||||
|
||||
Reference in New Issue
Block a user