Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ add_subdirectory(vsgtriangles)
add_subdirectory(vsgunicode)
add_subdirectory(vsgperformance)
add_subdirectory(vsgcast)
add_subdirectory(vsgpagedlodtest)
13 changes: 13 additions & 0 deletions tests/vsgpagedlodtest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(HEADERS DatabasePagerAutoscale.h)
set(SOURCES vsgpagedlodtest.cpp)

add_executable(vsgpagedlodtest ${HEADERS} ${SOURCES})

target_link_libraries(vsgpagedlodtest vsg::vsg)

if (vsgXchange_FOUND)
target_compile_definitions(vsgpagedlodtest PRIVATE vsgXchange_FOUND)
target_link_libraries(vsgpagedlodtest vsgXchange::vsgXchange)
endif()

install(TARGETS vsgpagedlodtest RUNTIME DESTINATION bin)
143 changes: 143 additions & 0 deletions tests/vsgpagedlodtest/DatabasePagerAutoscale.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#pragma once

#include <vsg/io/DatabasePager.h>
#include <vsg/io/Logger.h>
#include <vsg/io/read.h>
#include <vsg/io/ReaderWriter.h>
#include <vsg/nodes/MatrixTransform.h>
#include <vsg/threading/atomics.h>
#include <vsg/utils/ComputeBounds.h>

class DatabasePagerAutoscale : public vsg::Inherit<vsg::DatabasePager, DatabasePagerAutoscale>
{
public:
void start(uint32_t numReadThreads = 4) override
{
vsg::debug("DatabasePager::start(", numReadThreads, ")");

//
// set up read thread(s)
//
auto readThread = [](DatabasePagerAutoscale& databasePager, const std::string& threadName) {
vsg::debug("Started DatabaseThread read thread");

auto local_instrumentation = shareOrDuplicateForThreadSafety(databasePager.instrumentation);
if (local_instrumentation) local_instrumentation->setThreadName(threadName);

while (databasePager.status->active())
{
auto plod = databasePager._requestQueue->take_when_available(databasePager.frameCount.load());
if (plod)
{
CPU_INSTRUMENTATION_L1_NC(databasePager.instrumentation, "DatabasePager read", COLOR_PAGER);

uint64_t frameDelta = databasePager.frameCount - plod->frameHighResLastUsed.load();

if (frameDelta > 1 || !vsg::compare_exchange(plod->requestStatus, vsg::PagedLOD::ReadRequest, vsg::PagedLOD::Reading))
{
vsg::debug("Expire read request : databasePager.frameCount = ", databasePager.frameCount, ", plod->frameHighResLastUsed.load() = ", plod->frameHighResLastUsed.load());
databasePager.requestDiscarded(plod);
continue;
}

++plod->loadAttempts;

vsg::ref_ptr<vsg::Node> subgraph = plod->pending;
vsg::ref_ptr<vsg::Object> read_object;

if (!subgraph)
{
// vsg::info("read ", subgraph, " from ", plod->filename, " frameDelta = ", frameDelta, ", databasePager.frameCount = ", databasePager.frameCount, ", plod->frameHighResLastUsed.load() = ", plod->frameHighResLastUsed.load(), ", numActiveRequests = ", databasePager.numActiveRequests.load());
read_object = vsg::read(plod->filename, plod->options);

// Add scale node to show each model with the same size
if (vsg::ref_ptr<vsg::Node> node = read_object.cast<vsg::Node>())
{
vsg::ComputeBounds computeBounds;
node->accept(computeBounds);

vsg::dvec3 center = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5;
double radius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.5;
double radius_inv = 1.0 / radius;
auto scale = vsg::MatrixTransform::create(vsg::scale(radius_inv, radius_inv, radius_inv) * vsg::translate(-center));
scale->addChild(node);
subgraph = scale;
plod->bound = vsg::dsphere(center * radius_inv, 1.0);
}
}
else
{
// vsg::info("already read ", subgraph, " from ", plod->filename, " frameDelta = ", frameDelta, ", databasePager.frameCount = ", databasePager.frameCount, ", plod->frameHighResLastUsed.load() = ", plod->frameHighResLastUsed.load(), ", numActiveRequests = ", databasePager.numActiveRequests.load());
}

if (subgraph && compare_exchange(plod->requestStatus, vsg::PagedLOD::Reading, vsg::PagedLOD::Compiling))
{
{
std::scoped_lock<std::mutex> lock(databasePager.pendingPagedLODMutex);
plod->pending = subgraph;
}

try
{
// compile plod
if (auto result = databasePager.compileManager->compile(subgraph))
{
plod->requestStatus.exchange(vsg::PagedLOD::MergeRequest);

// info("DatabaserPager::start() compiled ", subgraph, ", success after ", plod->loadAttempts.load(), " loadAttempts");

// move to the merge queue;
databasePager._toMergeQueue->add(plod, result);
}
else
{
debug("DatabaserPager::start() unable to compile subgraph, discarding request ", subgraph);
databasePager.requestDiscarded(plod);
}
}
catch (...)
{
debug("DatabaserPager::start() compile threw exception, discarding request ", subgraph);
databasePager.requestDiscarded(plod);
}
}
else
{
if (auto read_error = read_object.cast<vsg::ReadError>())
vsg::warn(read_error->message);
else
vsg::warn("Failed to read ", plod, " ", plod->filename);

databasePager.requestDiscarded(plod);
}
}
else
{
// sleep for a frame.
std::this_thread::sleep_for(std::chrono::milliseconds(16 * 2));
}
}
vsg::debug("Finished DatabaseThread read thread");
};

auto deleteThread = [](DatabasePagerAutoscale& databasePager, const std::string& threadName) {
vsg::debug("Started DatabaseThread deletethread");

auto local_instrumentation = shareOrDuplicateForThreadSafety(databasePager.instrumentation);
if (local_instrumentation) local_instrumentation->setThreadName(threadName);

while (databasePager.status->active())
{
databasePager.deleteQueue->wait_then_clear();
}
vsg::debug("Finished DatabaseThread delete thread");
};

for (uint32_t i = 0; i < numReadThreads; ++i)
{
threads.emplace_back(readThread, std::ref(*this), vsg::make_string("DatabasePager read thread ", i));
}

threads.emplace_back(deleteThread, std::ref(*this), "DatabasePager delete thread ");
}
};
180 changes: 180 additions & 0 deletions tests/vsgpagedlodtest/vsgpagedlodtest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#include <vsg/all.h>

#ifdef vsgXchange_FOUND
# include <vsgXchange/all.h>
#endif

#include <iostream>

#include "DatabasePagerAutoscale.h"

int main(int argc, char** argv)
{
try
{
// set up defaults and read command line arguments to override them
vsg::CommandLine arguments(&argc, argv);

// create windowTraits using the any command line arugments to configure settings
auto windowTraits = vsg::WindowTraits::create(arguments);

// set up vsg::Options to pass in filepaths, ReaderWriters and other IO related options to use when reading and writing files.
auto options = vsg::Options::create();
options->fileCache = vsg::getEnv("VSG_FILE_CACHE");
options->paths = vsg::getEnvPaths("VSG_FILE_PATH");

#ifdef vsgXchange_all
// add vsgXchange's support for reading and writing 3rd party file formats
options->add(vsgXchange::all::create());
#endif

auto defaultDatabasePager = arguments.value(false, "--defaultDatabasePager");
auto maxPagedLOD = arguments.value(1500, "--maxPagedLOD");
auto useSharedObjects = arguments.value(false, "--shareObjects");

options->readOptions(arguments);

if (arguments.errors()) return arguments.writeErrorMessages(std::cerr);

if (argc <= 1)
{
std::cout << "Please specify 3d-models on the command line." << std::endl;
return 1;
}

auto vsg_scene = vsg::Group::create();

vsg::dvec3 origin(0.0, 0.0, 0.0);
vsg::dvec3 primary(2.0, 0.0, 0.0);
vsg::dvec3 secondary(0.0, 2.0, 0.0);

int numModels = argc - 1;
int numColumns = static_cast<int>(std::ceil(std::sqrt(static_cast<float>(numModels))));
int numRows = static_cast<int>(std::ceil(static_cast<float>(numModels) / static_cast<float>(numColumns)));

// read any 3d-models files
for (int i = 1; i < argc; ++i)
{
vsg::Path filename = arguments[i];

if (vsg::fileExists(filename))
{
int index = (i - 1);
vsg::dvec3 position = origin
+ primary * static_cast<double>(index % numColumns)
+ secondary * static_cast<double>(index / numColumns);
auto transform = vsg::MatrixTransform::create(vsg::translate(position));

auto pagedLOD = vsg::PagedLOD::create();
pagedLOD->filename = filename;
pagedLOD->options = options;
pagedLOD->bound = vsg::dsphere(origin, 1.0);
transform->addChild(pagedLOD);
vsg_scene->addChild(transform);

std::cout << "Sucessfully added pagedLOD for file " << filename << std::endl;
}
else
{
std::cout << "Unable to find file " << filename << std::endl;
}
}

if (vsg_scene->children.empty())
{
std::cout << "No 3d models added" << std::endl;
return 1;
}

// create the viewer and assign window(s) to it
auto viewer = vsg::Viewer::create();
auto window = vsg::Window::create(windowTraits);
if (!window)
{
std::cout << "Could not create window." << std::endl;
return 1;
}

viewer->addWindow(window);

// compute position the camera
double viewingDistance = std::sqrt(static_cast<float>(numModels)) * 3.0;
vsg::dvec3 eyeShift(0.0, -viewingDistance, 0.0);
vsg::dvec3 up(0.0, 0.0, 1.0);
vsg::dvec3 centre = origin
+ primary * (static_cast<double>(numColumns - 1) * 0.5)
+ secondary * (static_cast<double>(numRows - 1) * 0.5);

// set up the camera
auto lookAt = vsg::LookAt::create(centre + eyeShift, centre, up);
auto perspective = vsg::Perspective::create(30.0,
static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height),
0.01,
1000.0);
auto viewportState = vsg::ViewportState::create(window->extent2D());
auto camera = vsg::Camera::create(perspective, lookAt, viewportState);

// add close handler to respond to the close window button and pressing escape
viewer->addEventHandler(vsg::CloseHandler::create(viewer));

viewer->addEventHandler(vsg::Trackball::create(camera));
auto view = vsg::View::create(camera);
view->addChild(vsg::createHeadlight());
view->addChild(vsg_scene);

auto renderGraph = vsg::RenderGraph::create(window, view);
auto commandGraph = vsg::CommandGraph::create(window, renderGraph);
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});

// set custom databasepager which show each model with the same size for better demo scene
if (!defaultDatabasePager)
{
for (auto& task : viewer->recordAndSubmitTasks)
{
task->databasePager = DatabasePagerAutoscale::create();
std::cout << "Applied custom databasePager with autoscale models to the same size" << std::endl;
}
}

viewer->compile();

// set targetMaxNumPagedLODWithHighResSubgraphs after Viewer::compile() as it will assign any DatabasePager if required.
if (maxPagedLOD >= 0)
{
for (auto& task : viewer->recordAndSubmitTasks)
{
if (task->databasePager) task->databasePager->targetMaxNumPagedLODWithHighResSubgraphs = maxPagedLOD;
std::cout << "Applied databasePager->targetMaxNumPagedLODWithHighResSubgraphs = " << maxPagedLOD << std::endl;
}
}

if (useSharedObjects)
{
options->sharedObjects = vsg::SharedObjects::create();
}

viewer->start_point() = vsg::clock::now();

// rendering main loop
while (viewer->advanceToNextFrame())
{
// pass any events into EventHandlers assigned to the Viewer
viewer->handleEvents();

viewer->update();

viewer->recordAndSubmit();

viewer->present();
}
}
catch (const vsg::Exception& ve)
{
for (int i = 0; i < argc; ++i) std::cerr << argv[i] << " ";
std::cerr << "\n[Exception] - " << ve.message << " result = " << ve.result << std::endl;
return 1;
}

// clean up done automatically thanks to ref_ptr<>
return 0;
}