Skip to content

vsg::DepthSorted issues for nodes added at runtime #1715

@Vic-Min

Description

@Vic-Min

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:

Image

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.

Image

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.

Image

But I never see the correct order of displaying the squares corresponding to the numbers of their bins.

Desktop :

  • OS: Win10, VS2022
  • Version VSG: last from repo

Modified vsgdynamicload.cpp:

#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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions