mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 13:10:23 +01:00
Cull and draw the sleepers of tracks on the GPU with the instances
This commit is contained in:
@@ -76,26 +76,44 @@ void opengl46_instance_cull::begin_pass(pass_setup const &Setup)
|
||||
m_drawgroups.clear();
|
||||
m_lodlist.clear();
|
||||
m_groupmap.clear();
|
||||
m_sleepergroupmap.clear();
|
||||
m_cpucounts.clear();
|
||||
m_candidates = 0;
|
||||
m_entries = 0;
|
||||
m_sleepertracks = 0;
|
||||
m_sleepers = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
add(Key, Bucket, Area, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::add(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area, bool const Sleepers)
|
||||
{
|
||||
if (!m_inpass || Bucket.records.empty() || Key.pModel == nullptr)
|
||||
return;
|
||||
|
||||
// range of the squared distances the instances of the bucket can be at, scaled as in the culling, with some slack for
|
||||
// its single precision: the draws are made only for the lod bands within it
|
||||
auto const centerdistance{glm::length(Area.center - m_setup.distancecamera)};
|
||||
// its single precision: the draws are made only for the lod bands within it. sleepers go by the plain distance from the
|
||||
// camera of the pass
|
||||
auto const &distancecamera{Sleepers ? m_setup.camera : m_setup.distancecamera};
|
||||
auto const distancescale{Sleepers ? 1.0 : static_cast<double>(m_setup.distancescale)};
|
||||
auto const distanceoffset{Sleepers ? 0.0 : static_cast<double>(m_setup.distanceoffset)};
|
||||
auto const centerdistance{glm::length(Area.center - distancecamera)};
|
||||
auto const nearest{std::max(0.0, centerdistance - Area.radius)};
|
||||
auto const farthest{centerdistance + Area.radius};
|
||||
auto const distancemin{static_cast<float>(0.999 * (nearest * nearest * m_setup.distancescale + m_setup.distanceoffset))};
|
||||
auto const distancemax{static_cast<float>(1.001 * (farthest * farthest * m_setup.distancescale + m_setup.distanceoffset))};
|
||||
auto const distancemin{static_cast<float>(0.999 * (nearest * nearest * distancescale + distanceoffset))};
|
||||
auto const distancemax{static_cast<float>(1.001 * (farthest * farthest * distancescale + distanceoffset))};
|
||||
|
||||
auto lookup{m_groupmap.find(Key)};
|
||||
if (lookup == m_groupmap.end())
|
||||
auto &groupmap{Sleepers ? m_sleepergroupmap : m_groupmap};
|
||||
auto lookup{groupmap.find(Key)};
|
||||
if (lookup == groupmap.end())
|
||||
{
|
||||
// lod bounds of the model, the same set Render_Instanced() splits the distance into bands with
|
||||
auto const lodfirst{static_cast<std::uint32_t>(m_lodlist.size())};
|
||||
@@ -121,7 +139,7 @@ void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key c
|
||||
group_data const group{static_cast<std::uint32_t>(m_grouplist.size()), m_entries, lodcount + 1, distancemin, distancemax};
|
||||
m_grouplist.push_back({lodfirst, lodcount, m_entries, 0});
|
||||
m_entries += group.entrycount;
|
||||
lookup = m_groupmap.emplace(Key, group).first;
|
||||
lookup = groupmap.emplace(Key, group).first;
|
||||
if (m_setup.drawing)
|
||||
{
|
||||
// any instance of the group serves; it's only prepared and asked for its material
|
||||
@@ -134,7 +152,7 @@ void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key c
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_drawgroups.push_back({&lookup->first, instance, lodfirst, lodcount, group.entryfirst, distancemin, distancemax});
|
||||
m_drawgroups.push_back({&lookup->first, instance, Sleepers, lodfirst, lodcount, group.entryfirst, distancemin, distancemax});
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -169,9 +187,15 @@ void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key c
|
||||
job.count = static_cast<std::uint32_t>(Bucket.records.size());
|
||||
job.group = lookup->second.index;
|
||||
job.candidatestart = m_candidates;
|
||||
job.flags = (Sleepers ? gl::INSTANCE_JOB_SLEEPERS : 0u);
|
||||
m_joblist.push_back(job);
|
||||
m_buckets.push_back(&Bucket);
|
||||
m_candidates += job.count;
|
||||
if (Sleepers)
|
||||
{
|
||||
++m_sleepertracks;
|
||||
m_sleepers += job.count;
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::place(scene::basic_cell::instance_bucket const &Bucket)
|
||||
@@ -384,12 +408,13 @@ void opengl46_instance_cull::draw_result(std::uint32_t const Draws, std::uint32_
|
||||
m_cputime = Cputime;
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::cpu_result(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts)
|
||||
void opengl46_instance_cull::cpu_result(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts, bool const Sleepers)
|
||||
{
|
||||
if (!m_dispatched)
|
||||
return;
|
||||
auto const lookup{m_groupmap.find(Key)};
|
||||
if (lookup == m_groupmap.end())
|
||||
auto const &groupmap{Sleepers ? m_sleepergroupmap : m_groupmap};
|
||||
auto const lookup{groupmap.find(Key)};
|
||||
if (lookup == groupmap.end())
|
||||
return;
|
||||
auto const &group{lookup->second};
|
||||
for (std::size_t band = 0; band < std::min<std::size_t>(Bandcounts.size(), group.entrycount); ++band)
|
||||
@@ -412,6 +437,8 @@ void opengl46_instance_cull::end_pass()
|
||||
while (m_cpupasses.size() > 8)
|
||||
m_cpupasses.pop_front();
|
||||
}
|
||||
m_lastsleepertracks = m_sleepertracks;
|
||||
m_lastsleepers = m_sleepers;
|
||||
m_inpass = false;
|
||||
m_dispatched = false;
|
||||
}
|
||||
@@ -462,24 +489,26 @@ std::string opengl46_instance_cull::info(std::string const &Label, bool const Oc
|
||||
{
|
||||
if (Global.gfx_gpucull == 0)
|
||||
return {};
|
||||
// the sleepers are among the instances counted
|
||||
auto const sleepers{m_lastsleepertracks > 0 ? "\n" + Label + ": " + std::to_string(m_lastsleepers) + " sleepers of " + std::to_string(m_lastsleepertracks) + " tracks queued" : std::string{}};
|
||||
if (Global.gfx_gpucull == 1)
|
||||
{
|
||||
if (m_last.frame == 0)
|
||||
return "\n" + Label + ": no result yet";
|
||||
return "\n" + Label + ": no result yet" + sleepers;
|
||||
if (false == Occlusion)
|
||||
return "\n" + Label + ": " + std::to_string(m_last.gputotal) + " instances drawn, " + std::to_string(m_last.entries) + " bands; " + std::to_string(m_draws) +
|
||||
" indirect draws in " + std::to_string(m_calls) + " calls, cpu " + to_string(m_cputime, 3) + " ms";
|
||||
" indirect draws in " + std::to_string(m_calls) + " calls, cpu " + to_string(m_cputime, 3) + " ms" + sleepers;
|
||||
return "\n" + Label + ": " + std::to_string(m_last.gputotal) + " instances drawn" +
|
||||
(m_twophase ? " (" + std::to_string(m_last.first) + " of them first, seen in the last frame)" : std::string{}) + ", " + std::to_string(m_last.occluded) +
|
||||
" hidden by occlusion" + (m_occlusion ? (m_twophase ? " (also by one another)" : "") : " (test off)") + ", " + std::to_string(m_last.entries) + " bands; " +
|
||||
std::to_string(m_draws) + " indirect draws in " + std::to_string(m_calls) + " calls (+" + std::to_string(m_prepassdraws) + " depth prepass in " +
|
||||
std::to_string(m_prepasscalls) + "), cpu " + to_string(m_cputime, 3) + " ms";
|
||||
std::to_string(m_prepasscalls) + "), cpu " + to_string(m_cputime, 3) + " ms" + sleepers;
|
||||
}
|
||||
if (m_comparedframes == 0)
|
||||
return "\n" + Label + ": no result yet";
|
||||
return "\n" + Label + ": no result yet" + sleepers;
|
||||
return "\n" + Label + ": " + std::to_string(m_last.gputotal) + " instances (cpu " + std::to_string(m_last.cputotal) + "), " + std::to_string(m_last.entries) + " bands, " +
|
||||
std::to_string(m_last.mismatched) + " differ (by up to " + std::to_string(m_last.largestdifference) + "); frames differing " + std::to_string(m_mismatchedframes) + " of " +
|
||||
std::to_string(m_comparedframes);
|
||||
std::to_string(m_comparedframes) + sleepers;
|
||||
}
|
||||
|
||||
std::string opengl46_instance_cull::info_times(std::string const &Label)
|
||||
|
||||
@@ -46,6 +46,8 @@ class TAnimModel;
|
||||
// each render pass culled on the gpu (the main view, the cascades of the sun shadows, the faces of the environment map) has a
|
||||
// culling of its own, with its own buffers, while the records and the programs are shared by all of them: the records of a
|
||||
// bucket go to the gpu once and serve every pass which queues the bucket
|
||||
// the sleepers of tracks (TTrack::m_sleeper_local_transforms) are culled the same way, each track with them a bucket of its
|
||||
// own, and drawn together with the instances; their groups are kept apart from those of the model instances
|
||||
class opengl46_instance_cull
|
||||
{
|
||||
public:
|
||||
@@ -76,7 +78,8 @@ class opengl46_instance_cull
|
||||
struct draw_group
|
||||
{
|
||||
scene::basic_cell::instance_bucket_key const *key;
|
||||
TAnimModel *instance; // one of the group, prepares the model and gives the material
|
||||
TAnimModel *instance; // one of the group, prepares the model and gives the material. none for sleepers
|
||||
bool sleepers; // the skins come from the key, as Render_Sleepers() sets them
|
||||
std::uint32_t lodfirst; // in lod_bounds()
|
||||
std::uint32_t lodcount;
|
||||
std::uint32_t entryfirst;
|
||||
@@ -103,6 +106,11 @@ class opengl46_instance_cull
|
||||
// adds the records of a bucket (made already) to the pass, sending them to the gpu when they changed.
|
||||
// Area: bounds of the cell holding the bucket, which hold the locations of all its instances
|
||||
void add_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area);
|
||||
// adds the sleepers of a track, their records made already as a bucket with no instances (only the records and the place
|
||||
// in the record buffer count). Area: bounds of the locations of the sleepers. the range of the whole track is tested by
|
||||
// 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);
|
||||
// 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
|
||||
@@ -164,8 +172,9 @@ class opengl46_instance_cull
|
||||
{
|
||||
return static_cast<std::uint32_t>(m_last.gputotal);
|
||||
}
|
||||
// counts per lod band of the instances Render_Instanced() drew for a model and its skins in this pass
|
||||
void cpu_result(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts);
|
||||
// counts per lod band of the instances Render_Instanced() drew for a model and its skins in this pass, or of the sleepers
|
||||
// Render_Sleepers() drew. needs dispatch() before it
|
||||
void cpu_result(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts, bool const Sleepers = false);
|
||||
// ends the pass: its cpu counts are kept for the comparison
|
||||
void end_pass();
|
||||
// takes the newest counts of the gpu which arrived and compares them with the ones of the cpu. done when a pass begins;
|
||||
@@ -224,6 +233,10 @@ class opengl46_instance_cull
|
||||
bool restarted{false};
|
||||
};
|
||||
|
||||
using group_map = std::unordered_map<scene::basic_cell::instance_bucket_key, group_data, scene::basic_cell::instance_bucket_key_hash>;
|
||||
|
||||
// add_bucket() and add_sleepers()
|
||||
void add(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area, bool const Sleepers);
|
||||
// finds a place for the records of a bucket in the record buffer and sends them
|
||||
void place(scene::basic_cell::instance_bucket const &Bucket);
|
||||
// binds the buffers of the culling passes
|
||||
@@ -251,10 +264,16 @@ class opengl46_instance_cull
|
||||
std::vector<gl::instance_cull_group> m_grouplist;
|
||||
std::vector<draw_group> m_drawgroups;
|
||||
std::vector<float> m_lodlist;
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, group_data, scene::basic_cell::instance_bucket_key_hash> m_groupmap;
|
||||
group_map m_groupmap;
|
||||
group_map m_sleepergroupmap; // groups of the sleepers
|
||||
std::vector<TSubModel const *> m_pending; // walk of the submodel tree
|
||||
std::uint32_t m_candidates{0};
|
||||
std::uint32_t m_entries{0};
|
||||
// tracks with sleepers and their sleepers queued in the pass, and in the last pass, for the panel
|
||||
std::uint32_t m_sleepertracks{0};
|
||||
std::uint32_t m_sleepers{0};
|
||||
std::uint32_t m_lastsleepertracks{0};
|
||||
std::uint32_t m_lastsleepers{0};
|
||||
bool m_dispatched{false};
|
||||
|
||||
std::vector<std::uint32_t> m_cpucounts; // of the current pass, by entry
|
||||
|
||||
@@ -679,6 +679,8 @@ bool opengl46_renderer::Render()
|
||||
m_locallights.reset();
|
||||
m_localshadows.reset_lamps();
|
||||
m_localshadows.reset_headlights();
|
||||
// and so may the tracks the sleeper records were made for
|
||||
m_sleeperrecords.clear();
|
||||
}
|
||||
// generate new frame
|
||||
opengl_texture::reset_unit_cache();
|
||||
@@ -690,6 +692,14 @@ bool opengl46_renderer::Render()
|
||||
++m_instancerecordsversion;
|
||||
m_nodestateversion = scene::node_state_version;
|
||||
}
|
||||
if( ( m_framestamp % 256 ) == 0 ) {
|
||||
// the sleeper records of tracks out of sight for a while. their places in the record buffer of the gpu culling are
|
||||
// dropped when it starts over
|
||||
for( auto records { m_sleeperrecords.begin() }; records != m_sleeperrecords.end(); ) {
|
||||
if( m_framestamp - records->second.frame > 256 ) { records = m_sleeperrecords.erase( records ); }
|
||||
else { ++records; }
|
||||
}
|
||||
}
|
||||
// scenery lamps which get shadows in this frame. their maps hold the scenery as of the version of the instance records
|
||||
if (simulation::is_ready && (false == Global.gfx_skiprendering))
|
||||
m_localshadows.update_lamps(m_locallights.lights(), Global.pCamera.Pos, m_instancerecordsversion, m_framestamp);
|
||||
@@ -987,6 +997,7 @@ void opengl46_renderer::draw_debug_ui()
|
||||
{
|
||||
ImGui::Checkbox("the same for the cascades of the sun shadows (gfx.gpucull.shadows)", &Global.gfx_gpucull_shadows);
|
||||
ImGui::Checkbox("the same for the environment map, when it's drawn next (gfx.gpucull.reflections)", &Global.gfx_gpucull_reflections);
|
||||
ImGui::Checkbox("the same for the sleepers of the tracks of the view (gfx.gpucull.sleepers)", &Global.gfx_gpucull_sleepers);
|
||||
}
|
||||
ImGui::Checkbox("draw the depth of the whole scene before its colour (gfx.depthprepass.scene)", &Global.gfx_depthprepass_scene);
|
||||
if (Global.gfx_contactshadows > 0)
|
||||
@@ -2902,6 +2913,13 @@ void opengl46_renderer::Render(scene::basic_region *Region)
|
||||
setup.drawing = ( Global.gfx_gpucull == 1 );
|
||||
m_passcull->begin_pass( setup );
|
||||
}
|
||||
// the sleepers of tracks go with the instances of the main view
|
||||
m_sleeperspass = ( ( m_passcull == &m_instancecull ) && Global.gfx_gpucull_sleepers );
|
||||
if( m_sleeperspass && ( false == m_passcull->drawing() ) ) {
|
||||
for( auto &counts : m_sleeperbandcounts ) {
|
||||
counts.second.clear();
|
||||
}
|
||||
}
|
||||
// build a list of region sections to render
|
||||
glm::vec3 const cameraposition{m_renderpass.pass_camera.position()};
|
||||
auto const camerax = static_cast<int>(std::floor(cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2));
|
||||
@@ -3379,6 +3397,14 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
else {
|
||||
// the view of the pass is in place here, the one Render_Instanced() takes
|
||||
m_passcull->dispatch( OpenGLMatrices.data( GL_MODELVIEW ) );
|
||||
// the sleepers the cpu drew along the tracks, before the culling ran
|
||||
if( m_sleeperspass ) {
|
||||
for( auto const &counts : m_sleeperbandcounts ) {
|
||||
if( false == counts.second.empty() ) {
|
||||
m_passcull->cpu_result( counts.first, counts.second, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for( auto &bucket : m_frame_instance_buckets ) {
|
||||
@@ -3394,6 +3420,7 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
m_instancecullpass = false;
|
||||
m_passcull = nullptr;
|
||||
}
|
||||
m_sleeperspass = false;
|
||||
}
|
||||
|
||||
void opengl46_renderer::Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end)
|
||||
@@ -4045,6 +4072,7 @@ void opengl46_renderer::Render_Instances_Indirect()
|
||||
// the colour pass takes the draws from here, and counts them
|
||||
m_indirectcolourpending = true;
|
||||
m_indirectcolourset = colourset;
|
||||
m_indirectcoloursleepers = m_sleeperspass;
|
||||
m_renderpass.draw_stats.drawcalls += static_cast<int>( prepasscounts.calls );
|
||||
colourcounts.draws = static_cast<std::uint32_t>( m_indirectdraws.size() );
|
||||
}
|
||||
@@ -4100,10 +4128,25 @@ void opengl46_renderer::Record_Indirect_Draws( opengl46_instance_cull &Cull )
|
||||
m_indirectrecording = true;
|
||||
for( auto const &group : Cull.draw_groups() ) {
|
||||
auto *model { group.key->pModel };
|
||||
if( ( model == nullptr ) || ( model->Root == nullptr ) || ( group.instance == nullptr ) ) { continue; }
|
||||
group.instance->RaPrepare();
|
||||
auto const *material { group.instance->Material() };
|
||||
auto alpha { material != nullptr ? material->textures_alpha : 0x30300030 };
|
||||
if( ( model == nullptr ) || ( model->Root == nullptr ) || ( ( group.instance == nullptr ) && ( false == group.sleepers ) ) ) { continue; }
|
||||
material_handle const *skins { nullptr };
|
||||
auto alpha { 0x30300030 };
|
||||
if( group.sleepers ) {
|
||||
// as Render_Sleepers() sets them up: the skin of the track, if any. like scenery models they pick no texture
|
||||
// variants of their own
|
||||
if( group.key->skins[ 1 ] != null_handle ) {
|
||||
skins = group.key->skins.data();
|
||||
}
|
||||
TSubModel::iVariantSeed = 0;
|
||||
}
|
||||
else {
|
||||
group.instance->RaPrepare();
|
||||
auto const *material { group.instance->Material() };
|
||||
if( material != nullptr ) {
|
||||
skins = material->replacable_skins;
|
||||
alpha = material->textures_alpha;
|
||||
}
|
||||
}
|
||||
alpha ^= 0x0F0F000F;
|
||||
for( std::uint32_t band = 0; band <= group.lodcount; ++band ) {
|
||||
// a band holds the squared distances from its lower bound up to the next one; any of them selects the same lod
|
||||
@@ -4114,7 +4157,7 @@ void opengl46_renderer::Record_Indirect_Draws( opengl46_instance_cull &Cull )
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
model->Root->fSquareDist = bandmin;
|
||||
model->Root->ReplacableSet( ( material != nullptr ? material->replacable_skins : nullptr ), alpha );
|
||||
model->Root->ReplacableSet( skins, alpha );
|
||||
model->Root->pRoot = model;
|
||||
Render( model->Root );
|
||||
::glPopMatrix();
|
||||
@@ -4764,6 +4807,71 @@ std::vector<texture_handle> const &opengl46_renderer::Model_Textures(TModel3d *M
|
||||
return textures;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// the group of the sleepers of a track in the gpu culling: their model, and the skin in the slot Render_Sleepers() sets it in
|
||||
scene::basic_cell::instance_bucket_key sleeper_key( TTrack const &Track ) {
|
||||
scene::basic_cell::instance_bucket_key key;
|
||||
key.pModel = Track.m_sleeper_model;
|
||||
key.skins[ 1 ] = Track.m_sleeper_skin;
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void opengl46_renderer::Queue_Sleepers( TTrack *Track )
|
||||
{
|
||||
// the gates of Render_Sleepers(); the range is tested for the whole track
|
||||
if( ( Track->m_sleeper_model == nullptr ) || Track->m_sleeper_local_transforms.empty() || ( Global.SleeperDistance <= 0.f ) ) { return; }
|
||||
auto const distsq { glm::length2( Track->location() - m_renderpass.pass_camera.position() ) };
|
||||
auto const cutoffsq { static_cast<double>( Global.SleeperDistance ) * static_cast<double>( Global.SleeperDistance ) };
|
||||
if( distsq > cutoffsq ) { return; }
|
||||
|
||||
auto &records { m_sleeperrecords[ Track ] };
|
||||
auto const &transforms { Track->m_sleeper_local_transforms };
|
||||
if( ( records.bucket.recordsversion != m_instancerecordsversion )
|
||||
|| ( records.model != Track->m_sleeper_model )
|
||||
|| ( records.skin != Track->m_sleeper_skin )
|
||||
|| ( records.transforms != transforms.data() )
|
||||
|| ( records.count != transforms.size() ) ) {
|
||||
// each sleeper is a record placed and turned by its transform, relative to the origin of the track (the centre of its
|
||||
// cell), with the sphere Render_Sleepers() tests against the frustum
|
||||
auto const radius { std::max( Track->m_sleeper_model->bounding_radius(), 2.0f ) };
|
||||
auto &bucket { records.bucket };
|
||||
bucket.records.clear();
|
||||
bucket.records.reserve( transforms.size() );
|
||||
glm::vec3 low { std::numeric_limits<float>::max() };
|
||||
glm::vec3 high { std::numeric_limits<float>::lowest() };
|
||||
for( auto const &local : transforms ) {
|
||||
scene::basic_cell::instance_record record;
|
||||
glm::vec3 const position { local[ 3 ] };
|
||||
record.position = packed_vec3{ position };
|
||||
record.radius = radius;
|
||||
record.rangesquaredmin = 0.f;
|
||||
record.rangesquaredmax = std::numeric_limits<float>::max();
|
||||
record.flags = scene::basic_cell::instance_visible | scene::basic_cell::instance_instanceable;
|
||||
for( int column = 0; column < 3; ++column ) {
|
||||
record.rotationscale[ column ] = local[ column ];
|
||||
}
|
||||
bucket.records.emplace_back( record );
|
||||
low = glm::min( low, position );
|
||||
high = glm::max( high, position );
|
||||
}
|
||||
bucket.recordorigin = Track->m_origin;
|
||||
bucket.lodrangesquared = std::numeric_limits<float>::max();
|
||||
bucket.recordsversion = m_instancerecordsversion;
|
||||
bucket.gpustale = true;
|
||||
records.key = sleeper_key( *Track );
|
||||
records.area = scene::bounding_area{ Track->m_origin + glm::dvec3{ ( low + high ) * 0.5f }, 0.5f * glm::length( high - low ) };
|
||||
records.model = Track->m_sleeper_model;
|
||||
records.skin = Track->m_sleeper_skin;
|
||||
records.transforms = transforms.data();
|
||||
records.count = transforms.size();
|
||||
}
|
||||
records.frame = m_framestamp;
|
||||
m_passcull->add_sleepers( records.key, records.bucket, records.area );
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_Sleepers( TTrack *Track )
|
||||
{
|
||||
if( Track == nullptr ) { return; }
|
||||
@@ -4931,6 +5039,25 @@ void opengl46_renderer::Render_Sleepers( TTrack *Track )
|
||||
|
||||
m_renderpass.draw_stats.instances += static_cast<int>( total );
|
||||
m_renderpass.draw_stats.models += static_cast<int>( total );
|
||||
|
||||
if( m_sleeperspass && m_instancecullpass && ( false == m_passcull->drawing() ) ) {
|
||||
// the comparison with the gpu culling: the sleepers drawn by lod band, the bands as the culling makes them, split by
|
||||
// the bounds of the model past 0 and short of the largest float
|
||||
auto const counted = []( float const Bound ) {
|
||||
return ( Bound > 0.f ) && ( Bound < std::numeric_limits<float>::max() ); };
|
||||
auto &counts { m_sleeperbandcounts[ sleeper_key( *Track ) ] };
|
||||
auto const bands { static_cast<std::size_t>( std::count_if( lodbounds.begin(), lodbounds.end(), counted ) ) + 1 };
|
||||
if( counts.size() < bands ) {
|
||||
counts.resize( bands, 0 );
|
||||
}
|
||||
for( auto const &survivor : survivors ) {
|
||||
std::size_t band { 0 };
|
||||
for( auto const bound : lodbounds ) {
|
||||
if( counted( bound ) && ( bound <= survivor.first ) ) { ++band; }
|
||||
}
|
||||
++counts[ band ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool opengl46_renderer::Render(TDynamicObject *Dynamic)
|
||||
@@ -5699,6 +5826,15 @@ void opengl46_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
auto *track = *first;
|
||||
if( false == track->m_visible ) { continue; }
|
||||
if( false == track->m_sleeper_enabled ) { continue; }
|
||||
if( m_sleeperspass ) {
|
||||
// culled with the instances of the pass and drawn from the result, or drawn by the cpu as well to compare
|
||||
Queue_Sleepers( track );
|
||||
if( m_passcull->drawing() ) { continue; }
|
||||
}
|
||||
else if( m_indirectcolourpending && m_indirectcoloursleepers ) {
|
||||
// the depth pass of the whole scene culled and recorded them, the colour pass draws what it recorded
|
||||
continue;
|
||||
}
|
||||
Render_Sleepers( track );
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -425,6 +425,9 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// no-op if the track has no sleepermodel, Global.SleeperDistance is 0, or the camera is beyond
|
||||
// Global.SleeperDistance from the track origin.
|
||||
void Render_Sleepers( TTrack *Track );
|
||||
// queues the sleepers of a track in the gpu culling of the pass (Global.gfx_gpucull_sleepers), with the gates of
|
||||
// Render_Sleepers(), making their records first when they're missing or out of date
|
||||
void Queue_Sleepers( TTrack *Track );
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
@@ -581,6 +584,25 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// Instances_Cast_Shadow() of the groups of the shadow pass under way
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, bool, scene::basic_cell::instance_bucket_key_hash> m_shadowcasters;
|
||||
std::vector<std::uint32_t> m_instance_bandcounts; // reported by Render_Instanced() for the comparison
|
||||
// the sleepers of tracks culled on the gpu with the instances of the main view (Global.gfx_gpucull_sleepers): records made
|
||||
// from TTrack::m_sleeper_local_transforms when the track is queued first, and again when they or the instance records
|
||||
// (editor, m_instancerecordsversion) went out of date. the records of tracks not queued for a while are let go
|
||||
struct sleeper_records {
|
||||
scene::basic_cell::instance_bucket bucket; // the records only, no instances
|
||||
scene::basic_cell::instance_bucket_key key; // the model, and the skin in the slot Render_Sleepers() sets it in
|
||||
scene::bounding_area area; // of the locations of the sleepers
|
||||
TModel3d const *model { nullptr }; // what the records were made from
|
||||
material_handle skin { null_handle };
|
||||
glm::mat4 const *transforms { nullptr };
|
||||
std::size_t count { 0 };
|
||||
unsigned int frame { 0 }; // queued last
|
||||
};
|
||||
std::unordered_map<TTrack const *, sleeper_records> m_sleeperrecords;
|
||||
bool m_sleeperspass { false }; // the sleepers of the pass under way go to its gpu culling (the one of the main view)
|
||||
bool m_indirectcoloursleepers { false }; // with m_indirectcolourpending: the depth pass culled and recorded the sleepers too
|
||||
// counts per lod band of the sleepers Render_Sleepers() drew in the pass, by model and skin, for the comparison (they're
|
||||
// drawn before the culling runs)
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<std::uint32_t>, scene::basic_cell::instance_bucket_key_hash> m_sleeperbandcounts;
|
||||
// depth pyramid for the occlusion test of the gpu culling
|
||||
opengl46_hiz m_hiz;
|
||||
// a draw recorded from the walk of the submodel tree of a group in one lod band (Render_Instances_Indirect()), to be
|
||||
|
||||
Reference in New Issue
Block a user