mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 03:59:19 +02:00
perf(tracks): geometry of neighbouring tracks of a cell which share the material drawn together, with one material bind and one upload of model data; chunks of the same geometry bank sent to the gpu in a single multi draw call
This commit is contained in:
@@ -332,6 +332,13 @@ geometry_bank::draw_instanced( gfx::geometry_handle const &Geometry, gfx::stream
|
||||
return draw_instanced_( Geometry, Units, InstanceCount, Streams );
|
||||
}
|
||||
|
||||
// draws geometry stored in supplied sequence of chunks of the bank
|
||||
std::size_t
|
||||
geometry_bank::draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) {
|
||||
// template method implementation
|
||||
return draw_batch_( First, Last, Units, Streams );
|
||||
}
|
||||
|
||||
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
|
||||
void
|
||||
geometry_bank::release() {
|
||||
@@ -436,6 +443,32 @@ geometrybank_manager::draw_instanced( gfx::geometry_handle const &Geometry, std:
|
||||
m_primitivecount += bankrecord.first->draw_instanced( Geometry, m_units, InstanceCount, Streams );
|
||||
}
|
||||
|
||||
// draws geometry stored in supplied sequence of chunks
|
||||
void
|
||||
geometrybank_manager::draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, unsigned int const Streams ) {
|
||||
|
||||
while( First != Last ) {
|
||||
|
||||
if( *First == null_handle ) {
|
||||
++First;
|
||||
continue;
|
||||
}
|
||||
// neighbouring chunks stored in the same bank go to the bank together
|
||||
auto const *last { First + 1 };
|
||||
while( ( last != Last )
|
||||
&& ( last->bank == First->bank ) ) {
|
||||
++last;
|
||||
}
|
||||
|
||||
auto &bankrecord = bank( *First );
|
||||
|
||||
bankrecord.second = m_garbagecollector.timestamp();
|
||||
m_primitivecount += bankrecord.first->draw_batch( First, last, m_units, Streams );
|
||||
|
||||
First = last;
|
||||
}
|
||||
}
|
||||
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
geometrybank_manager::indices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
@@ -121,6 +121,8 @@ public:
|
||||
auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in specified chunk N times via glDrawElementsInstanced*
|
||||
auto draw_instanced( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, std::size_t const InstanceCount, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in supplied sequence of chunks of the bank, in their order. the bank is free to combine them into fewer draw calls
|
||||
auto draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in supplied list of chunks
|
||||
template <typename Iterator_>
|
||||
auto draw( Iterator_ First, Iterator_ Last, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) ->std::size_t {
|
||||
@@ -187,6 +189,11 @@ private:
|
||||
std::size_t count { 0 };
|
||||
for( std::size_t i = 0; i < InstanceCount; ++i ) { count += draw_( Geometry, Units, Streams ); }
|
||||
return count; }
|
||||
// draw_batch() subclass details. Default implementation falls back to regular draw of each chunk.
|
||||
virtual auto draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t {
|
||||
std::size_t count { 0 };
|
||||
for( ; First != Last; ++First ) { count += draw_( *First, Units, Streams ); }
|
||||
return count; }
|
||||
// resource release subclass details
|
||||
virtual void release_() = 0;
|
||||
};
|
||||
@@ -218,6 +225,8 @@ public:
|
||||
// draws geometry stored in specified chunk InstanceCount times via GPU instancing.
|
||||
// The shader reads per-instance modelview matrices from instance_ubo[gl_InstanceID].
|
||||
void draw_instanced( gfx::geometry_handle const &Geometry, std::size_t const InstanceCount, unsigned int const Streams = basic_streams );
|
||||
// draws geometry stored in supplied sequence of chunks, in their order. neighbouring chunks of the same bank are passed to it together
|
||||
void draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, unsigned int const Streams = basic_streams );
|
||||
template <typename Iterator_>
|
||||
void draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) {
|
||||
while( First != Last ) {
|
||||
|
||||
@@ -259,6 +259,66 @@ opengl33_vaogeometrybank::draw_instanced_( gfx::geometry_handle const &Geometry,
|
||||
return prims * InstanceCount;
|
||||
}
|
||||
|
||||
// draw_batch() subclass details — neighbouring non-indexed chunks of the same type are sent with a single GL call.
|
||||
// The chunks are drawn in the supplied order, same as with a draw_() call for each of them.
|
||||
std::size_t
|
||||
opengl33_vaogeometrybank::draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams )
|
||||
{
|
||||
std::size_t count { 0 };
|
||||
|
||||
if( !glMultiDrawArrays ) {
|
||||
// GLES context doesn't provide the call
|
||||
for( ; First != Last; ++First ) { count += draw_( *First, Units, Streams ); }
|
||||
return count;
|
||||
}
|
||||
|
||||
setup_buffer();
|
||||
|
||||
std::array<GLint, 64> offsets;
|
||||
std::array<GLsizei, 64> sizes;
|
||||
GLsizei pending { 0 };
|
||||
unsigned int pendingtype { 0 };
|
||||
|
||||
auto const flush = [ & ]() {
|
||||
if( pending == 0 ) { return; }
|
||||
m_vao->bind();
|
||||
if( pending == 1 ) { ::glDrawArrays( pendingtype, offsets[ 0 ], sizes[ 0 ] ); }
|
||||
else { ::glMultiDrawArrays( pendingtype, offsets.data(), sizes.data(), pending ); }
|
||||
pending = 0;
|
||||
};
|
||||
|
||||
for( ; First != Last; ++First ) {
|
||||
|
||||
auto const &chunkrecord = m_chunkrecords.at( First->chunk - 1 );
|
||||
if( ( false == chunkrecord.is_good )
|
||||
|| ( chunkrecord.index_count > 0 ) ) {
|
||||
// chunks which wait for upload of their data and indexed chunks take the regular route
|
||||
flush();
|
||||
count += draw_( *First, Units, Streams );
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const type { gfx::geometry_bank::chunk( *First ).type };
|
||||
if( ( pending == static_cast<GLsizei>( offsets.size() ) )
|
||||
|| ( ( pending > 0 ) && ( type != pendingtype ) ) ) {
|
||||
flush();
|
||||
}
|
||||
pendingtype = type;
|
||||
offsets[ pending ] = static_cast<GLint>( chunkrecord.vertex_offset );
|
||||
sizes[ pending ] = static_cast<GLsizei>( chunkrecord.vertex_count );
|
||||
++pending;
|
||||
|
||||
switch( type ) {
|
||||
case GL_TRIANGLES: { count += chunkrecord.vertex_count / 3; break; }
|
||||
case GL_TRIANGLE_STRIP: { count += ( chunkrecord.vertex_count > 2 ? chunkrecord.vertex_count - 2 : 0 ); break; }
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
flush();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// release () subclass details
|
||||
void
|
||||
opengl33_vaogeometrybank::release_() {
|
||||
|
||||
@@ -56,6 +56,9 @@ private:
|
||||
// draw_instanced() subclass details — issues glDrawElementsInstancedBaseVertex
|
||||
auto
|
||||
draw_instanced_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, std::size_t const InstanceCount, unsigned int const Streams ) -> std::size_t override;
|
||||
// draw_batch() subclass details — issues glMultiDrawArrays for runs of non-indexed chunks of the same type
|
||||
auto
|
||||
draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override;
|
||||
// release() subclass details
|
||||
void
|
||||
release_() override;
|
||||
|
||||
@@ -2854,7 +2854,13 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
|
||||
void opengl33_renderer::Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end)
|
||||
{
|
||||
m_geometry.draw(begin, end);
|
||||
if (begin == end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the handles are handed over as a whole, so chunks kept in the same bank can be drawn with fewer calls
|
||||
auto const *first{&(*begin)};
|
||||
m_geometry.draw_batch(first, first + std::distance(begin, end));
|
||||
}
|
||||
|
||||
void opengl33_renderer::Draw_Geometry(const gfx::geometrybank_handle &handle)
|
||||
@@ -4066,6 +4072,31 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
}
|
||||
}
|
||||
|
||||
// geometry of neighbouring tracks which share the material is gathered and drawn together.
|
||||
// only neighbours are combined, the tracks are still drawn in their order in the cell
|
||||
material_handle batchmaterial{null_handle};
|
||||
auto const flush_batch = [&]()
|
||||
{
|
||||
if (false == m_pathbatch.empty())
|
||||
{
|
||||
draw(std::begin(m_pathbatch), std::end(m_pathbatch));
|
||||
m_pathbatch.clear();
|
||||
}
|
||||
batchmaterial = null_handle;
|
||||
};
|
||||
// returns: true if the material starts a new batch, false if it continues the current one
|
||||
auto const begin_batch = [&](material_handle const Material)
|
||||
{
|
||||
if (Material == batchmaterial)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flush_batch();
|
||||
Bind_Material(Material);
|
||||
batchmaterial = Material;
|
||||
return true;
|
||||
};
|
||||
|
||||
// TODO: render auto generated trackbeds together with regular trackbeds in pass 1, and all rails in pass 2
|
||||
// first pass, material 1
|
||||
for (auto first{First}; first != Last; ++first)
|
||||
@@ -4092,15 +4123,21 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
// track with light level of its own is drawn on its own
|
||||
flush_batch();
|
||||
setup_environment_light(track->eEnvironment);
|
||||
}
|
||||
Bind_Material(track->m_material1);
|
||||
draw(std::begin(track->Geometry1), std::end(track->Geometry1));
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
Bind_Material(track->m_material1);
|
||||
draw(std::begin(track->Geometry1), std::end(track->Geometry1));
|
||||
// restore default lighting
|
||||
setup_environment_light();
|
||||
break;
|
||||
}
|
||||
if (false == begin_batch(track->m_material1))
|
||||
{
|
||||
// the track is drawn with the call of its predecessor
|
||||
--m_renderpass.draw_stats.drawcalls;
|
||||
}
|
||||
m_pathbatch.insert(std::end(m_pathbatch), std::begin(track->Geometry1), std::end(track->Geometry1));
|
||||
break;
|
||||
}
|
||||
case rendermode::shadows:
|
||||
@@ -4130,6 +4167,7 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
flush_batch();
|
||||
// second pass, material 2
|
||||
for (auto first{First}; first != Last; ++first)
|
||||
{
|
||||
@@ -4151,15 +4189,16 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
setup_environment_light(track->eEnvironment);
|
||||
}
|
||||
Bind_Material(track->m_material2);
|
||||
draw(std::begin(track->Geometry2), std::end(track->Geometry2));
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
Bind_Material(track->m_material2);
|
||||
draw(std::begin(track->Geometry2), std::end(track->Geometry2));
|
||||
// restore default lighting
|
||||
setup_environment_light();
|
||||
break;
|
||||
}
|
||||
begin_batch(track->m_material2);
|
||||
m_pathbatch.insert(std::end(m_pathbatch), std::begin(track->Geometry2), std::end(track->Geometry2));
|
||||
break;
|
||||
}
|
||||
case rendermode::shadows:
|
||||
@@ -4185,6 +4224,7 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
flush_batch();
|
||||
|
||||
// third pass, material 3
|
||||
for (auto first{First}; first != Last; ++first)
|
||||
@@ -4212,15 +4252,16 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
setup_environment_light(track->eEnvironment);
|
||||
}
|
||||
Bind_Material(track->SwitchExtension->m_material3);
|
||||
draw(track->SwitchExtension->Geometry3);
|
||||
if (track->eEnvironment != e_flat)
|
||||
{
|
||||
Bind_Material(track->SwitchExtension->m_material3);
|
||||
draw(track->SwitchExtension->Geometry3);
|
||||
// restore default lighting
|
||||
setup_environment_light();
|
||||
break;
|
||||
}
|
||||
begin_batch(track->SwitchExtension->m_material3);
|
||||
m_pathbatch.emplace_back(track->SwitchExtension->Geometry3);
|
||||
break;
|
||||
}
|
||||
case rendermode::shadows:
|
||||
@@ -4246,6 +4287,7 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
flush_batch();
|
||||
|
||||
// fourth pass: per-track sleeper models (sleepermodel optional directive).
|
||||
// drawn after rails/trackbeds so depth pre-pass culling is favourable, and only in passes
|
||||
|
||||
@@ -388,6 +388,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// of once per cell -- collapsing many tiny instanced draws into a few large
|
||||
// batches. Reused every pass; the vectors are emptied (not freed) at the top of Render(scene::basic_region*).
|
||||
scene::basic_cell::instance_bucket_map m_frame_instance_buckets;
|
||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
||||
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||
// copies of instance bounds kept by the buckets of scene cells are good as long as they carry this number.
|
||||
// it changes with each frame drawn with the scenery editor active, as the editor moves and resizes instances in place
|
||||
unsigned int m_instanceboundsversion { 1 };
|
||||
|
||||
Reference in New Issue
Block a user