I create a scene of 4 overlapping squares placed in different bins using vsg::DepthSorted: the red square is in the 4th bin, the green one is in the 1st bin, the yellow one is in the 3rd bin, and the cyan one is in the 2nd bin.
scene.vsgt.txt
I see that the order in which the squares are displayed corresponds to the numbers of their bins:
Then I split the graph into two subgraphs. The first contains red and green squares, the second contains yellow and cyan ones.
scene1.vsgt.txt
scene2.vsgt.txt
I load subgraphs at runtime using a modified vsgdynamicload example. I modified it to allow overlapping of one subgraph with another.
In singleThreaded mode, I see that both squares from the second subgraph are drawn on top of the squares from the first subgraph.
In multiThreaded mode, I sometimes see a different picture: squares from the first subgraph are drawn on top of squares from the second subgraph. This apparently depends on the order in which the subgraphs are loaded.
But I never see the correct order of displaying the squares corresponding to the numbers of their bins.
#include <vsg/all.h>
#ifdef vsgXchange_FOUND
# include <vsgXchange/all.h>
#endif
#ifdef Tracy_FOUND
# include <vsg/utils/TracyInstrumentation.h>
#endif
#include <algorithm>
#include <chrono>
#include <iostream>
#include <thread>
vsg::ref_ptr<vsg::Node> decorateWithInstrumentationNode(vsg::ref_ptr<vsg::Node> node, const std::string& name, vsg::uint_color color)
{
auto instrumentationNode = vsg::InstrumentationNode::create(node);
instrumentationNode->setName(name);
instrumentationNode->setColor(color);
vsg::info("decorateWithInstrumentationNode(", node, ", ", name, ", {", int(color.r), ", ", int(color.g), ", ", int(color.b), ", ", int(color.a), "})");
return instrumentationNode;
}
struct Merge : public vsg::Inherit<vsg::Operation, Merge>
{
Merge(const vsg::Path& in_path, vsg::observer_ptr<vsg::Viewer> in_viewer, vsg::ref_ptr<vsg::Group> in_attachmentPoint, vsg::ref_ptr<vsg::Node> in_node, const vsg::CompileResult& in_compileResult) :
path(in_path),
viewer(in_viewer),
attachmentPoint(in_attachmentPoint),
node(in_node),
compileResult(in_compileResult) {}
vsg::Path path;
vsg::observer_ptr<vsg::Viewer> viewer;
vsg::ref_ptr<vsg::Group> attachmentPoint;
vsg::ref_ptr<vsg::Node> node;
vsg::CompileResult compileResult;
bool autoPlay = true;
void run() override
{
std::cout << "Merge::run() path = " << path << ", " << attachmentPoint << ", " << node << std::endl;
vsg::ref_ptr<vsg::Viewer> ref_viewer = viewer;
if (ref_viewer)
{
updateViewer(*ref_viewer, compileResult);
if (autoPlay)
{
// find any animation groups in the loaded scene graph and play the first animation in each of the animation groups.
auto animationGroups = vsg::visit<vsg::FindAnimations>(node).animationGroups;
for (auto ag : animationGroups)
{
if (!ag->animations.empty()) ref_viewer->animationManager->play(ag->animations.front());
}
}
}
attachmentPoint->addChild(node);
}
};
struct LoadOperation : public vsg::Inherit<vsg::Operation, LoadOperation>
{
LoadOperation(vsg::ref_ptr<vsg::Viewer> in_viewer, vsg::ref_ptr<vsg::Group> in_attachmentPoint, const vsg::Path& in_filename, vsg::ref_ptr<vsg::Options> in_options) :
viewer(in_viewer),
attachmentPoint(in_attachmentPoint),
filename(in_filename),
options(in_options) {}
vsg::observer_ptr<vsg::Viewer> viewer;
vsg::ref_ptr<vsg::Group> attachmentPoint;
vsg::Path filename;
vsg::ref_ptr<vsg::Options> options;
void run() override
{
vsg::ref_ptr<vsg::Viewer> ref_viewer = viewer;
// std::cout << "Loading " << filename << std::endl;
if (auto node = vsg::read_cast<vsg::Node>(filename, options))
{
if (vsg::value<bool>(false, "write", options))
{
auto outputFilename = vsg::simpleFilename(filename) + ".vsgt";
vsg::write(node, outputFilename, options);
vsg::info("Written ", outputFilename);
}
if (vsg::value<bool>(false, "decorate", options))
{
node = decorateWithInstrumentationNode(node, filename.string(), vsg::uint_color(255, 255, 64, 255));
}
auto result = ref_viewer->compileManager->compile(node);
if (result) ref_viewer->addUpdateOperation(Merge::create(filename, viewer, attachmentPoint, node, result));
else vsg::info("Loaded ", filename, " but compile failed { ", result.result, ", ", result.message, " }");
}
}
};
int main(int argc, char** argv)
{
try
{
// set up defaults and read command line arguments to override them
vsg::CommandLine arguments(&argc, argv);
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->sharedObjects = vsg::SharedObjects::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
options->readOptions(arguments);
auto numFrames = arguments.value(-1, "-f");
auto numThreads = arguments.value(16, "-n");
if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level);
// provide setting of the resource hints on the command line
vsg::ref_ptr<vsg::ResourceHints> resourceHints;
if (vsg::Path resourceFile; arguments.read("--resource", resourceFile)) resourceHints = vsg::read_cast<vsg::ResourceHints>(resourceFile);
// Use --decorate command line option to set "decorate" user Options value.
// this will be checked by the MergeOperation to decide wither to decorate the loaded subgraph with a InstrumentationNode
options->setValue("decorate", arguments.read("--decorate"));
vsg::ref_ptr<vsg::Instrumentation> instrumentation;
if (arguments.read({"--gpu-annotation", "--ga"}) && vsg::isExtensionSupported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
windowTraits->debugUtils = true;
auto gpu_instrumentation = vsg::GpuAnnotation::create();
if (arguments.read("--func")) gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_function;
instrumentation = gpu_instrumentation;
}
else if (arguments.read({"--profiler", "--pr"}))
{
// set Profiler options
auto settings = vsg::Profiler::Settings::create();
arguments.read("--cpu", settings->cpu_instrumentation_level);
arguments.read("--gpu", settings->gpu_instrumentation_level);
arguments.read("--log-size", settings->log_size);
arguments.read("--gpu-size", settings->gpu_timestamp_size);
// create the profiler
instrumentation = vsg::Profiler::create(settings);
}
#ifdef Tracy_FOUND
else if (arguments.read("--tracy"))
{
windowTraits->deviceExtensionNames.push_back(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
auto tracy_instrumentation = vsg::TracyInstrumentation::create();
arguments.read("--cpu", tracy_instrumentation->settings->cpu_instrumentation_level);
arguments.read("--gpu", tracy_instrumentation->settings->gpu_instrumentation_level);
instrumentation = tracy_instrumentation;
}
#endif
bool compileTraversalUseReserve = arguments.read("--reserve");
bool singleThreaded = arguments.read("--st");
auto outputFilename = arguments.value<vsg::Path>("", "-o");
if (arguments.read("--write")) options->setValue("write", true);
if (arguments.errors()) return arguments.writeErrorMessages(std::cerr);
if (argc <= 1)
{
std::cout << "Please specify at least one 3d model on the command line." << std::endl;
return 1;
}
// create a Group to contain all the nodes
auto vsg_scene = vsg::Group::create();
vsg::ref_ptr<vsg::Window> window(vsg::Window::create(windowTraits));
if (!window)
{
std::cout << "Could not create window." << std::endl;
return 1;
}
// create the viewer and assign window(s) to it
auto viewer = vsg::Viewer::create();
viewer->addWindow(window);
// set up the camera
auto lookAt = vsg::LookAt::create(
vsg::dvec3(0.0, 0.0, 1e5), // eye
vsg::dvec3(0.0, 0.0, 0.0), // center
vsg::dvec3(0.0, 1.0, 0.0)); // up
auto perspective = vsg::Orthographic::create(
0, windowTraits->width, 0, windowTraits->height, -1e6, 1e6);
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 to pressing escape
viewer->addEventHandler(vsg::CloseHandler::create(viewer));
viewer->addEventHandler(vsg::Trackball::create(camera));
auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene);
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});
if (instrumentation) viewer->assignInstrumentation(instrumentation);
if (!resourceHints)
{
// To help reduce the number of vsg::DescriptorPool that need to be allocated we'll provide a minimum requirement via ResourceHints.
resourceHints = vsg::ResourceHints::create();
resourceHints->numDescriptorSets = 256;
resourceHints->descriptorPoolSizes.push_back(VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256});
}
// configure the viewer's rendering backend, initialize and compile Vulkan objects, passing in ResourceHints to guide the resources allocated.
viewer->compile(resourceHints);
if (auto deviceMemoryBufferPools = viewer->recordAndSubmitTasks.front()->device->deviceMemoryBufferPools.ref_ptr())
{
deviceMemoryBufferPools->compileTraversalUseReserve = compileTraversalUseReserve;
}
vsg::ref_ptr<vsg::OperationThreads> loadThreads;
vsg::ref_ptr<vsg::OperationQueue> loadQueue;
if (singleThreaded)
{
loadQueue = vsg::OperationQueue::create(viewer->status);
}
else
{
loadThreads = vsg::OperationThreads::create(numThreads, viewer->status);
loadQueue = loadThreads->queue;
}
// assign the LoadOperation that will do the load in the background and once loaded and compiled, merge via Merge operation that is assigned to updateOperations and called from viewer.update()
vsg::observer_ptr<vsg::Viewer> observer_viewer(viewer);
for (int i = 1; i < argc; ++i)
{
loadQueue->add(LoadOperation::create(observer_viewer, vsg_scene, argv[i], options));
}
if (singleThreaded)
{
for(auto& operation : loadQueue->take_all())
{
operation->run();
}
}
// rendering main loop
while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0))
{
// pass any events into EventHandlers assigned to the Viewer
viewer->handleEvents();
viewer->update();
viewer->recordAndSubmit();
viewer->present();
// if (loadThreads->queue->empty()) break;
}
if (outputFilename)
{
vsg::write(vsg_scene, outputFilename, options);
vsg::info("Written ", outputFilename);
}
if (auto profiler = instrumentation.cast<vsg::Profiler>())
{
instrumentation->finish();
profiler->log->report(std::cout);
}
}
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;
}
I create a scene of 4 overlapping squares placed in different bins using vsg::DepthSorted: the red square is in the 4th bin, the green one is in the 1st bin, the yellow one is in the 3rd bin, and the cyan one is in the 2nd bin.
scene.vsgt.txt
I see that the order in which the squares are displayed corresponds to the numbers of their bins:
Then I split the graph into two subgraphs. The first contains red and green squares, the second contains yellow and cyan ones.
scene1.vsgt.txt
scene2.vsgt.txt
I load subgraphs at runtime using a modified vsgdynamicload example. I modified it to allow overlapping of one subgraph with another.
In singleThreaded mode, I see that both squares from the second subgraph are drawn on top of the squares from the first subgraph.
In multiThreaded mode, I sometimes see a different picture: squares from the first subgraph are drawn on top of squares from the second subgraph. This apparently depends on the order in which the subgraphs are loaded.
But I never see the correct order of displaying the squares corresponding to the numbers of their bins.
Desktop :
Modified vsgdynamicload.cpp: