mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-06 23:13:00 +01:00
perf(textures): with texture streaming read the data ahead of the camera instead of the whole scenery at load, drop read-ahead data unused for 5 min
This commit is contained in:
@@ -202,6 +202,7 @@ public:
|
|||||||
glm::vec3 m_rotationscaleangles { 0.0f, 0.0f, 0.0f };
|
glm::vec3 m_rotationscaleangles { 0.0f, 0.0f, 0.0f };
|
||||||
glm::vec3 m_rotationscalescale { 1.0f, 1.0f, 1.0f };
|
glm::vec3 m_rotationscalescale { 1.0f, 1.0f, 1.0f };
|
||||||
bool m_rotationscalevalid { false };
|
bool m_rotationscalevalid { false };
|
||||||
|
bool m_texturesprefetched { false }; // the renderer had the data of its textures read ahead (gfx.textures.streaming)
|
||||||
material_data m_materialdata;
|
material_data m_materialdata;
|
||||||
|
|
||||||
std::string asText; // tekst dla wyświetlacza znakowego
|
std::string asText; // tekst dla wyświetlacza znakowego
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ struct texture_request {
|
|||||||
int height { 0 };
|
int height { 0 };
|
||||||
int mapcount { 0 };
|
int mapcount { 0 };
|
||||||
int blocksize { 0 };
|
int blocksize { 0 };
|
||||||
|
// handed to the workers (opengl_texture::prefetch()), and when: the main thread only
|
||||||
|
bool submitted { false };
|
||||||
|
std::chrono::steady_clock::time_point submittime;
|
||||||
// results, written by the thread reading the data
|
// results, written by the thread reading the data
|
||||||
std::vector<unsigned char> data;
|
std::vector<unsigned char> data;
|
||||||
bool failed { false };
|
bool failed { false };
|
||||||
@@ -90,6 +93,22 @@ public:
|
|||||||
}
|
}
|
||||||
m_read.wait( lock, [ &Request ]() { return Request->done; } ); }
|
m_read.wait( lock, [ &Request ]() { return Request->done; } ); }
|
||||||
|
|
||||||
|
// bytes of the data read for a request, 0 while it isn't there yet
|
||||||
|
std::size_t read_size( texture_request const &Request ) {
|
||||||
|
std::lock_guard<std::mutex> lock( m_mutex );
|
||||||
|
return ( Request.done ? Request.data.capacity() : 0 ); }
|
||||||
|
|
||||||
|
// gives back the data read for a request, once it's there. the request can be submitted again then
|
||||||
|
bool drop( texture_request &Request ) {
|
||||||
|
std::lock_guard<std::mutex> lock( m_mutex );
|
||||||
|
if( false == Request.done ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Request.data = std::vector<unsigned char>();
|
||||||
|
Request.done = false;
|
||||||
|
Request.failed = false;
|
||||||
|
return true; }
|
||||||
|
|
||||||
~texture_loader() {
|
~texture_loader() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock( m_mutex );
|
std::lock_guard<std::mutex> lock( m_mutex );
|
||||||
@@ -613,7 +632,8 @@ opengl_texture::load_DDS() {
|
|||||||
request->height = data_height;
|
request->height = data_height;
|
||||||
request->mapcount = data_mapcount;
|
request->mapcount = data_mapcount;
|
||||||
request->blocksize = blockSize;
|
request->blocksize = blockSize;
|
||||||
texture_loader::instance().submit( request );
|
// the data is read when it's going to be needed (prefetch()), or the first time the texture is made: the data of textures
|
||||||
|
// nothing draws yet would take the memory for nothing
|
||||||
|
|
||||||
data_components =
|
data_components =
|
||||||
ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1 ? GL_RGB : GL_RGBA;
|
ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1 ? GL_RGB : GL_RGBA;
|
||||||
@@ -1096,6 +1116,8 @@ opengl_texture::complete_data() {
|
|||||||
|
|
||||||
if( request == nullptr ) { return; }
|
if( request == nullptr ) { return; }
|
||||||
|
|
||||||
|
// read right away if nothing asked for the data before
|
||||||
|
prefetch();
|
||||||
texture_loader::instance().wait( request );
|
texture_loader::instance().wait( request );
|
||||||
auto const completed { std::move( request ) };
|
auto const completed { std::move( request ) };
|
||||||
|
|
||||||
@@ -1111,6 +1133,17 @@ opengl_texture::complete_data() {
|
|||||||
size = data.size() / 1024;
|
size = data.size() / 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// has a worker thread read the data of the texture, if it's waiting for that
|
||||||
|
void
|
||||||
|
opengl_texture::prefetch() {
|
||||||
|
|
||||||
|
if( ( request == nullptr ) || ( true == request->submitted ) ) { return; }
|
||||||
|
|
||||||
|
request->submitted = true;
|
||||||
|
request->submittime = std::chrono::steady_clock::now();
|
||||||
|
texture_loader::instance().submit( request );
|
||||||
|
}
|
||||||
|
|
||||||
// releases resources allocated on the opengl end, storing local copy if requested
|
// releases resources allocated on the opengl end, storing local copy if requested
|
||||||
void
|
void
|
||||||
opengl_texture::release() {
|
opengl_texture::release() {
|
||||||
@@ -1424,6 +1457,7 @@ texture_manager::mark_as_needed( texture_handle const Texture, bool const Near )
|
|||||||
// the vehicle came back, the texture is read again before it's drawn (in the background with gfx.textures.streaming)
|
// the vehicle came back, the texture is read again before it's drawn (in the background with gfx.textures.streaming)
|
||||||
texture.reload_on_use = false;
|
texture.reload_on_use = false;
|
||||||
texture.load();
|
texture.load();
|
||||||
|
texture.prefetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( needed == resource_timestamp() ) {
|
else if( needed == resource_timestamp() ) {
|
||||||
@@ -1499,10 +1533,59 @@ texture_manager::memory() const {
|
|||||||
++result.gpu.count;
|
++result.gpu.count;
|
||||||
result.gpu.bytes += static_cast<std::uint64_t>( texture.first->size ) * 1024;
|
result.gpu.bytes += static_cast<std::uint64_t>( texture.first->size ) * 1024;
|
||||||
}
|
}
|
||||||
|
if( texture.first->request != nullptr ) {
|
||||||
|
auto const &request { *texture.first->request };
|
||||||
|
if( false == request.submitted ) {
|
||||||
|
++result.waiting;
|
||||||
|
}
|
||||||
|
else if( auto const size { texture_loader::instance().read_size( request ) }; size > 0 ) {
|
||||||
|
++result.readahead.count;
|
||||||
|
result.readahead.bytes += size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++result.reading;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// has the data of a texture read, if it's waiting for that
|
||||||
|
void
|
||||||
|
texture_manager::prefetch( texture_handle const Texture ) {
|
||||||
|
|
||||||
|
if( ( Texture <= 0 )
|
||||||
|
|| ( Texture >= static_cast<texture_handle>( m_textures.size() ) ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_textures[ Texture ].first->prefetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// gives back the data read for textures which nothing drew within the specified time since it was asked for. it's read again when
|
||||||
|
// a texture is needed
|
||||||
|
std::size_t
|
||||||
|
texture_manager::drop_read_ahead( std::chrono::steady_clock::duration const Age ) {
|
||||||
|
|
||||||
|
auto const now { std::chrono::steady_clock::now() };
|
||||||
|
std::size_t count { 0 };
|
||||||
|
for( auto const &texture : m_textures ) {
|
||||||
|
if( ( texture.first == nullptr )
|
||||||
|
|| ( texture.first->request == nullptr ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto &request { *texture.first->request };
|
||||||
|
if( ( false == request.submitted )
|
||||||
|
|| ( now - request.submittime < Age ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( true == texture_loader::instance().drop( request ) ) {
|
||||||
|
request.submitted = false;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
// debug performance string
|
// debug performance string
|
||||||
std::string
|
std::string
|
||||||
texture_manager::info() const {
|
texture_manager::info() const {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <istream>
|
#include <istream>
|
||||||
|
#include <chrono>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include "winheaders.h"
|
#include "winheaders.h"
|
||||||
#include "utilities/memorystats.h"
|
#include "utilities/memorystats.h"
|
||||||
@@ -117,6 +118,8 @@ public:
|
|||||||
|
|
||||||
// takes over the data read by a worker thread, waiting for it if needed
|
// takes over the data read by a worker thread, waiting for it if needed
|
||||||
void complete_data();
|
void complete_data();
|
||||||
|
// has a worker thread read the data of the texture, if it's waiting for that (gfx.textures.streaming)
|
||||||
|
void prefetch();
|
||||||
|
|
||||||
// members
|
// members
|
||||||
std::shared_ptr<texture_request> request; // data being read by a worker thread
|
std::shared_ptr<texture_request> request; // data being read by a worker thread
|
||||||
@@ -183,12 +186,23 @@ public:
|
|||||||
info() const;
|
info() const;
|
||||||
// what the textures keep in memory, for the debug panel: copies of the data kept in the process (count, bytes), and the
|
// what the textures keep in memory, for the debug panel: copies of the data kept in the process (count, bytes), and the
|
||||||
// textures made on the gpu with the size of their data (count, bytes)
|
// textures made on the gpu with the size of their data (count, bytes)
|
||||||
|
// with gfx.textures.streaming also the data read for textures not made yet (count, bytes), the textures which wait for a
|
||||||
|
// worker to read it, and the ones which wait to be asked for it
|
||||||
struct memory_data {
|
struct memory_data {
|
||||||
memory_count cpu;
|
memory_count cpu;
|
||||||
memory_count gpu;
|
memory_count gpu;
|
||||||
|
memory_count readahead;
|
||||||
|
std::uint64_t reading { 0 };
|
||||||
|
std::uint64_t waiting { 0 };
|
||||||
};
|
};
|
||||||
memory_data
|
memory_data
|
||||||
memory() const;
|
memory() const;
|
||||||
|
// has the data of a texture read, if it's waiting for that (gfx.textures.streaming)
|
||||||
|
void
|
||||||
|
prefetch( texture_handle const Texture );
|
||||||
|
// gives back the data read for textures which nothing drew within the specified time since it was asked for
|
||||||
|
std::size_t
|
||||||
|
drop_read_ahead( std::chrono::steady_clock::duration const Age );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types:
|
// types:
|
||||||
|
|||||||
@@ -1254,8 +1254,13 @@ void opengl46_renderer::Update_Memory_Info()
|
|||||||
// textures: the copies of the data kept in the process after the upload
|
// textures: the copies of the data kept in the process after the upload
|
||||||
auto const textures { m_textures.memory() };
|
auto const textures { m_textures.memory() };
|
||||||
accounted += textures.cpu.bytes;
|
accounted += textures.cpu.bytes;
|
||||||
|
accounted += textures.readahead.bytes;
|
||||||
text += "textures: " + std::to_string( textures.cpu.count ) + " keep their data in ram, " + mb( textures.cpu.bytes ) + "; "
|
text += "textures: " + std::to_string( textures.cpu.count ) + " keep their data in ram, " + mb( textures.cpu.bytes ) + "; "
|
||||||
+ std::to_string( textures.gpu.count ) + " on the card, ~" + mb( textures.gpu.bytes ) + "\n";
|
+ std::to_string( textures.gpu.count ) + " on the card, ~" + mb( textures.gpu.bytes ) + "\n";
|
||||||
|
if( textures.readahead.count + textures.reading + textures.waiting > 0 ) {
|
||||||
|
text += " streamed: " + std::to_string( textures.readahead.count ) + " read ahead, not drawn yet, " + mb( textures.readahead.bytes ) + "; "
|
||||||
|
+ std::to_string( textures.reading ) + " being read; " + std::to_string( textures.waiting ) + " not read until they're needed\n";
|
||||||
|
}
|
||||||
// geometry: the copies of the chunks sent to the card
|
// geometry: the copies of the chunks sent to the card
|
||||||
auto const geometry { m_geometry.memory() };
|
auto const geometry { m_geometry.memory() };
|
||||||
accounted += geometry.vertices.bytes + geometry.indices.bytes + geometry.userdata.bytes;
|
accounted += geometry.vertices.bytes + geometry.indices.bytes + geometry.userdata.bytes;
|
||||||
@@ -6118,6 +6123,172 @@ void opengl46_renderer::Update_Texture_Release()
|
|||||||
m_textures.release_unneeded();
|
m_textures.release_unneeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// with gfx.textures.streaming the data of a texture is read when it's going to be drawn, not with the scenery: the textures of the
|
||||||
|
// whole scenery don't fit in memory. the cells of the region within the draw range and a margin around the camera are gone through
|
||||||
|
// a few in a frame, and the textures of what they hold which will be seen from there are read ahead, as are the ones of the vehicles
|
||||||
|
// nearby. what is drawn without that waits for its data to be read
|
||||||
|
void opengl46_renderer::Update_Texture_Prefetch()
|
||||||
|
{
|
||||||
|
if (m_colorpass.draw_range <= 0.f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double const margin{500.0}; // m: the way the data has to arrive
|
||||||
|
std::size_t const cellsperframe{32};
|
||||||
|
auto const camera{m_colorpass.pass_camera.position()};
|
||||||
|
auto const range{static_cast<double>(m_colorpass.draw_range) * Global.fDistanceFactor + margin};
|
||||||
|
|
||||||
|
auto const prefetch_material = [this](material_handle const Material) {
|
||||||
|
if (Material <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const &material{m_materials.material(Material)};
|
||||||
|
for (auto const texture : material.textures)
|
||||||
|
{
|
||||||
|
m_textures.prefetch(texture);
|
||||||
|
}
|
||||||
|
for (auto const &variant : material.texture_variants)
|
||||||
|
{
|
||||||
|
for (auto const texture : variant)
|
||||||
|
{
|
||||||
|
m_textures.prefetch(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto const prefetch_model = [this](TModel3d *Model) {
|
||||||
|
if (Model == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto const texture : Model_Textures(Model))
|
||||||
|
{
|
||||||
|
m_textures.prefetch(texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// the sections around the camera, the same way Render(scene::basic_region *) picks them
|
||||||
|
int constexpr cellspersection{(scene::EU07_SECTIONSIZE / scene::EU07_CELLSIZE) * (scene::EU07_SECTIONSIZE / scene::EU07_CELLSIZE)};
|
||||||
|
auto const camerax{static_cast<int>(std::floor(camera.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2))};
|
||||||
|
auto const cameraz{static_cast<int>(std::floor(camera.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2))};
|
||||||
|
int const segmentcount{2 * static_cast<int>(std::ceil(range / scene::EU07_SECTIONSIZE))};
|
||||||
|
int const side{segmentcount + 1};
|
||||||
|
int const originx{camerax - segmentcount / 2};
|
||||||
|
int const originz{cameraz - segmentcount / 2};
|
||||||
|
auto const cellcount{static_cast<std::size_t>(side) * side * cellspersection};
|
||||||
|
auto *region{simulation::Region};
|
||||||
|
|
||||||
|
// the first time the whole range at once: the textures around the start are read by the workers, not one by one by the first frame
|
||||||
|
auto const steps{m_prefetchcursor == 0 ? cellcount : cellsperframe};
|
||||||
|
for (std::size_t step = 0; step < steps; ++step, ++m_prefetchcursor)
|
||||||
|
{
|
||||||
|
auto const index{m_prefetchcursor % cellcount};
|
||||||
|
auto const sectionindex{static_cast<int>(index / cellspersection)};
|
||||||
|
auto const cellindex{index % cellspersection};
|
||||||
|
auto const row{originz + sectionindex / side};
|
||||||
|
auto const column{originx + sectionindex % side};
|
||||||
|
if ((row < 0) || (row >= scene::EU07_REGIONSIDESECTIONCOUNT) || (column < 0) || (column >= scene::EU07_REGIONSIDESECTIONCOUNT))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto *section{region->m_sections[row * scene::EU07_REGIONSIDESECTIONCOUNT + column]};
|
||||||
|
if (section == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((cellindex == 0) && (glm::length(section->m_area.center - camera) - section->m_area.radius <= range))
|
||||||
|
{
|
||||||
|
for (auto const &shape : section->m_shapes)
|
||||||
|
{
|
||||||
|
prefetch_material(shape.data().material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto const &cell{section->m_cells[cellindex]};
|
||||||
|
if ((false == cell.m_active) || (glm::length(cell.m_area.center - camera) - cell.m_area.radius > range))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (auto const &shape : cell.m_shapesopaque)
|
||||||
|
{
|
||||||
|
prefetch_material(shape.data().material);
|
||||||
|
}
|
||||||
|
for (auto const &shape : cell.m_shapestranslucent)
|
||||||
|
{
|
||||||
|
prefetch_material(shape.data().material);
|
||||||
|
}
|
||||||
|
for (auto const *track : cell.m_paths)
|
||||||
|
{
|
||||||
|
prefetch_material(track->m_material1);
|
||||||
|
prefetch_material(track->m_material2);
|
||||||
|
prefetch_material(track->m_sleeper_skin);
|
||||||
|
if (track->SwitchExtension != nullptr)
|
||||||
|
{
|
||||||
|
prefetch_material(track->SwitchExtension->m_material3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the instances which will be seen from within the margin. the ones asked for once aren't gone through again until they're
|
||||||
|
// out of reach, it's when their data could have been dropped
|
||||||
|
auto const prefetch_instances = [&](scene::basic_cell::instance_sequence const &Instances) {
|
||||||
|
for (auto *instance : Instances)
|
||||||
|
{
|
||||||
|
auto const distance{glm::length(instance->location() - camera)};
|
||||||
|
auto const reach{std::sqrt(instance->m_rangesquaredmax * Global.fDistanceFactor) + margin};
|
||||||
|
if ((distance > reach) || (false == instance->visible()))
|
||||||
|
{
|
||||||
|
if (distance > reach + margin)
|
||||||
|
{
|
||||||
|
instance->m_texturesprefetched = false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (true == instance->m_texturesprefetched)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
instance->m_texturesprefetched = true;
|
||||||
|
prefetch_model(instance->Model());
|
||||||
|
for (auto const skin : instance->Material()->replacable_skins)
|
||||||
|
{
|
||||||
|
prefetch_material(skin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
prefetch_instances(cell.m_instancesopaque);
|
||||||
|
prefetch_instances(cell.m_instancetranslucent);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const now{std::chrono::steady_clock::now()};
|
||||||
|
if (now - m_prefetchvehicletime >= std::chrono::seconds(1))
|
||||||
|
{
|
||||||
|
m_prefetchvehicletime = now;
|
||||||
|
for (auto *vehicle : simulation::Vehicles.sequence())
|
||||||
|
{
|
||||||
|
if ((vehicle == nullptr) || (glm::length(vehicle->vPosition - camera) - vehicle->radius() > range))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prefetch_model(vehicle->mdModel);
|
||||||
|
prefetch_model(vehicle->mdLowPolyInt);
|
||||||
|
prefetch_model(vehicle->mdLoad);
|
||||||
|
for (auto *attachment : vehicle->mdAttachments)
|
||||||
|
{
|
||||||
|
prefetch_model(attachment);
|
||||||
|
}
|
||||||
|
for (auto const skin : vehicle->m_materialdata.replacable_skins)
|
||||||
|
{
|
||||||
|
prefetch_material(skin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the data read for what wasn't drawn after all (the details of models seen only from close, or a turn not taken) goes after a
|
||||||
|
// while
|
||||||
|
if (now - m_prefetchdroptime >= std::chrono::seconds(10))
|
||||||
|
{
|
||||||
|
m_prefetchdroptime = now;
|
||||||
|
m_textures.drop_read_ahead(std::chrono::minutes(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
||||||
std::vector<texture_handle> const &opengl46_renderer::Model_Textures(TModel3d *Model)
|
std::vector<texture_handle> const &opengl46_renderer::Model_Textures(TModel3d *Model)
|
||||||
{
|
{
|
||||||
@@ -8506,6 +8677,11 @@ void opengl46_renderer::Update(double const Deltatime)
|
|||||||
Update_Texture_Release();
|
Update_Texture_Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((true == Global.gfx_texture_streaming) && (true == simulation::is_ready) && (simulation::Region != nullptr))
|
||||||
|
{
|
||||||
|
Update_Texture_Prefetch();
|
||||||
|
}
|
||||||
|
|
||||||
if ((true == Global.ControlPicking) && (false == FreeFlyModeFlag))
|
if ((true == Global.ControlPicking) && (false == FreeFlyModeFlag))
|
||||||
Pick_Control_Callback([](const TSubModel *, const glm::vec2) {});
|
Pick_Control_Callback([](const TSubModel *, const glm::vec2) {});
|
||||||
// temporary conditions for testing. eventually will be coupled with editor mode
|
// temporary conditions for testing. eventually will be coupled with editor mode
|
||||||
|
|||||||
@@ -507,6 +507,8 @@ class opengl46_renderer : public gfx_renderer {
|
|||||||
void Update_Texture_Release();
|
void Update_Texture_Release();
|
||||||
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
||||||
std::vector<texture_handle> const &Model_Textures( TModel3d *Model );
|
std::vector<texture_handle> const &Model_Textures( TModel3d *Model );
|
||||||
|
// has the data of the textures read ahead of the camera, with gfx.textures.streaming
|
||||||
|
void Update_Texture_Prefetch();
|
||||||
void Render(TMemCell *Memcell);
|
void Render(TMemCell *Memcell);
|
||||||
void Render_particles();
|
void Render_particles();
|
||||||
void Render_precipitation();
|
void Render_precipitation();
|
||||||
@@ -647,6 +649,10 @@ class opengl46_renderer : public gfx_renderer {
|
|||||||
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<queued_instance>, scene::basic_cell::instance_bucket_key_hash> m_frame_instance_buckets;
|
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<queued_instance>, scene::basic_cell::instance_bucket_key_hash> m_frame_instance_buckets;
|
||||||
// textures used by vehicle models, for the release of far textures
|
// textures used by vehicle models, for the release of far textures
|
||||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||||
|
// the read ahead of textures (Update_Texture_Prefetch()): the cell it goes on with
|
||||||
|
std::size_t m_prefetchcursor { 0 };
|
||||||
|
std::chrono::steady_clock::time_point m_prefetchvehicletime;
|
||||||
|
std::chrono::steady_clock::time_point m_prefetchdroptime;
|
||||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
// 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;
|
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||||
// instance records kept by the buckets of scene cells are good as long as they carry this number.
|
// instance records kept by the buckets of scene cells are good as long as they carry this number.
|
||||||
|
|||||||
Reference in New Issue
Block a user