diff --git a/CMakeLists.txt b/CMakeLists.txt index a37b781b..ad7b2261 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.7) project(vsgExamples - VERSION 1.1.0 + VERSION 1.1.1 DESCRIPTION "Set of example programs that test and illustrate how to use the VulkanSceneGraph" LANGUAGES CXX C ) @@ -21,7 +21,7 @@ if (VULKAN_SDK) set(ENV{VULKAN_SDK} ${VULKAN_SDK}) endif() -find_package(vsg 1.1.0) +find_package(vsg 1.1.1) vsg_setup_dir_vars() vsg_setup_build_vars() @@ -32,6 +32,8 @@ find_package(vsgXchange 1.0.5 QUIET) # find the optional vsgImGui that can be used for GUI elements added into graphics windows. find_package(vsgImGui QUIET) +find_package(Tracy QUIET) + # set the use of C++17 globally as all examples require it set(CMAKE_CXX_STANDARD 17) diff --git a/examples/app/vsgviewer/vsgviewer.cpp b/examples/app/vsgviewer/vsgviewer.cpp index 406b9674..e4c660d8 100644 --- a/examples/app/vsgviewer/vsgviewer.cpp +++ b/examples/app/vsgviewer/vsgviewer.cpp @@ -118,6 +118,20 @@ int main(int argc, char** argv) if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + vsg::ref_ptr 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("--name")) gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_name; + else if (arguments.read("--className")) gpu_instrumentation->labelType = vsg::GpuAnnotation::Object_className; + else if (arguments.read("--func")) gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_function; + + instrumentation = gpu_instrumentation; + } + + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); if (argc <= 1) @@ -227,6 +241,8 @@ int main(int argc, char** argv) auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene); viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + if (instrumentation) viewer->assignInstrumentation(instrumentation); + viewer->compile(); if (maxPagedLOD > 0) diff --git a/examples/nodes/vsgshadow/CMakeLists.txt b/examples/nodes/vsgshadow/CMakeLists.txt index be8ecf92..8314b41b 100644 --- a/examples/nodes/vsgshadow/CMakeLists.txt +++ b/examples/nodes/vsgshadow/CMakeLists.txt @@ -11,4 +11,9 @@ if (vsgXchange_FOUND) target_link_libraries(vsgshadow vsgXchange::vsgXchange) endif() +if (Tracy_FOUND) + target_compile_definitions(vsgshadow PRIVATE Tracy_FOUND) + target_link_libraries(vsgshadow Tracy::TracyClient) +endif() + install(TARGETS vsgshadow RUNTIME DESTINATION bin) diff --git a/examples/nodes/vsgshadow/vsgshadow.cpp b/examples/nodes/vsgshadow/vsgshadow.cpp index 2c837992..f4bd4925 100644 --- a/examples/nodes/vsgshadow/vsgshadow.cpp +++ b/examples/nodes/vsgshadow/vsgshadow.cpp @@ -4,6 +4,10 @@ # include #endif +#ifdef Tracy_FOUND +# include +#endif + #include struct ModelSettings @@ -210,6 +214,28 @@ int main(int argc, char** argv) windowTraits->decoration = false; } + vsg::ref_ptr 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; + } +#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_instumentation_level); + arguments.read("--gpu", tracy_instrumentation->settings->gpu_instumentation_level); + instrumentation = tracy_instrumentation; + } +#endif + double maxShadowDistance = arguments.value(1e8, "--sd"); double shadowMapBias = arguments.value(0.005, "--sb"); double lambda = arguments.value(0.5, "--lambda"); @@ -523,6 +549,8 @@ int main(int argc, char** argv) auto commandGraph = vsg::CommandGraph::create(window, renderGraph); viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + if (instrumentation) viewer->assignInstrumentation(instrumentation); + viewer->compile(resourceHints); auto startTime = vsg::clock::now(); diff --git a/examples/utils/CMakeLists.txt b/examples/utils/CMakeLists.txt index e5cf8c4c..7e82dfaa 100644 --- a/examples/utils/CMakeLists.txt +++ b/examples/utils/CMakeLists.txt @@ -4,3 +4,7 @@ add_subdirectory(vsgshaderset) add_subdirectory(vsgintersection) add_subdirectory(vsgstoragebuffer) add_subdirectory(vsgcustomshaderset) + +if (Tracy_FOUND) + add_subdirectory(vsgtracyinstrumentation) +endif() diff --git a/examples/utils/vsgtracyinstrumentation/CMakeLists.txt b/examples/utils/vsgtracyinstrumentation/CMakeLists.txt new file mode 100644 index 00000000..43352c34 --- /dev/null +++ b/examples/utils/vsgtracyinstrumentation/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SOURCES + vsgtracyinstrumentation.cpp +) + +add_executable(vsgtracyinstrumentation ${SOURCES}) + +target_link_libraries(vsgtracyinstrumentation vsg::vsg Tracy::TracyClient) + +if (vsgXchange_FOUND) + target_compile_definitions(vsgtracyinstrumentation PRIVATE vsgXchange_FOUND) + target_link_libraries(vsgtracyinstrumentation vsgXchange::vsgXchange) +endif() + +install(TARGETS vsgtracyinstrumentation RUNTIME DESTINATION bin) diff --git a/examples/utils/vsgtracyinstrumentation/vsgtracyinstrumentation.cpp b/examples/utils/vsgtracyinstrumentation/vsgtracyinstrumentation.cpp new file mode 100644 index 00000000..54b8abfc --- /dev/null +++ b/examples/utils/vsgtracyinstrumentation/vsgtracyinstrumentation.cpp @@ -0,0 +1,359 @@ +#include + +#ifdef vsgXchange_FOUND +# include +#endif + +#include + +#include +#include +#include +#include + +vsg::ref_ptr createTextureQuad(vsg::ref_ptr sourceData, vsg::ref_ptr options) +{ + auto builder = vsg::Builder::create(); + builder->options = options; + + vsg::StateInfo state; + state.image = sourceData; + state.lighting = false; + + vsg::GeometryInfo geom; + geom.dy.set(0.0f, 0.0f, 1.0f); + geom.dz.set(0.0f, -1.0f, 0.0f); + + return builder->createQuad(geom, state); +} + +void enableGenerateDebugInfo(vsg::ref_ptr options) +{ + auto shaderHints = vsg::ShaderCompileSettings::create(); + shaderHints->generateDebugInfo = true; + + auto& text = options->shaderSets["text"] = vsg::createTextShaderSet(options); + text->defaultShaderHints = shaderHints; + + auto& flat = options->shaderSets["flat"] = vsg::createFlatShadedShaderSet(options); + flat->defaultShaderHints = shaderHints; + + auto& phong = options->shaderSets["phong"] = vsg::createPhongShaderSet(options); + phong->defaultShaderHints = shaderHints; + + auto& pbr = options->shaderSets["pbr"] = vsg::createPhysicsBasedRenderingShaderSet(options); + pbr->defaultShaderHints = shaderHints; +} + +class InstrumentationHandler : public vsg::Inherit +{ +public: + + vsg::ref_ptr instrumentation; + + InstrumentationHandler(vsg::ref_ptr in_instrumentation) : instrumentation(in_instrumentation) {} + + void apply(vsg::KeyPressEvent& keyPress) override + { + if (keyPress.keyModified == 'c') + { + if (instrumentation->settings->cpu_instumentation_level > 0) --instrumentation->settings->cpu_instumentation_level; + } + else if (keyPress.keyModified == 'C') + { + if (instrumentation->settings->cpu_instumentation_level < 3) ++instrumentation->settings->cpu_instumentation_level; + } + if (keyPress.keyModified == 'g') + { + if (instrumentation->settings->gpu_instumentation_level > 0) --instrumentation->settings->gpu_instumentation_level; + } + else if (keyPress.keyModified == 'G') + { + if (instrumentation->settings->gpu_instumentation_level < 3) ++instrumentation->settings->gpu_instumentation_level; + } + } +}; + +vsg::ref_ptr decorateWithInstrumentationNode(vsg::ref_ptr 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; +} + +int main(int argc, char** argv) +{ + try + { + // set up defaults and read command line arguments to override them + vsg::CommandLine arguments(&argc, argv); + + // 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 + + arguments.read(options); + + auto windowTraits = vsg::WindowTraits::create(); + windowTraits->windowTitle = "vsgtracyinstrumentation"; + windowTraits->debugLayer = arguments.read({"--debug", "-d"}); + windowTraits->apiDumpLayer = arguments.read({"--api", "-a"}); + windowTraits->synchronizationLayer = arguments.read("--sync"); + bool reportAverageFrameRate = arguments.read("--fps"); + bool multiThreading = arguments.read("--mt"); + if (arguments.read("--double-buffer")) windowTraits->swapchainPreferences.imageCount = 2; + if (arguments.read("--triple-buffer")) windowTraits->swapchainPreferences.imageCount = 3; // default + if (arguments.read("--IMMEDIATE")) { windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } + if (arguments.read("--FIFO")) windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_FIFO_KHR; + if (arguments.read("--FIFO_RELAXED")) windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; + if (arguments.read("--MAILBOX")) windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + if (arguments.read({"-t", "--test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->fullscreen = true; + reportAverageFrameRate = true; + } + if (arguments.read({"--st", "--small-test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->width = 192, windowTraits->height = 108; + windowTraits->decoration = false; + reportAverageFrameRate = true; + } + + if (arguments.read({"--fullscreen", "--fs"})) windowTraits->fullscreen = true; + if (arguments.read({"--window", "-w"}, windowTraits->width, windowTraits->height)) { windowTraits->fullscreen = false; } + if (arguments.read({"--no-frame", "--nf"})) windowTraits->decoration = false; + if (arguments.read("--or")) windowTraits->overrideRedirect = true; + if (arguments.read("--d32")) windowTraits->depthFormat = VK_FORMAT_D32_SFLOAT; + arguments.read("--screen", windowTraits->screenNum); + arguments.read("--display", windowTraits->display); + arguments.read("--samples", windowTraits->samples); + auto numFrames = arguments.value(-1, "-f"); + auto pathFilename = arguments.value("", "-p"); + auto outputFilename = arguments.value("", "-o"); + auto horizonMountainHeight = arguments.value(0.0, "--hmh"); + auto nearFarRatio = arguments.value(0.001, "--nfr"); + if (arguments.read("--rgb")) options->mapRGBtoRGBAHint = false; + + // set whether calibrated timestamp extension should be enabled. + bool calibrated = arguments.read("--calibrated"); + bool decorate = arguments.read("--decorate"); + + // set TracyInstrumentation options + auto instrumentation = vsg::TracyInstrumentation::create(); + arguments.read("--cpu", instrumentation->settings->cpu_instumentation_level); + arguments.read("--gpu", instrumentation->settings->gpu_instumentation_level); + + + if (arguments.read({"--shader-debug-info", "--sdi"})) + { + enableGenerateDebugInfo(options); + windowTraits->deviceExtensionNames.push_back(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); + } + +#ifndef TRACY_ON_DEMAND + vsg::info("TRACY_ON_DEMAND not enabled so assigning TracyInstrumentation by default."); + bool assignInstrumentationBeforeMainLoop = true; +#else + bool assignInstrumentationBeforeMainLoop = arguments.read("--always"); +#endif + + auto window = vsg::Window::create(windowTraits); + if (!window) + { + std::cout << "Could not create window." << std::endl; + return 1; + } + + if (calibrated) + { + auto physicalDevice = window->getOrCreatePhysicalDevice(); + if (physicalDevice->supportsDeviceExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)) + { + windowTraits->deviceExtensionNames.push_back(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME); + } + } + + if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); + + if (argc <= 1) + { + std::cout << "Please specify a 3d model or image file on the command line." << std::endl; + return 1; + } + + // assign instrumentation to vsg::Options to enable read/write functions to provide instrumentation + options->instrumentation = instrumentation; + + + auto group = vsg::Group::create(); + + vsg::Path path; + + // read any vsg files + for (int i = 1; i < argc; ++i) + { + vsg::Path filename = arguments[i]; + path = vsg::filePath(filename); + + auto object = vsg::read(filename, options); + if (auto node = object.cast()) + { + if (decorate) + { + group->addChild(decorateWithInstrumentationNode(node, filename.string(), vsg::uint_color(255, 255, 64, 255))); + } + else + { + group->addChild(node); + } + } + else if (auto data = object.cast()) + { + if (auto textureGeometry = createTextureQuad(data, options)) + { + if (decorate) + { + group->addChild(decorateWithInstrumentationNode(textureGeometry, filename.string(), vsg::uint_color(255, 255, 64, 255))); + } + else + { + group->addChild(textureGeometry); + } + } + } + else if (object) + { + std::cout << "Unable to view object of type " << object->className() << std::endl; + } + else + { + std::cout << "Unable to load file " << filename << std::endl; + } + } + + if (group->children.empty()) + { + return 1; + } + + vsg::ref_ptr vsg_scene; + if (group->children.size() == 1) + vsg_scene = group->children[0]; + else + vsg_scene = group; + + + if (outputFilename) + { + vsg::write(vsg_scene, outputFilename, options); + return 1; + } + + // create the viewer and assign window(s) to it + auto viewer = vsg::Viewer::create(); + viewer->addWindow(window); + + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + vsg_scene->accept(computeBounds); + vsg::dvec3 centre = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5; + double radius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.6; + + // set up the camera + auto lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -radius * 3.5, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0)); + + vsg::ref_ptr perspective; + auto ellipsoidModel = vsg_scene->getRefObject("EllipsoidModel"); + if (ellipsoidModel) + { + perspective = vsg::EllipsoidPerspective::create(lookAt, ellipsoidModel, 30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio, horizonMountainHeight); + } + else + { + perspective = vsg::Perspective::create(30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio * radius, radius * 4.5); + } + + auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D())); + + // add close handler to respond to the close window button and pressing escape + viewer->addEventHandler(vsg::CloseHandler::create(viewer)); + + auto animationPathHandler = vsg::RecordAnimationPathHandler::create(camera, pathFilename, options); + animationPathHandler->printFrameStatsToConsole = true; + viewer->addEventHandler(animationPathHandler); + + viewer->addEventHandler(vsg::Trackball::create(camera, ellipsoidModel)); + + // add event handler to control the cpu and gpu_instrumentation_level using the 'c', 'g' keys to reduce the cpu and gpu instruemntation level, and 'C' and 'G' to increase them respectively. + viewer->addEventHandler(InstrumentationHandler::create(instrumentation)); + + auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene); + viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + + if (assignInstrumentationBeforeMainLoop) + { + viewer->assignInstrumentation(instrumentation); + } + + if (multiThreading) + { + viewer->setupThreading(); + } + + viewer->compile(); + + viewer->start_point() = vsg::clock::now(); + + // rendering main loop + while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0)) + { +#if defined(TRACY_ENABLE) && defined(TRACY_ON_DEMAND) + if (!viewer->instrumentation && GetProfiler().IsConnected()) + { + vsg::info("Tracy profile is now connected, assigning TracyInstrumentation."); + viewer->assignInstrumentation(instrumentation); + } +#endif + + // pass any events into EventHandlers assigned to the Viewer + viewer->handleEvents(); + + viewer->update(); + + viewer->recordAndSubmit(); + + viewer->present(); + } + + if (reportAverageFrameRate) + { + auto fs = viewer->getFrameStamp(); + double fps = static_cast(fs->frameCount) / std::chrono::duration(vsg::clock::now() - viewer->start_point()).count(); + std::cout<<"Average frame rate = "<