├── .gitignore ├── 1_SettingTheScene ├── 01_hello_world │ ├── CMakeLists.txt │ └── hello_world.cpp ├── BuildingVulkanSceneGraph.md ├── CMakeLists.txt ├── DevelopmentPrinciples.md ├── Ecosystem.md ├── HelloWorld.md ├── HighLevelAPIs.md ├── LowLevelAPIs.md ├── PerformancePrinciples.md ├── Vulkan.md ├── VulkanSceneGraphLibrary.md └── index.md ├── 2_Foundations ├── 2_PrintVisitor │ ├── CMakeLists.txt │ └── PrintVisitor.cpp ├── 2_observer_ptr │ ├── CMakeLists.txt │ └── observer_ptr.cpp ├── 2_rtti │ ├── CMakeLists.txt │ └── rtti.cpp ├── 2_serialization │ ├── CMakeLists.txt │ └── serialization.cpp ├── 2_streams │ ├── CMakeLists.txt │ └── streams.cpp ├── Allocator.md ├── DataTypes.md ├── FileSystem.md ├── MathFunctions.md ├── MathTypes.md ├── Metadata.md ├── Object_base_class_and_ref_ptr.md ├── RTTI.md ├── ReaderWriter.md ├── Serialization.md ├── StreamsAndLogger.md ├── Visitors.md ├── index.md ├── observer_ptr.md └── vsgXchange.md ├── 3_SceneGraph ├── Commands.md ├── MeshShaders.md ├── Nodes.md ├── RayTracing.md ├── State.md ├── index.md └── osg2vsg.md ├── 404.html ├── 4_Application └── index.md ├── 5_DevelopingSkills └── index.md ├── CMakeLists.txt ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── README.md ├── _config.yml ├── _includes ├── header.html └── includelines ├── images ├── VSGTutorialLogo.svg ├── VSGlogo.png └── hello_world.png └── index.md /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | -------------------------------------------------------------------------------- /1_SettingTheScene/01_hello_world/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(hello_world) 4 | 5 | # set the use of C++17 globally as all examples require it 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | find_package(vsg REQUIRED) 9 | find_package(vsgXchange REQUIRED) 10 | 11 | add_executable(hello_world hello_world.cpp) 12 | 13 | target_link_libraries(hello_world vsg::vsg vsgXchange::vsgXchange) 14 | -------------------------------------------------------------------------------- /1_SettingTheScene/01_hello_world/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int, char**) 6 | { 7 | #ifndef vsgXchange_curl 8 | std::cerr<<"vsgXchange::curl not available, so can not load OpenStreetMap data over http."<add(vsgXchange::all::create()); 19 | 20 | // create the scene graph using OpenStreetMap convenience function 21 | auto scene = vsg::TileDatabase::create(); 22 | scene->settings = vsg::createOpenStreetMapSettings(options); 23 | scene->readDatabase(options); 24 | 25 | // 26 | // Section 2 : Create and setup the Viewer, Window and compile Vulkan objects 27 | // 28 | // create the viewer and assign window(s) to it 29 | auto viewer = vsg::Viewer::create(); 30 | 31 | // create window with default traits 32 | auto windowTraits = vsg::WindowTraits::create(); 33 | auto window = vsg::Window::create(windowTraits); 34 | viewer->addWindow(window); 35 | 36 | // set up the camera 37 | double radius = vsg::WGS_84_RADIUS_EQUATOR; 38 | double nearFarRatio = 0.0001; 39 | 40 | auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, -radius*4.5, 0.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 0.0, 1.0)); 41 | auto perspective = vsg::Perspective::create(30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio*radius, radius * 4.5); 42 | auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D())); 43 | 44 | // add close handler to respond to pressing the window close window button and pressing escape 45 | viewer->addEventHandler(vsg::CloseHandler::create(viewer)); 46 | 47 | // add a trackball event handler to control the camera view using the mouse 48 | viewer->addEventHandler(vsg::Trackball::create(camera)); 49 | 50 | // create a command graph to render the scene on specified window 51 | auto commandGraph = vsg::createCommandGraphForView(window, camera, scene); 52 | viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); 53 | 54 | // compile all the Vulkan objects and transfer data required to render the scene 55 | viewer->compile(); 56 | 57 | // 58 | // Section 3 : execute the frame loop 59 | // 60 | while (viewer->advanceToNextFrame()) 61 | { 62 | // pass any events into EventHandlers assigned to the Viewer 63 | viewer->handleEvents(); 64 | 65 | // update the scene graph, such as adding/removing database pager tiles 66 | viewer->update(); 67 | 68 | // record the commands in the scene graph and submit the completed command buffers to the vulkan queue 69 | viewer->recordAndSubmit(); 70 | 71 | // wait for completion of the rendering and present the resulting color buffer to the Window's swap chain. 72 | viewer->present(); 73 | } 74 | 75 | // clean up done automatically thanks to ref_ptr<> 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /1_SettingTheScene/BuildingVulkanSceneGraph.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Building VulkanSceneGraph software 4 | permalink: /SettingTheScene/BuildingVulkanSceneGraph 5 | --- 6 | 7 | To conclude this chapter, we'll look at building the VulkanSceneGraph projects and a minimal standalone **hello world** application. 8 | 9 | All VulkanSceneGraph projects, including the vsgTutorial exercises, are written in C++17 and use CMake as the cross-platform build system. To aid integration of the VulkanSceneGraph projects with other software, CMake config files are installed along with headers and libraries. CMake config files provide details of the libraries, header locations and any compiler definitions that are required when building software that uses VulkanSceneGraph libraries and help to avoid issues when working across different platforms and with differences in static and dynamic library builds. 10 | 11 | The first task is to check out the main repositories and vsgTutorial for its exercises, for brevity we'll assume you have a Debian-based system and install headers, libraries and CMake files locally and set env paths up to find the install locations, this way there is no need to install to system directories. If you have other types of systems, use one of the alternative build instructions: 12 | * Windows - to be written (looking for volunteers to write this.) 13 | * macOS - to be written (looking for volunteers to write this.) 14 | 15 | First we'll set up some environment variable, for ease of use you may wish to put this into a resource file like .bashrc: 16 | 17 | ~~~ sh 18 | # env vars useful for locating source projects, installed libraries and binaries 19 | export INSTALL_DIR=~/Install 20 | export PROJECT_DIR=~/Projects 21 | export PATH=${PATH}:${INSTALL_DIR}/bin 22 | export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${INSTALL_DIR}/lib 23 | 24 | # env vars that are used to find example data, and locating where to cache files downloaded from HTTP during database paging 25 | export VSG_FILE_PATH=${PROJECT_DIR}/vsgExamples/data 26 | export VSG_FILE_CACHE=${PROJECT_DIR}/vsgFileCache 27 | 28 | # only required if you use VulkanSDK rather than installing Vulkan from distro repositories 29 | export VULKAN_SDK=${INSTALL_DIR}/VulkanSDK 30 | export PATH=${PATH}:${VULKAN_SDK}/bin 31 | ~~~ 32 | 33 | Next install dependencies, under Debian these are: 34 | 35 | ~~~ sh 36 | # dependencies useful for building the VulkanSceneGraph 37 | sudo apt-get install git g++ 38 | sudo apt-get install cmake cmake-curses-gui 39 | 40 | # install Vulkan and the validation layers, alternatively you can download and install the VulkanSDK, see below. 41 | sudo apt-get install libvulkan-dev vulkan-tools vulkan-validationlayers 42 | 43 | # dependencies used by vsgXchange 44 | sudo apt-get install libassimp-dev libfreetype-dev libopenexr-dev libcurl4-openssl-dev 45 | ~~~ 46 | 47 | If you want to use the VulkanSDK instead of the distro vulkan package, download it from [LunarG](https://vulkan.lunarg.com/sdk/home): 48 | 49 | ~~~ sh 50 | wget https://sdk.lunarg.com/sdk/download/1.3.239.0/linux/vulkansdk-linux-x86_64-1.3.239.0.tar.gz 51 | tar xvf vulkansdk-linux-x86_64-1.3.239.0.tar.gz -C ${INSTALL_DIR} 52 | ~~~ 53 | 54 | Now checkout the VulkanSceneGraph, vsgXchange, vsgExamples and vsgTutorial 55 | 56 | ~~~ sh 57 | mkdir ${PROJECT_DIR} 58 | cd ${PROJECT_DIR} 59 | 60 | git clone https://github.com/vsg-dev/VulkanSceneGraph.git 61 | git clone https://github.com/vsg-dev/vsgXchange.git 62 | git clone https://github.com/vsg-dev/vsgExamples.git 63 | git clone https://github.com/vsg-dev/vsgTutorial.git 64 | ~~~ 65 | 66 | To generate the make system, build and install: 67 | 68 | ~~~ sh 69 | cd VulkanSceneGraph 70 | cmake . -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} 71 | make -j 16 72 | make install 73 | 74 | cd ../vsgXchange 75 | cmake . -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} 76 | make -j 16 77 | make install 78 | 79 | cd ../vsgExamples 80 | cmake . -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} 81 | make -j 16 82 | make install 83 | ~~~ 84 | 85 | To test out examples: 86 | 87 | ~~~ sh 88 | vsgdraw 89 | vsgviewer models/lz.vsgt 90 | vsgdynamictexture 91 | ~~~ 92 | 93 | --- 94 | 95 | Prev: [VulkanSceneGraph Library](VulkanSceneGraphLibrary.md) | Next : [Hello World](HelloWorld.md) 96 | -------------------------------------------------------------------------------- /1_SettingTheScene/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(01_hello_world) 2 | -------------------------------------------------------------------------------- /1_SettingTheScene/DevelopmentPrinciples.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Development Principles 4 | permalink: /SettingTheScene/DevelopmentPrinciples 5 | --- 6 | 7 | The VulkanSceneGraph Project goal is to make the development of high-performance graphics and compute applications quick and easy. Vulkan provides an excellent base for achieving high performance but is low-level and complicated to use. Simply wrapping a C API in C++ is not sufficient for application developers' needs, a collection of high-level features that make development easy is what is required. The ***Vulkan Made Easy*** tag line was adopted for the vsg-dev GitHub account as a daily reminder of our project's fundamental goal. 8 | 9 | The VulkanSceneGraph library is built upon the Scene Graph concept and is written specifically for Vulkan. Just as Vulkan is the successor to OpenGL, one that has wholly different API and architecture but retains the fundamental goal of providing a low-level, open, cross platform hardware abstraction, the VulkanSceneGraph is successor to the OpenSceneGraph, it also has an entirely different API and architecture but retains the fundamental goal of providing an open, cross platform high-performance API for application developers. 10 | 11 | ## Productivity and Performance 12 | 13 | The underlying principles that guided the VulkanSceneGraph development are the desire to enable application developers to be as ***Productive*** as possible while delivering the best ***Performance*** possible. These two are often competing principles, to deliver both at the same time requires good engineering - one must continually strive to write better class interfaces and implementations, to seek out simple yet flexible and efficient solutions. 14 | 15 | The process of refining the software to better meet the needs of application developers won't stop with the VulkanSceneGraph-1.0 release, as the software develops these guiding principles will remain and we'll keep striving to deliver on the mantra Vulkan Made Easy. 16 | 17 | As open-source middleware used by developers we aspire for the software to be not just useful for the functionality it provides, but also as an example of good software, illustrating how to write modern C++ software and avoid the many pitfalls of writing advanced software. 18 | 19 | ## Approach to Development 20 | 21 | The VulkanSceneGraph is an open-source project in license and practice: 22 | * From day 1 all work has been published on github and made available to all under the [MIT](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/LICENSE.md) license. 23 | * Fully embrace the capabilities of C++ 17, modern CMake and GitHub for building software, managing software and communicating. 24 | * Take lessons from developing the OpenSceneGraph, elements that are strong are updated and brought into the new scene graph, while flaws are recognized and used as a driver for finding better solutions. 25 | * To bring in lessons from the wider C++ community the [C++CoreGuidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) are used to guide class design and implementation. 26 | * Spiral Development Model: 27 | 28 | Work follows a cycle starting at the centre and then moving around and outward following a spiral. On the innermost cycles of the spiral, work is relatively simple and exploratory but builds basic elements that later work can build upon, or on the next cycle round the spiral can be revisited, refactored and made more capable if the original implementation is found insufficient. Steadily, understanding of the problem domain is built and the software is incrementally built out in features and robustness. 29 | 30 | * While not formally an [Agile Software Development](https://en.wikipedia.org/wiki/Agile_software_development) project we draw upon its manifesto: 31 | * **Individuals and interactions** over processes and tools 32 | * **Working software** over comprehensive documentation 33 | * **Customer collaboration** over contract negotiation 34 | * **Responding to change** over following a plan 35 | 36 | ## Only high value dependencies 37 | 38 | The VulkanSceneGraph library only has C++17, CMake and Vulkan as external dependencies. The Vulkan C headers are used rather than the Vulkan C++ header which is unnecessary as the VulkanSceneGraph provides its own encapsulation of Vulkan objects in a way that is coherent with how they are used in the scene graph. 39 | 40 | For runtime shader compilation support the VulkanSceneGraph library uses the [glslang](https://github.com/KhronosGroup/glslang) library. Originally integrated as an optional external dependency, it is now built internally as a submodule. While glslang support is compiled in by default it can be toggled off by setting the CMake VSG_SUPPORTS_ShaderCompiler variable to 0 before building the source. Compiling glslang within the VulkanSceneGraph library resolved problems with inconsistent 3rd party packaging of glslang, so now users can have a seamless experience across platforms. 41 | 42 | During the initial development of VulkanSceneGraph various other 3rd party dependencies, like glm and glfw, were considered for features like maths and windowing, but in each of these cases it was decided to implement the required features within the project rather than add an external dependency. The reasons for implementing the functionality within the project were: 43 | 44 | * Coherent class interfaces and naming 45 | * Coherent memory management 46 | * Provide classes focused just on the needs of the VulkanSceneGraph users 47 | * Avoid the glue code required to make different libraries work well together 48 | * Keep memory and CPU overhead to a minimum 49 | * Keep dependencies to a minimum to avoid ***Dependency Hell*** 50 | * Keep licensing simple and permissive 51 | 52 | An example of how local implementations can achieve what we need with far less code can be seen looking at the glm library. It's a header only library with over 63,000 lines of code. The VulkanSceneGraph has all GLSL style vector, quaternion, and matrix functionality it needs in less than 3,000 lines of code. The VulkanSceneGraph code base has 57,000 lines of code for its headers and source, and it has a scene graph, Vulkan integration, cross platform windowing, viewer classes, serialization support and much more. 53 | 54 | --- 55 | 56 | Prev: [High-level APIs](HighLevelAPIs.md)| Next: [Performance Principles](PerformancePrinciples.md) 57 | -------------------------------------------------------------------------------- /1_SettingTheScene/Ecosystem.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: VulkanSceneGraph Ecosystem 4 | permalink: /SettingTheScene/Ecosystem 5 | --- 6 | 7 | One of the weaknesses of the OpenSceneGraph project is that, while it started as a small scene graph library, step by step it encompassed a wider range of features and dependencies. While the OpenSceneGraph stops short of providing all the functionality of a Game Engine it is bloated for the needs of many application developers. Early in the VulkanSceneGraph project the decision was made to create an ecosystem built on a general purpose scene graph library and a collection of companion libraries each focused on specific features that users can easily compile and use in their applications if they need them. 8 | 9 | ## Foundation for all VulkanSceneGraph applications 10 | 11 | Features that all application developers using Vulkan will need, so provided by the VulkanSceneGraph library: 12 | * Robustly create and cleanup data 13 | * Serialization of scene graph objects to native ascii and binary file formats 14 | * Extensible mechanism for reading/writing 3rd party data. 15 | * Vector math classes and associated functions 16 | * Robust management of the lifetime of Vulkan objects including support for Vulkan extensions 17 | * Scene Graph internal nodes, state and geometry 18 | * Creation and management of Vulkan capable windows, offscreen buffers and event handling 19 | * Support for views, cameras and control of them 20 | * Support for commonly used graphics pipelines - flat shaded, phong and physics based rendering 21 | 22 | ## Ecosystem centred around vsg-dev GitHub account 23 | 24 | The [vsg-dev](https://github.com/vsg-dev) GitHub account hosts the following projects which are officially supported as part the VulkanSceneGraph project, the three main projects that the majority of developers will need to use are: 25 | * [VulkanSceneGraph](https://github.com/vsg-dev/VulkanSceneGraph) - The VulkanSceneGraph library itself is capable of being used on its own for standalone graphics applications, and for embedded platforms this may be the desired approach as it will minimize the code base that needs to be QA'd for security and robustness, and minimizes the final executable size. 26 | * [vsgXchange](https://github.com/vsg-dev/vsgXchange) 27 | It is expected that for most desktop applications, users will also want to load a range of 2D image and 3D model data, to support this usage the library adds support for a wide range of image and 3D model formats, using assimp, as well as support for reading data from http/https using libcurl, and reading/writing/processing of GIS imagery and DEMS through the optional GDAL integration. 28 | * [vsgExamples](https://github.com/vsg-dev/vsgExamples) 29 | A collection of 59 examples (as of 20th March 2023) that have been written to help test VulkanSceneGraph features as they are developed and as an educational tool for new users. 30 | 31 | Also hosted on vsg-dev are more specialized projects: 32 | 33 | * [osg2vsg](https://github.com/vsg-dev/osg2vsg) - osg2vsg library for converting data and integrating OpenGL/OSG and Vulkan/VSG applications. vsgXchange will automatically add support for reading data using the OpenSceneGraph's loader when osg2vsg has been built and installed before vsgXchange. 34 | * [vsgImGui](https://github.com/vsg-dev/vsgImGui) - ImGui and ImPlot integration with the VulkanSceneGraph 35 | * [vsgQt](https://github.com/vsg-dev/vsgQt) - Qt5/Qt6 integration with the VulkanSceneGraph 36 | * [vsgPoints](https://github.com/vsg-dev/vsgPoints) 3d point cloud loading and rendering for VulkanSceneGraph. 37 | * [vsgUnity](https://github.com/vsg-dev/vsgUnity) - plugin for the Unity Editor for exporting VulkanSceneGraph models 38 | * [MyFirstVsgApplication](https://github.com/vsg-dev/MyFirstVsgApplication) - simple standalone example illustrating the CMake and C++ code required for a basic application. 39 | * [vsgFramework](https://github.com/vsg-dev/vsgFramework) - template project that uses CMake FetchContent to pull in all the main libraries associated with VulkanSceneGraph and its dependencies and builds them together. 40 | 41 | ## Community projects: 42 | * [vsgSDL](https://github.com/ptrfun/vsgSDL) SDL integration with VulkanSceneGraph. 43 | * [vsgvr](https://github.com/geefr/vsgvr) OpenVR integration with VulkanSceneGraph. 44 | * [vsgCs](https://github.com/timoore/vsgCs) 3D Tiles and Cesium ion integration 45 | * [vsgEarth](https://github.com/timoore/vsgEarth) osgEarth integration 46 | 47 | ## 3rd party dependencies 48 | 49 | The VulkanSceneGraph library uses Vulkan, C++17 and CMake as external dependencies. The Vulkan C headers are used rather than the Vulkan C++ header which is unnecessary as the VulkanSceneGraph provides its own encapsulation of Vulkan objects in a way that is consistent with how they are used in the scene graph. 50 | 51 | For runtime shader compilation support the [glslang](https://github.com/KhronosGroup/glslang) library is used, originally as an optional external dependency, but since VulkanSceneGraph-1.0.3 it is now built internally as a submodule, this is compiled in by default but can be toggled off by setting the CMake VSG_SUPPORTS_ShaderCompiler variable to 0 before building the source. Compiling glslang within the VulkanSceneGraph library resolved problems with inconsistent 3rd party packaging of glslang, so now users can have a seamless experience across platforms. 52 | 53 | The additional VulkanSceneGraph projects add their own dependencies: 54 | 55 | | Project | Required | Optional | 56 | | [VulkanSceneGraph](https://github.com/vsg-dev/VulkanSceneGraph) | C++17, CMake, Vulkan | glslang integrated as a submodule | 57 | | [vsgXchange](https://github.com/vsg-dev/vsgXchange) | VulkanSceneGraph | curl, assimp, gdal, freetype, OpenEXR, osg2vsg | 58 | | [vsgImGui](https://github.com/vsg-dev/vsgImGui) | VulkanSceneGraph, ImGui & ImPlot integrated as a submodule | | 59 | | [vsgQt](https://github.com/vsg-dev/vsgQt) | VulkanSceneGraph, Qt5.10 or later, Qt6 | | 60 | | [vsgPoints](https://github.com/vsg-dev/vsgPoints) | VulkanSceneGraph | | 61 | | [vsgExamples](https://github.com/vsg-dev/vsgExamples) | VulkanSceneGraph | vsgXchange, vsgImGui | 62 | 63 | --- 64 | 65 | Prev: [Performance Principles](PerformancePrinciples.md)| Next: [VulkanSceneGraph Library](VulkanSceneGraphLibrary.md) 66 | -------------------------------------------------------------------------------- /1_SettingTheScene/HelloWorld.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Hello World 4 | permalink: /SettingTheScene/HelloWorld 5 | --- 6 | 7 | To download the tutorial exercises, clone the vsgTutorial repository. It contains both the book contents and the exercises: 8 | ~~~ sh 9 | cd ${PROJECT_DIR} 10 | git clone https://github.com/vsg-dev/vsgTutorial.git 11 | ~~~ 12 | 13 | To build the hello world exercise change into the exercise directory, and use the following CMake script to build a standalone application: 14 | 15 | ~~~ cmake 16 | {% include_relative 01_hello_world/CMakeLists.txt %} 17 | ~~~ 18 | 19 | To build the application change into source directory, run CMake then make: 20 | 21 | ~~~ sh 22 | cd vsgTutorial/1_SettingTheScene/01_hello_world 23 | cmake . 24 | make 25 | ~~~ 26 | 27 | The hello world exercise is just a single main() function that has three sections to it: 28 | 29 | 1. create the scene graph 30 | 1. create and setup the viewer to render the scene graph 31 | 1. execute the frame loop which does the handling of GUI events and rendering. 32 | 33 | ~~~ cpp 34 | {% include_relative 01_hello_world/hello_world.cpp %} 35 | ~~~ 36 | 37 | To run the application: 38 | 39 | ~~~ sh 40 | ./helloworld 41 | ~~~ 42 | 43 | --- 44 | 45 | Prev: [Building VulkanSceneGraph Software](BuildingVulkanSceneGraph.md) | Next: [Next Chapter : Foundations](../2_Foundations/index.md) 46 | -------------------------------------------------------------------------------- /1_SettingTheScene/HighLevelAPIs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: High-level APIs 4 | permalink: /SettingTheScene/HighLevelAPIs 5 | --- 6 | 7 | Developers wishing to add graphics capabilities to their applications have to either deal directly with the low-level APIs to drive the graphics and compute hardware, or choose from a collection of higher level APIs that are more focused on the needs of application developers than the underlying low-level APIs. A good high-level API will not only enable developers to be more productive but also provide better out of the box performance and visual quality, with the high-level API packaging up the expertise and time put in by specialists in high performance graphics and compute. 8 | 9 | ### Scene Graphs 10 | 11 | A common concept utilized by high-level graphics APIs is a Scene Graph. Scene Graphs are an example of a Directed Acyclic Graph (DAG), where a tree like hierarchy is used to represent the scene. Nodes in the graph may be internal group nodes, support culling, such as view frustum or level of detail culling, through to behavior like switches or sequences, or represent state or geometries that are passed to the GPU for rendering. 12 | 13 | ### Features vs Generality 14 | 15 | High-level APIs may choose to remain quite minimal in feature set and leave more functionality for the application developer to implement, while others build in many different areas of functionality. Broadly speaking the wider the range of features provided by an API the more domain specific it becomes as each additional feature will choose a particular approach or 3rd party dependency that is likely to work better for some domains than others. The more features are bundled together, the more choices are made for you, and the more likely it is they'll be inappropriate for your needs and just bloat the codebase and binaries. The advantage of the greater feature integration is potentially providing a cohesive system of features that work well together. 16 | 17 | Examples of high-level APIs with a relatively modest feature set and tight focus would be scene graphs like Open Inventor or Performer, while ones that encompass an extensive feature set would be game engines like Unreal or Unity. These two approaches aren't incompatible - a game engine could contain its own scene graph or a 3rd party scene graph, the latter approach was taken by the [Delta3D](https://en.wikipedia.org/wiki/Delta3D) project that built upon the OpenSceneGraph. 18 | 19 | 20 | ### Abstraction vs Encapsulation 21 | 22 | Some high-level APIs choose to build in low-level API abstraction layers so they can support multiple APIs such as Vulkan, OpenGL, multiple versions of Direct3D, Metal etc. - this brings us to the perverse realm of the hardware abstraction layer abstraction layer. Such extra abstraction layers add complexity and move the application developer further away from what they are attempting to run on the hardware. This approach also adds more code for the high-level API maintainer to write, test, debug and support, and a far greater range of permutations of hardware and APIs to test and support. This poor engineering approach has been born from the real or perceived issues with how well the cross platform low-level APIs are supported across platforms, it's a workaround to the attempts of Microsoft/Apple/etc. to exert vendor lock-in. 23 | 24 | An alternative approach used by some high-level APIs is to simply encapsulate the low-level APIs rather than abstract from them, this keeps the application developer closer to a 1:1 relationship with the data and processing they are managing on the hardware. Taking this path could limit portability to particular OSs, or hardware, but thankfully with OpenGL and Vulkan they are designed to be both OS agnostic and able to handle a wide range of hardware so the choice to encapsulate OpenGL or Vulkan need not limit portability over the low-level API abstraction approach. 25 | 26 | ## A Brief History of High-level APIs 27 | 28 | One of the first widely used high-level APIs for real-time 3D graphics was IRIS [Inventor](https://en.wikipedia.org/wiki/Open_Inventor) that was started at SGI in the late 1980's and followed a scene graph approach to represent a 3D scene. It was created to make development of 3D graphics applications easier as the low-level nature of IRIS GL was a barrier to entry. Then it was succeeded by Open Inventor which used OpenGL as its base and became widely adopted in the scientific and engineering sectors where ease of use was more critical than maximizing performance. Inventor's strength lay in its embodiment of the scene graph concept in such a flexible and extensible way. Its weakness was in the way its design limited performance and scalability. 29 | 30 | [Performer](https://en.wikipedia.org/wiki/OpenGL_Performer), created in 1991 by developers from the Inventor development group, focused on performance rather than usability. Performer was designed to work multithreaded, scaling to multiple CPUs and multiple GPUs in order to utilize SGI's high-end hardware such as the Onyx line of graphics "super" computers. These have far less performance and capabilities than modern phones, but during the mid-90's they were the world's most powerful graphics systems. 31 | 32 | [Cosmo3D](https://en.wikipedia.org/wiki/OpenGL%2B%2B) - mid 1990's there was yet another SGI scene graph! Created to provide a unified scene graph that was scalable and fast like Performer, and with better usability like Inventor. SGI worked with other vendors to create a standardised OpenGL++ scene graph, using Cosmo3D as the base. 33 | 34 | [Fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit_(graphics_API)) - late 1990's. SGI and Microsoft collaborated to create a low-level API successor to OpenGL, and high-level successor to Inventor/Performer. At the high-level they used Cosmo3D as a starting place. Microsoft strung SGI along for a year before the project collapsed, with Microsoft using the IP agreement to enable more advanced features to make it into Direct3D. Microsoft's plan all along? 35 | 36 | The collapse of the Fahrenheit project left a vacuum in the world of standardized high-level APIs, the proprietary scene graph world had failed to deliver a standardized solution as had been done for low-level APIs. In the late 1990's Open Source/Free Software was growing in significance and offered an entirely different approach to creating software, a number of hobby level projects sprung up like [Plib](https://sourceforge.net/projects/plib/) through to [CrystalSpace](https://en.wikipedia.org/wiki/Crystal_Space) but none had anything close to the capabilities of the professional APIs like Inventor, Performer and other proprietary APIs like Vega. 37 | 38 | In 1999 the Fraunhofer Institute created a consortium to develop the [OpenSG](https://en.wikipedia.org/wiki/OpenSG) project, from the outset it had high aspirations for delivering an Open Source cross platform, scalable, high performance scene graph - embracing all the facets that the industry had been wanting from Fahrenheit. 39 | 40 | At the same time, completely independently and initially unaware of the OpenSG project, Don Burns, an SGI engineer, working in his spare time on a home-built Hang Gliding Simulator, created a small scene graph library, on top of OpenGL, simply because Performer hadn't yet been ported to the Linux PC. Don sought assistance from Robert Osfield, a fellow software engineer and Hang Glider pilot with knowledge of aerodynamics to help with the development of the flight model. Robert ported the nascent scene graph library to Windows and the decision was made to publish the library as Open Source under the LGPL license. Originally named SG, for Scene Graph, Open was added to the name and the [OpenSceneGraph](https://en.wikipedia.org/wiki/OpenSceneGraph) project came into existence with a tar.gz file published on a single web page. Two engineers in their spare time were hacking away on a hang glider sim with no lofty goal beyond just getting a hang glider, a flying site with a hill and a few trees rendered. 41 | 42 | In 2000 the vis-sim and GIS sectors desperately needed a modern, professional grade graphics scene graph alternative to the proprietary scene graph projects which were poorly supported, slow moving or failing. OpenSG and OpenSceneGraph both were contenders but paradoxically the grand plans of the OpenSG project meant its release cycle was too slow for an industry desperate for a new scene graph, while the OpenSceneGraph was developed in spare time, with no formal plans or funding but a rapid release cycle and responsive lead developers and gained acceptance in the vis-sim and GIS sector. 43 | 44 | By 2001 the OpenSceneGraph had gained a commercial following sufficient for Robert and then Don to leave their full-time jobs and go fulltime and pay the bills through consulting, bespoke development work and training services. Through the first decade of the new millennium the project gained a following in both commercial and open-source applications with thousands of developers worldwide using it, and over 500 developers having contributed directly to the software code base. The strength of the OpenSceneGraph lay in the expertise and responsiveness of lead developers and the choice to encapsulate OpenGL in a clean way, without attempting to abstract away from the API layer. 45 | 46 | In 2005 [Unity](https://en.wikipedia.org/wiki/Unity_(game_engine)) was released, a game engine and tools with a licensing model that was free for hobby/small scale use. Unity evolved over the years from a niche of the market to wide market acceptance. 47 | 48 | Around the middle of the second decade OpenGL's evolution was under serious strain with CPU and GPUs capabilities so far removed from the capabilities of hardware in the early 90's. Driver quality across vendors and performance remained a serious issue for application developers. Small incremental changes were no longer sufficient to keep up, and the attempt at introducing the OpenGL Core Profile only fractured and complicated the task of supporting OpenGL. For high-level APIs like the OpenSceneGraph that focused entirely on OpenGL and OpenGL ES the troubles with OpenGL and its own age began to make it more cumbersome to support and use. 49 | 50 | In 2014 the [Unreal Engine 4](https://en.wikipedia.org/wiki/Unreal_Engine) was released with a new licensing model, with the full source code released and royalties charged on game revenue. Having a professional grade game engine and tools widely available changed the competitive landscape for high-level APIs. 51 | 52 | In 2016 Khronos released Vulkan as the successor to OpenGL, which then presented the question for high-level APIs - do they add yet another low-level hardware abstraction layer to the ones they already support or create new high-level APIs specifically for Vulkan building upon its strengths. Game engines that adopted Vulkan showed modest performance gains over OpenGL and have increasingly become the preferred choice for cross platform games. 53 | 54 | The advent of Vulkan and the clear need for it given OpenGL's failings presented the OpenSceneGraph with a dilemma. The OpenSceneGraph is written to be an OpenGL scene graph - it embodies the OpenGL++ dream held by engineers in the 90's, the whole design is built around the OpenGL state machine and the OpenGL FIFO and threading constraints. Vulkan has a completely different API and architecture to OpenGL and a significantly lower CPU overhead. For the OpenSceneGraph to adopt Vulkan and still support OpenGL would require a major rewrite of the API and internal architecture and implementation, handling two very different low-level APIs would require significant compromises in the way features are exposed in order to support both OpenGL and Vulkan. Such considerations would make the final high-level software incompatible with older versions of the OpenSceneGraph and deliver performance that is less than either OpenGL or Vulkan is capable of due to the extra abstraction layer that would need to be introduced. 55 | 56 | It was clear to OpenSceneGraph project lead Robert Osfield that a new scene graph built for Vulkan using modern C++ would be the best choice for exposing all the new capabilities that Vulkan brings and to keep the CPU overheads as low as possible so that performance could fully take advantage of Vulkan's low CPU overhead. For instance, if your existing scene graph has a CPU overhead of 10ms, and OpenGL adds another 5ms then the best-case improvement from going to Vulkan with a 1ms CPU overhead would be a 10.5ms frame-time - a 43% improvement, even though Vulkan might be 10 X faster. To get a 5 X improvement overall you also have to make the scene graph 10 X faster as well. 57 | 58 | In Spring 2018 Robert Osfield was approached by a company that used OpenSceneGraph for vis-sim that wanted to fund the initial development of a new Vulkan based scene graph. Work began on the VulkanSceneGraph in the [3rd week of May 2018](https://github.com/vsg-dev/VulkanSceneGraph/commit/5fb0bdb1b49741ac5f8911c21128511a46823825). Four and a half very intense years later [VulkanSceneGraph-1.0](https://github.com/vsg-dev/VulkanSceneGraph/releases/tag/VulkanSceneGraph-1.0.0) was released on the 13th November 2022. 59 | 60 | --- 61 | 62 | Prev: [Vulkan](Vulkan.md) | Next: [Development Principles](DevelopmentPrinciples.md) 63 | -------------------------------------------------------------------------------- /1_SettingTheScene/LowLevelAPIs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Low-level APIs 4 | permalink: /SettingTheScene/LowLevelAPIs 5 | --- 6 | 7 | Low-level graphics and compute APIs provide an interface with hardware drivers that manage data transfers and processing on highly parallelized graphics and compute hardware. Low-level APIs may be tied to specific operating systems and/or specific sets of hardware, through to being capable of running on multiple operating systems and across a wide range of hardware. OpenGL and Vulkan are examples of the latter that are OS agnostic and provide extensible hardware abstraction. 8 | 9 | ### Level of Hardware Abstraction vs Complexity 10 | 11 | Low-level APIs focus on interfacing with hardware efficiently, rather than making it convenient to develop graphics and compute applications. Older low-level APIs like OpenGL provide a higher level of abstraction than modern low-level APIs like Vulkan and Direct3D 12, which are designed to enable greater low-level control over data, processing and synchronization with the hardware. 12 | 13 | These modern APIs can be seen as more focused on hardware performance and capabilities than on supporting the needs of application developers, so while lifting the ceiling on raw performance and quality they require significantly greater expertise from application developers in order to use them. 14 | 15 | ### Incremental vs Monolithic 16 | 17 | OpenGL and Vulkan are written to evolve incrementally and in a fine grained way, with vendor specific extensions being exposed before these features are worked on by groups of vendors and before these features eventually are merged into the core. This contrasts with a monolithic approach taken by Direct3D with major versions changing the API and features exposed, to use the latest features you have no choice but to update to the new API version. 18 | 19 | For applications that will be developed over many years the approach taken with OpenGL and Vulkan helps keep the applications relevant without requiring major rewrites as new features can be introduced one by one as software & hardware evolves. The discrete jumps of Direct3D would require major rewrites in order to take advantage of new features, not a major issue for application types like games that get written once and have a short shelf life, but would be a hindrance for long lived applications. 20 | 21 | ## A Brief History of low-level APIs 22 | 23 | In the 1980's SGI created IRIS GL (https://en.wikipedia.org/wiki/IRIS_GL), a C API for 2D and 3D rendering on SGI hardware. 24 | 25 | In 1992 SGI introduced OpenGL - an open and cross platform C API capable of rendering textured triangles and basic lighting on a variety of hardware and OS's. Throughout the early nineties SGI and OpenGL saw massive adoption and growth for both OpenGL and SGI as a company. 26 | 27 | In 1996 Microsoft introduced Direct3D - starting a new era of vendor lock-in, while early rev's weren't competitive to OpenGL, the industry sleepwalked into adoption by Microsoft's anti-competitive practices. 28 | 29 | During the early 2000s Direct3D progressively dominated the games industry, with OpenGL left serving professional graphics applications. 30 | 31 | In August 2009 [OpenCL](https://en.wikipedia.org/wiki/OpenCL) was launched by Apple and in 2010 it was adopted by Khronos Group as a cross-vendor Compute on GPU API to complement OpenGL's Graphics on GPU capabilities. 32 | 33 | In 2013 AMD started working on [Mantle](https://en.wikipedia.org/wiki/Mantle_(API)) to develop an API with much lower overhead that was better able to maximize utilization of modern multi-core CPUs and super fast GPUs. 34 | 35 | In 2014, after many years of supporting OpenGL, Apple joined the vendor lock-in game, creating its own low-level [Metal API](https://en.wikipedia.org/wiki/Metal_(API)), and began a march towards deprecating OpenGL on Apple systems. 36 | 37 | Khronos adopted Mantle as the basis for a new cross-vendor, cross platform successor to OpenGL, releasing the [Vulkan-1.0](https://en.wikipedia.org/wiki/Vulkan) spec in February 2016. Vulkan is lower level than OpenGL, applications take over more responsibilities that previously OpenGL drivers would have had to manage, but provides more freedom and control, such as multi-threaded control of data and commands being passed to/from the GPU. Vulkan combines Compute and Graphics in one single coherent API. 38 | 39 | In February 2018 the MoltenVK library was released enabling Vulkan to run on top of Metal on macOS and iOS systems breaking the vendor lock-in and enabling Vulkan to work on all major hardware and software platforms. 40 | 41 | --- 42 | 43 | Prev: [Introduction](index.md) | Next: [Vulkan](Vulkan.md) 44 | -------------------------------------------------------------------------------- /1_SettingTheScene/PerformancePrinciples.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Performance Principles 4 | permalink: /SettingTheScene/PerformancePrinciples 5 | --- 6 | 7 | Efficiency of the design and implementation is critical to the project's success so took centre stage when evaluating different approaches to design and implementation, with the aspiration of making each VulkanSceneGraph feature 10 X faster than the equivalent feature in the OpenSceneGraph. Understanding what is happening in hardware to the data and how it is processed in each stage of the application is crucial to achieving this goal. 8 | 9 | CPU bottlenecks with scene graph application predominantly occur during traversals of the scene graph, this is due to: 10 | * Scene Graphs can contain tens or hundreds of thousands of objects and use many GBs of memory spread across nodes, textures and geometry data 11 | * Traversing large scene graphs pushes the limits of memory bandwidth and CPU cache coherency, this was true in the 90's and is even more critical in 2020's 12 | * CPU features like pre-fetch, branch prediction and instruction parallelization are sensitive to how data is arranged and how control flows are structured 13 | 14 | Different CPUs have different strengths and weaknesses, some cope well with diffuse memory access and large numbers of branches, others struggle with such challenging code and data. Taking care of how data is stored and processed to avoid stressing the CPU and memory architecture can substantially improve CPU utilization and throughput. A series of design decisions were made for the VulkanSceneGraph to help make this happen: 15 | 16 | * From the [vsg::Object](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/Object.h) base class through to scene graph nodes, classes are kept as small as possible. Data members like names, descriptions, masks and callbacks are avoided in the most commonly used classes. 17 | * A flexible meta data scheme is provided by a [vsg::Auxiliary](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/Auxiliary.h) class that can be used on all objects when required, but in most cases will simply be the overhead of a null pointer. This allows developers the flexibility to add names, descriptions etc when needed, without all objects paying the memory penalty. 18 | * Scene graph callbacks are eliminated, with easy subclassing from scene graph nodes making this possible. Not having scene graph callbacks on nodes avoids the need to hold pointers to them and to check those pointers to see if custom behaviour is required. 19 | * Scene graph node masks only exist on specific node types where they are most useful, again reducing memory footprint of nodes and avoiding conditional statements. 20 | * Dedicated nodes for scene graph culling like view frustum and LOD culling rather than having this functionality on all nodes avoids the need for a bounding volume data member on all scene graph nodes, and avoids the need for checking if a cull test is required and any branching based on a cull test. 21 | * A block memory allocator, the [https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/Allocator.h](vsg::Allocator), groups objects of similar type together into pre allocated blocks in memory. Nodes are packed together, geometry and texture data are packed together etc. 22 | 23 | Together these design decisions ensure: 24 | 25 | * minimal object size so more objects can fit in main memory and crucially the CPU caches - vsg::Node takes just 24 bytes vs osg::Node 208 bytes. 26 | * objects that will be operated on together are stored in memory together to increase the probability that cache lines will contain objects that will be next accessed. 27 | * conditional statements are reserved to just the places where they are required, rather than everywhere just in case they might be needed, so there is less branching, and branch prediction has a better chance of following the correct path. 28 | 29 | The end result is scene graph traversals that are around 10 times faster than the equivalent in the OpenSceneGraph. These are just a taste of the many performance related decisions in the VulkanSceneGraph, as you work through the vsgTutorial you'll be introduced to more tips and tricks that have been used, and ones that you can use in your own application development as well. 30 | 31 | --- 32 | 33 | Prev: [Development Principles](DevelopmentPrinciples.md) | Next: [Ecosystem](Ecosystem.md) 34 | 35 | -------------------------------------------------------------------------------- /1_SettingTheScene/Vulkan.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Vulkan - graphics and compute API 4 | permalink: /SettingTheScene/Vulkan 5 | --- 6 | 7 | The standards body [Khronos](https://www.khronos.org/) released the successor to OpenGL, Vulkan-1.0 in February 2016, ushering in the next generation of Open, Cross Platform API for graphics and compute. 8 | 9 | ### Pros: 10 | * Very low CPU overhead enables low power consumption and higher performance. 11 | 12 | * Multi-threaded for modern CPUs and GPUs: 13 | Designed from the ground up for multi-threading, with state localized within command buffers and explicit synchronisation primitives. 14 | 15 | * Cross platform: [source Wikipedia](https://en.wikipedia.org/wiki/Vulkan) 16 | "Vulkan runs natively on Android, Linux, BSD Unix, QNX, Haiku, Nintendo Switch, Raspberry Pi, Stadia, Fuchsia, Tizen, and Windows 7, 8, 10, and 11. MoltenVK provides freely-licensed third-party support for macOS, iOS and tvOS by wrapping over Apple's Metal API." 17 | 18 | * Runtime extension system, enabling fine-grained support for latest hardware features, including: 19 | * Ray Tracing 20 | * Mesh Shaders 21 | 22 | * Vulkan layers that can be enabled at runtime: 23 | * Validation layers 24 | * API output layers 25 | * Best practice guides 26 | 27 | ### Cons: 28 | 29 | * Low level, responsibility for many tasks moves from driver into application scope. 30 | Developers must explicitly configure settings, allocate and manage memory, manage transferring of data to/from GPU and dispatching and synchronizing CPU and GPU operations. 31 | 32 | * Requires more knowledge and code to implement even simple features - 1500 lines of code to just render a textured triangle! 33 | 34 | ### Useful links: 35 | 36 | * [VulkanSDK](https://vulkan.lunarg.com/sdk/home) LUNARG's website containing the VulkanSDK - 37 | provides a single package with headers and libs for Windows, Linux and macOS. 38 | 39 | * [AndroidNDK](https://developer.android.com/ndk/guides/graphics/index.html) - Googles Android NDK 40 | 41 | * [vulkan.org](https://www.vulkan.org/) - official website 42 | * Learning: 43 | * [Key Resources](https://www.vulkan.org/learn#key-resources) 44 | * [Tutorials](https://www.vulkan.org/learn#vulkan-tutorials) 45 | * [Videos](https://www.vulkan.org/learn#videos) 46 | * [Blogs](https://www.vulkan.org/blog) 47 | * Tools: 48 | * [Drivers]([https://www.vulkan.org/tools#vulkan-gpu-resources) 49 | * [SDK](https://www.vulkan.org/tools#download-these-essential-development-tools) 50 | * [Profilers and debuggers](https://www.vulkan.org/tools#profilers-and-debuggers) 51 | * [Frameworks](https://www.vulkan.org/tools#frameworks-and-helper-libraries) - includes VulkanSceneGraph :-) 52 | * [Profiles](https://www.vulkan.org/tools#vulkan-profiles) 53 | * [Events](https://www.vulkan.org/events) 54 | * [News](https://www.vulkan.org/news) 55 | * [Vulkan-SC](https://www.khronos.org/vulkansc/) Vulkan for safety-critical systems 56 | 57 | --- 58 | 59 | Prev: [Low Level APIs](LowLevelAPIs.md) | Next: [High Level APIs](HighLevelAPIs.md) 60 | 61 | -------------------------------------------------------------------------------- /1_SettingTheScene/VulkanSceneGraphLibrary.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: VulkanSceneGraph Library 4 | permalink: /SettingTheScene/VulkanSceneGraphLibrary 5 | --- 6 | 7 | The [VulkanSceneGraph library](https://github.com/vsg-dev/VulkanSceneGraph/) provides the features that will be common to all graphics & compute applications - creating a viewer, windows, scene graph, reading/writing data, creating Vulkan objects and recording Vulkan commands, synchronizing with Vulkan processes and CPU threads, 3d vector maths and common utilities like computing bounds and intersection test and shader composition and compilation. 8 | 9 | ## Feature organization 10 | 11 | All VulkanSceneGraph projects follow the same structure - public headers can be found in include/ directories and the implementation can be found in the src/ directories. This pattern is used to make it clear to end users what part they will interface with, and as clear demarcation to library developers & contributors that interfaces and implementations both have distinct roles. When installing the software it will only be the files in the include directories and any built libraries + cmake config files that will be installed. 12 | 13 | The [vsg library headers](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg) are grouped in the following [include/vsg](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/) subdirectories: 14 | 15 | Foundational class directories: 16 | * [core](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/core) - base classes, smart pointer, data containers, allocators and visitor base classes 17 | * [maths](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths) - GLSL style maths classes, similar to glm 18 | * [io](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io) - serialization and built-in reader/writers. 19 | 20 | Scene graph class directories: 21 | * [commands](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/commands) - scene graph nodes for Vulkan Commands 22 | * [state](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/state) - scene graph objects for setting Vulkan state. 23 | * [nodes](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/) - scene graph nodes like groups, switches & transforms 24 | * [text](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/text) - text scene graph nodes 25 | * [meshshaders](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/meshshaders) - Khronos Mesh Shader support 26 | * [raytracing](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/raytracing) - Khronos Ray Tracing support 27 | 28 | Application class directories: 29 | * [vk](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/vk) - high level Vulkan integration such as VkInstance/VkDevice etc. 30 | * [app](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/app) - application level Vulkan classes 31 | * [platform](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/platform) - platform specific Windowing support 32 | * [ui](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/ui) - user interface classes such as mouse and keyboard events 33 | * [utils](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/utils) - collection of utilities such as intersections through to shader composition and compilation. 34 | * [threading](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/threading) - threading classes and helper functions 35 | 36 | The sections of the vsgTutorial are grouped in this way so that as you work through you'll be introduced to [Foundational](../foundations.md), [Scene Graph](../scenegraph.md) and [Application](../application.md) areas in turn. 37 | 38 | ## Naming conventions 39 | 40 | All VulkanSceneGraph projects use the following naming conventions: 41 | 42 | | **Project** | **Library** | **Namespace** | **CMake package**| 43 | | VulkanSceneGraph | vsg | vsg:: | find_package(vsg) | 44 | | vsgXchange | vsgXchange | vsgXchange:: | find_package(vsgXchange) | 45 | | vsgImGui | vsgImGui | vsgImGui:: | find_package(vsgImGui) | 46 | | vsgQt | vsgQt | vsgQt:: | find_package(vsgQt) | 47 | 48 | All Vulkan related classes in the VulkanSceneGraph follow the naming conventions VkFeatureName -> vsg::FeatureName, for example: 49 | 50 | | **Vulkan** | **VulkanSceneGraph** | 51 | | VkInstance | vsg::Instance | 52 | | VkPhysicalDevice | vsg::PhysicalDevice | 53 | | VkDevice | vsg::Device | 54 | | VkFramebuffer | vsg::Framebuffer | 55 | | VkSurface | vsg::Surface | 56 | 57 | The class and variable naming convention is predominantly CamelCase but there are cases where we use snake_case. If a function/template or struct mirrors a Standard C++ feature, then snake_case may be used. For the GLSL inspired math, support structs are used and have a leading lower case letter i.e. vsg::vec3 and vsg::mat4. 58 | 59 | ## Public/Protected/Private scope conventions 60 | 61 | Classes in the VulkanSceneGraph predominantly follow the declaration of scope in order of public interface through to internal implementation details. The naming of public member variables and methods follow camelCase while protected and private member variables and methods are prefixed with a _ so that their roles can be seen in the declaration and implementation. 62 | 63 | For the same reasons that headers and source files are kept in distinct `include` and `src` directories, this convention is used to make it clear to end users and to implementers that public facing variables and methods are special. As end users will directly use public members and methods, priority is given to maintaining consistency of their naming and types, while the protected and private members are implementation details that may be more subject to change. 64 | 65 | ~~~ cpp 66 | 67 | class MyClass 68 | { 69 | public: 70 | bool enabled = true; 71 | void publicMethod(); 72 | 73 | protected: 74 | int _count = 0; 75 | void _protectedMethod(); 76 | 77 | private: 78 | float _forDevelopersOnly = 1; 79 | }; 80 | ~~~ 81 | 82 | Where data members can vary independently those members are simply declared in the public scope and can be set directly, this mirrors Vulkan CreateInfo structs used to set up Vulkan objects, as well as keeping the usage simple and the code base clean and minimal. This coherence with Vulkan also means it's easier to reuse Vulkan documentation. If a class only has public scope we simply declare it as a struct. 83 | 84 | ~~~ cpp 85 | struct KeepItSimple 86 | { 87 | std::string name; 88 | float value = 0.0f; 89 | }; 90 | ~~~ 91 | 92 | For cases where data members have a dependency, a protected or private member is used and access is managed through public methods: 93 | 94 | ~~~ cpp 95 | class AnotherClass 96 | { 97 | public: 98 | void setValue(float v) { _value = v; ++_modifiedCount; } 99 | float getValue() const { return _value;} 100 | 101 | int getModifiedCount() const { return _modifiedCount; } 102 | 103 | protected: 104 | float _value = 0.0f; 105 | int _modifiedCount = 0; 106 | }; 107 | ~~~ 108 | 109 | Prev: [Ecosystem](Ecosystem.md) | Next: [Building the VulkanSceneGraph projects and vsgTutorial exercises](BuildingVulkanSceneGraph.md) 110 | -------------------------------------------------------------------------------- /1_SettingTheScene/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Setting the Scene 4 | permalink: /SettingTheScene/ 5 | --- 6 | 7 | **The first draft of this chapter is now complete. We'll make corrections and refine it over the coming weeks** 8 | 9 | This chapter introduces developers to the world of Vulkan, Scene Graphs, and how the VulkanSceneGraph combines these technologies to improve productivity and deliver high performance graphics and compute applications, takes you through downloading, building and installing the software and running the first exercise. The topic is broken down into the following sections: 10 | 11 | 1. [Low-level APIs](LowLevelAPIs.md) 12 | 1. [Vulkan](Vulkan.md) 13 | 1. [High-level APIs](HighLevelAPIs.md) 14 | 1. [Development Principles](DevelopmentPrinciples.md) 15 | 1. [Performance Principles](PerformancePrinciples.md) 16 | 1. [VulkanSceneGraph Ecosystem](Ecosystem.md) 17 | 1. [VulkanSceneGraph Library](VulkanSceneGraphLibrary.md) 18 | 1. [Building the VulkanSceneGraph projects](BuildingVulkanSceneGraph.md) 19 | 1. [Hello World](HelloWorld.md) 20 | 21 | With completion of this chapter you will be able run the Hello World application: 22 | 23 | ![](../images/hello_world.png) 24 | -------------------------------------------------------------------------------- /2_Foundations/2_PrintVisitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(PrintVisitor) 4 | 5 | # set the use of C++17 globally as all examples require it 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | find_package(vsg REQUIRED) 9 | 10 | add_executable(PrintVisitor PrintVisitor.cpp) 11 | 12 | target_link_libraries(PrintVisitor vsg::vsg) 13 | -------------------------------------------------------------------------------- /2_Foundations/2_PrintVisitor/PrintVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | int main(int, char**) 7 | { 8 | // implement simple print visitor that traverses the scene graph printing out node types 9 | struct PrintVisitor : public vsg::Inherit 10 | { 11 | size_t indent = 0; 12 | 13 | void apply(const vsg::Object& object) override 14 | { 15 | for(size_t i=0; iaddChild(vsg::vec3Value::create(1.0f, 2.0f, 3.0f)); 49 | 50 | auto nested = vsg::Objects::create(); 51 | nested->addChild(leaf); 52 | nested->addChild(vsg::doubleArray::create({4.0, 5.0, 6.0})); 53 | 54 | auto root = vsg::Objects::create(); 55 | root->addChild(vsg::stringValue::create("Everybody Loves Raymond")); 56 | root->addChild(nested); 57 | 58 | // construct our visitor and then pass it to root node to invoke the visitor. 59 | PrintVisitor print; 60 | root->accept(print); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /2_Foundations/2_observer_ptr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(observer_ptr) 4 | 5 | # set the use of C++17 globally as all examples require it 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | find_package(vsg REQUIRED) 9 | 10 | add_executable(observer_ptr observer_ptr.cpp) 11 | 12 | target_link_libraries(observer_ptr vsg::vsg) 13 | -------------------------------------------------------------------------------- /2_Foundations/2_observer_ptr/observer_ptr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int, char**) 6 | { 7 | auto scene = vsg::Object::create(); 8 | 9 | auto background_process = [](vsg::observer_ptr in_scene) 10 | { 11 | std::cout<<" Background thread : has started."<(scene)); 32 | 33 | for(int i=0; i<50; ++i) 34 | { 35 | std::cout<<"Main thread : scene = "< 2 | 3 | #include 4 | 5 | namespace astro 6 | { 7 | class Body : public vsg::Inherit 8 | { 9 | public: 10 | 11 | std::string name = "Universe"; 12 | double age = 13.4e9; // years 13 | 14 | int compare(const Object& rhs_object) const override 15 | { 16 | int result = Object::compare(rhs_object); 17 | if (result != 0) return result; 18 | 19 | auto& rhs = static_cast(rhs_object); 20 | if ((result = vsg::compare_value(age, rhs.age))) return result; 21 | return vsg::compare_value(name, rhs.name); 22 | } 23 | }; 24 | } 25 | EVSG_type_name(astro::Body) 26 | 27 | int main(int, char**) 28 | { 29 | // second constructed body object 30 | auto sun = astro::Body::create(); 31 | sun->name = "Sun"; 32 | sun->age = 5.603; // 5.603 billion years 33 | 34 | auto earth = astro::Body::create(); 35 | earth->name = "Earth"; 36 | earth->age = 4.543; // 4.543 billion years 37 | 38 | auto moon = astro::Body::create(); 39 | moon->name = "Moon"; 40 | moon->age = 4.51; // 4.51 billion years 41 | 42 | auto mars = astro::Body::create(); 43 | mars->name = "Mars"; 44 | mars->age = 4.603; // 4.603 billion years 45 | 46 | std::vector> bodies{sun, mars, earth, moon}; 47 | 48 | std::cout<<"Bodies before sorting"<setValue("name", "Apollo 11"); 64 | objects.push_back(spacecraft); 65 | 66 | // use the vsg::DereferenceLess functor which implements the < operator 67 | // that dereferences the ref_ptr<> and compares using the custom Object::compare(), 68 | std::sort(objects.begin(), objects.end(), vsg::DereferenceLess()); 69 | 70 | std::cout<<"Bodies after adding extra objects and sorting"< to ref_ptr 74 | // ref_ptr<>.cast() is implemented using the vsg::Object::cast<>() to efficiently replace a dynamic_cast<>. 75 | if (auto body = object.cast()) 76 | { 77 | std::cout<<" pointer = "< s_Register_Animal; 29 | 30 | int main(int, char**) 31 | { 32 | 33 | // create our animal object 34 | auto animal = nature::Animal::create(); 35 | animal->name = "Fido"; 36 | animal->age = 3.5; 37 | 38 | std::cout<<"animal = "<("animal.vsgt")) 45 | { 46 | std::cout<<"loaded_animal = "< 2 | #include 3 | 4 | int main(int, char**) 5 | { 6 | vsg::vec3 position = {1.0, 2.0, 3.0}; 7 | 8 | vsg::dquat rotation(vsg::radians(90.0), vsg::dvec3(0.0, 0.0, 1.0)); 9 | 10 | vsg::mat4 matrix = { 2.0, 0.0, 0.0, 0.0, 11 | 0.0, 2.0, 0.0, 0.0, 12 | 0.0, 0.0, 2.0, 0.0, 13 | 0.0, 0.0, 0.0, 1.0}; 14 | 15 | auto place = vsg::stringValue::create("Paris"); 16 | 17 | std::cout<<"position = "<; 30 | using PathObjects = std::map>; 31 | 32 | /// return path stripped of the filename or final path component. 33 | extern VSG_DECLSPEC Path filePath(const Path& path); 34 | 35 | /// return file extension including the . prefix, i.e. vsg::fileExtension("file.vsgt") returns .vsgt 36 | extern VSG_DECLSPEC Path fileExtension(const Path& path); 37 | 38 | /// return lower case file extension including the . prefix, i.e. vsg::fileExtension("file.VSGT") returns .vsgt 39 | /// By default prunes extras such as REST strings at the end of the extensions, uses ? as the deliminator for REST additions i.e. ".jpeg?g=42" becomes ".jpeg" 40 | extern VSG_DECLSPEC Path lowerCaseFileExtension(const Path& path, bool pruneExtras = true); 41 | 42 | /// return the filename stripped of any paths and extensions, i.e vsg::simpleFilname("path/file.vsgb") returns file 43 | extern VSG_DECLSPEC Path simpleFilename(const Path& path); 44 | 45 | /// return true if the path equals ., .. or has a trailing \.. \.., /.. or /.... 46 | extern VSG_DECLSPEC bool trailingRelativePath(const Path& path); 47 | 48 | /// return the path minus the extension, i.e. vsg::removeExtension("path/file.png") return path/file 49 | extern VSG_DECLSPEC Path removeExtension(const Path& path); 50 | ~~~ 51 | 52 | ## File system functions 53 | 54 | The file system functions are grouped together in the [include/vsg/io/FileSystem.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/FileSystem.h#L23) header. The file system functions from this header are: 55 | 56 | ~~~ cpp 57 | class Options; 58 | using Paths = std::vector; 59 | using PathObjects = std::map>; 60 | 61 | /// get the specified environmental variable. 62 | extern VSG_DECLSPEC std::string getEnv(const char* env_var); 63 | 64 | /// parse the specified environmental variable using platorm specific delimiter, returning list of Paths 65 | /// delimiter used is ; under Windows, and : on all other platforms. 66 | extern VSG_DECLSPEC Paths getEnvPaths(const char* env_var); 67 | 68 | /// parse multiple environmental variables, merging them to return a list of Paths. 69 | template 70 | Paths getEnvPaths(const char* env_var, Args... args) 71 | { 72 | auto paths = getEnvPaths(env_var); 73 | auto right_paths = getEnvPaths(args...); 74 | paths.insert(paths.end(), right_paths.begin(), right_paths.end()); 75 | return paths; 76 | } 77 | 78 | /// return file type, see include/vsg/io/Path.h for FileType enum, 79 | extern VSG_DECLSPEC FileType fileType(const Path& path); 80 | 81 | /// return true if a specified file/path exists on system. 82 | extern VSG_DECLSPEC bool fileExists(const Path& path); 83 | 84 | /// return the full filename path if specified filename can be found in the list of paths. 85 | extern VSG_DECLSPEC Path findFile(const Path& filename, const Paths& paths); 86 | 87 | /// return the full filename path if specified filename can be found in the options->paths list. 88 | /// If options is null and the filename can be found using its existing path then filename is returned, otherwise empty Path{} is returned. 89 | extern VSG_DECLSPEC Path findFile(const Path& filename, const Options* options); 90 | 91 | /// make a directory, return true if path already exists or full path has been created successfully, return false on failure. 92 | extern VSG_DECLSPEC bool makeDirectory(const Path& path); 93 | 94 | /// get the contents of a directory, return {} if directory name is not a directory 95 | extern VSG_DECLSPEC Paths getDirectoryContents(const Path& directoryName); 96 | 97 | /// returns the path/filename of the currently executed program. 98 | extern VSG_DECLSPEC Path executableFilePath(); 99 | 100 | /// Open a file using a the C style fopen() adapted to work with the vsg::Path. 101 | extern VSG_DECLSPEC FILE* fopen(const Path& path, const char* mode); 102 | ~~~ 103 | 104 | Prev: [Serialization](Serialization.md) | Next: [Streams & Logger](StreamsAndLogger.md) 105 | 106 | -------------------------------------------------------------------------------- /2_Foundations/MathFunctions.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Maths Functions 4 | permalink: /foundations/MathsFunctions 5 | --- 6 | 7 | The VulkanSceneGraph's maths types and functions are found in the [include/vsg/maths](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/) directory. The conventions broadly follow GLSL conventions with additions that are helpful for scene graph usage. In the [Math Types](MathTypes.md) section, earlier in this chapter, the vec2, vec3, vec4, mat3, mat4, quat, plane, box and sphere classes were introduced. In this section we build upon this with a tour of the range of helper functions available and an explanation of the conventions used. 8 | 9 | ## Conventions 10 | 11 | The VulkanSceneGraph follows the GLSL convention of [Right Hand Rule](https://en.wikipedia.org/wiki/Right-hand_rule) and [Column Major](https://en.wikipedia.org/wiki/Row-_and_column-major_order) matrices. The GLSL conventions are followed to make it easier to move code between shaders and C++ code, and to make it easier to follow 3rd party coding examples. 12 | 13 | ## Headers and associated types/functionality 14 | 15 | | header | types & functions | 16 | | --- | --- | 17 | | *vector and quaternion* | | 18 | | [include/vsg/maths/vec2.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec2.h) | vsg::vec2 ==, != <, +, -, *, /, dot, cross, length, length2, mix & normalize | 19 | | [include/vsg/maths/vec3.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec3.h) | vsg::vec3 ==, != <, +, -, *, /, dot, cross, length, length2, mix & normalize | 20 | | [include/vsg/maths/vec4.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec4.h) | vsg::vec4 ==, != <, +, -, *, /, dot, cross, length, length2, mix & normalize | 21 | | [include/vsg/maths/quat.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/quat.h) | vsg::quat ==, != <, +, -, *, /, dot, cross, length, mix, normalize, conjugate & inverse | 22 | | *matrix types* | | 23 | | [include/vsg/maths/mat2.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat2.h) | vsg::mat2 | 24 | | [include/vsg/maths/mat3.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat3.h) | vsg::mat3 | 25 | | [include/vsg/maths/mat4.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat4.h) | vsg::mat3 | 26 | | *geometric primitive types* | | 27 | | [include/vsg/maths/plane.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/plane.h) | vsg::plane | 28 | | [include/vsg/maths/sphere.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/sphere.h) | vsg::sphere | 29 | | [include/vsg/maths/box.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/box.h) | vsg::box | 30 | | *functions* | | 31 | | [include/vsg/maths/clamp.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/clamp.h) | clamp and repeat texture coord style functions | 32 | | [include/vsg/maths/color.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/color.h) | color helper functions | 33 | | [include/vsg/maths/common.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/common.h) | angle, square, smoothstep and mix helper functions | 34 | | [include/vsg/maths/sample.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/sample.h) | sample function equvilant to GPU texture sampler | 35 | | [include/vsg/maths/transform.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/transform.h) | range of transform related functions | 36 | 37 | Matrix and vector multiplication: 38 | 39 | ~~~ cpp 40 | vsg::dmat4 projection; // typically camera will provide projection matrix 41 | vsg::dmat4 view; // typically camera will provide view matrix 42 | vsg::dmat4 model; // typically transforms in scene graph will be accumulated into model matrix 43 | vsg::dvec3 object_coord; // typically provided by vertices in scene graph 44 | 45 | vsg::dmat4 modelview = view * model; 46 | vsg::dvec3 eye_coord = modelview * object_coord; // implicit transpose of object_coord vector 47 | vsg::dvec3 clip_coord = projection * modelview * object_coord; 48 | ~~~ 49 | 50 | Dot and cross products: 51 | 52 | ~~~ cpp 53 | vsg::vec3 a(1.0f, 0.0f, 0.0f); 54 | vsg::vec3 b(0.0f, 1.0f, 0.0f); 55 | vsg::vec3 c = vsg::cross(a, b); 56 | float d = vsg::dot(a, b); 57 | ~~~ 58 | 59 | Transform related functions: 60 | 61 | ~~~ cpp 62 | vsg::dmat4 matrix = vsg::translate(x, y, z) * 63 | vsg::rotate(angle_radians, rx, ry, rz) * 64 | vsg::scale(sx, sy, sz); 65 | ~~~ 66 | 67 | ## Difference with OpenSceneGraph conventions 68 | 69 | The use of column major contrasts that of the OpenSceneGraph that uses row major, this not only affects order of access for elements of matrices but also the transformation order you'll use. The row major convention used by the OpenSceneGraph was adopted prior to the existence of GLSL which unfortunately chose the opposite convention, leaving the OpenSceneGraph in an awkward situation where C++ and shaders follow differing conventions. 70 | 71 | ~~~ cpp 72 | // OpenSceneGraph uses row major 73 | osg::Vec3d osg_vec; 74 | osg::Matrixd osg_matrix; 75 | osg_matrix(row, column) = value; 76 | osg::Vec3d osg_dash = osg_vec * matrix; 77 | 78 | // VulkanSceneGraph using GLSL column major convention 79 | vsg::dvec3 vsg_vec; 80 | vsg::dmat4 vsg_matrix; 81 | vsg_matrix(column, row) = value; 82 | vsg::dvec3 vsg_dash = matrix * vsg_vec 83 | ~~~ 84 | 85 | The OpenSceneGraph also implements the dot product and cross product functions as * and ^ operators while the VulkanSceneGraph follows GLSL's convention of using [dot](https://registry.khronos.org/OpenGL-Refpages/gl4/html/dot.xhtml) and [cross](https://registry.khronos.org/OpenGL-Refpages/gl4/html/cross.xhtml) functions. 86 | 87 | --- 88 | 89 | Prev: [Math Types](MathTypes.md) | Next: [Data Types](DataTypes.md) 90 | -------------------------------------------------------------------------------- /2_Foundations/MathTypes.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Maths Types 4 | permalink: /foundations/MathsTypes 5 | --- 6 | 7 | The VulkanSceneGraph provides GLSL style vector, quaternion and matrix types which are used to represent data, they can be used both to store data on the CPU and directly mapped to GPU memory for use by shaders. A range of standard vector [Math Functions](MathFunctions.md) are provided that we'll discuss later in this chapter, this page will discuss the data types. 8 | 9 | The math data types are found in the [include/vsg/maths](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/) directory. All the types are declared as template<> structs with definitions provided for the specific types, supporting bool, 8, 16 and 32 bit int and unsigned ints, float and double versions of each type. 10 | 11 | Simple structs are used, only containing the data fields required for the type. They are not subclassed from vsg::Object like other scene graph objects as they focus on representing low level data and supporting maths operations. All math types can be used as part of data objects which we will cover in the next page - [Data Types](DataTypes.md). 12 | 13 | ## Available types and the associated headers 14 | 15 | Vector & quaternion: 16 | * vsg::vec2 [include/vsg/maths/vec2.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec2.h) 17 | * vsg::vec3 [include/vsg/maths/vec3.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec3.h) 18 | * vsg::vec4 [include/vsg/maths/vec4.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec4.h) 19 | * vsg::quat [include/vsg/maths/quat.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/quat.h) 20 | 21 | Matrix types: 22 | * vsg::mat2 [include/vsg/maths/mat2.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat2.h) 23 | * vsg::mat3 [include/vsg/maths/mat3.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat3.h) 24 | * vsg::mat4 [include/vsg/maths/mat4.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/mat4.h) 25 | 26 | Geometric primitive types: 27 | * vsg::plane [include/vsg/maths/plane.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/plane.h) 28 | * vsg::sphere [include/vsg/maths/sphere.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/sphere.h) 29 | * vsg::box [include/vsg/maths/box.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/box.h) 30 | 31 | ## Prefix naming convention 32 | 33 | The prefix of the type describes the numerical type, the mappings are: 34 | 35 | | Prefix | Description | Type | Example | 36 | | no prefix | 32 bit floating point | float | vsg::vec3 | 37 | | d | 64 bit floating point | double | vsg::dmat4 | 38 | | b | signed byte | std::int8_t | vsg::bvec4 | 39 | | ub | unsigned byte | std::uint8_t | vsg::ubvec4 | 40 | | s | signed short | std::int16_t | vsg::svec2 | 41 | | us | unsigned short | std::uint16_t | vsg::usvec2 | 42 | | i | signed int | std::int32_t | vsg::ivec3 | 43 | | ui | unsigned int | std::uint32_t | vsg::uivec3 | 44 | 45 | ## Vectors 46 | 47 | The [vsg::vec2](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec2.h), [vec3](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec3.h), [vec4](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/maths/vec4.h) types provide GLSL style access, with {x,y,z,w}, {r,g,b,a}, {s,t,p,q} and [] accessors which all map to the same underlying numerical values. All the vector types also support set(..) methods and assignment. The vector types have a range of uses and the accessors used support these: 48 | 49 | ~~~ cpp 50 | // double precision position, good for GIS on CPU 51 | vsg::dvec3 position{1.0, 2.0, 3.0); 52 | position.x = 1.0; // equivalent to position[0] = 1.0 53 | position.y = 2.0; // equivalent to position[1] = 2.0 54 | 55 | // float vertex, good for GPU work 56 | vsg::vec3 vertex{1.0f, 2.0f, 3.0f); 57 | vertex.x = 1.0f; // equivalent to position[0] = 1.0f 58 | vertex[1] = 2.0f; // equivalent to position.y = 2.0f 59 | 60 | // float normals 61 | vsg::vec3 normal(0.0f, 0.0f, 1.0f); // floating point vec3 62 | normal.set(0.0f, 1.0f, 0.0f); 63 | 64 | // colours 65 | vsg::vec4 color(1.0f, 0.0f, 0.0f, 1.0f); // float vec4 representing opaque red 66 | color.r *= 0.5f; // half the red intensity 67 | color[3] = 1.0f; // set the a channel to 1.0 68 | 69 | vsg::ubvec4 packed_color(0, 255, 0, 127); // unsigned byte semi-transparent green 70 | packed_color = vsg::ubvec4(255, 255, 255, 255); // assign an all white color 71 | 72 | // you can use .x, .r & [0] etc. access interchangeably 73 | std::cout<<"color : red = "< polytope = { 131 | {1.0, 0.0, 0.0, 1.0}, // left plane at x=-1, pointing right 132 | {-1.0, 0.0, 0.0, 1.0}, // right plane at x=1, pointing left 133 | {0.0, 1.0, 0.0, 1.0}, // front plane at y=-1, pointing forward 134 | {0.0, -1.0, 0.0, 1.0}, // back plane at y=1, pointing backward 135 | {0.0, 0.0, 1.0, 1.0}, // bottom plane at z=-1, pointing upwards 136 | {0.0, 0.0, -1.0, 1.0} // top plane at z=1, pointing downwards 137 | }; 138 | 139 | // double precision sphere at {10, 20, 30} with radius of 40 units 140 | vsg::dsphere bounding_sphere(10.0, 20.0, 30.0, 40.0); 141 | 142 | // default constructed single precision box representing an undefined box 143 | // bounding_box.min is set to maximum float values. 144 | // bounding_box.max is set to minimum float values. 145 | // when min.x value > max.x then box is treated as undefined/invalid/empty. 146 | vsg::box bounding_box; 147 | 148 | // use the vsg::box::add(..) method to compute the bounding box that encloses points 149 | bounding_box.add(vsg::vec3(0.0f, 0.0f, 0.0f)); 150 | bounding_box.add(vsg::vec3(10.0f, 0.0f, 0.0f)); 151 | bounding_box.add(vsg::vec3(0.0f, 5.0f, 0.0f)); 152 | bounding_box.add(vsg::vec3(0.0f, 6.0f, 3.0f)); 153 | 154 | std::cout<<"bounding_box min = ("< object); 18 | 19 | /// get Object pointer associated with key, return nullptr if no object associated with key has been assigned 20 | Object* getObject(const std::string& key); 21 | 22 | /// get const Object pointer associated with key, return nullptr if no object associated with key has been assigned 23 | const Object* getObject(const std::string& key) const; 24 | 25 | /// get object pointer of specified type associated with key, return nullptr if no object associated with key has been assigned 26 | template 27 | T* getObject(const std::string& key) { return dynamic_cast(getObject(key)); } 28 | 29 | /// get const object pointer of specified type associated with key, return nullptr if no object associated with key has been assigned 30 | template 31 | const T* getObject(const std::string& key) const { return dynamic_cast(getObject(key)); } 32 | 33 | /// get ref_ptr associated with key, return nullptr if no object associated with key has been assigned 34 | ref_ptr getRefObject(const std::string& key); 35 | 36 | /// get ref_ptr pointer associated with key, return nullptr if no object associated with key has been assigned 37 | ref_ptr getRefObject(const std::string& key) const; 38 | 39 | /// get ref_ptr of specified type associated with key, return nullptr if no object associated with key has been assigned 40 | template 41 | ref_ptr getRefObject(const std::string& key) { return getRefObject(key).cast(); } 42 | 43 | /// get ref_ptr of specified type associated with key, return nullptr if no object associated with key has been assigned 44 | template 45 | const ref_ptr getRefObject(const std::string& key) const { return getRefObject(key).cast(); } 46 | 47 | /// remove meta object or value associated with key 48 | void removeObject(const std::string& key); 49 | ~~~ 50 | 51 | The vsg::Object class implements these methods using a [vsg::Auxiliary](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/core/Auxiliary.h) object that provides both observer_ptr<> support and the std::map> that holds the user assigned objects. The vsg::Auxiliary object is only created and assigned to a vsg::Object when an observer_ptr<> and/or Metadata are required. As most scene graph objects don't require either, most objects will just have a null Auxiliary pointer. 52 | 53 | ## Setting and getting named values 54 | 55 | To provide support for standard C++ types like std::string, float and simple scene graph types like vsg::vec4, vsg::Object provides template setValue() and getValue() methods: 56 | 57 | ~~~ cpp 58 | /// meta data access methods 59 | /// wraps the value with a vsg::Value object and then assigns via setObject(key, vsg::Value) 60 | template 61 | void setValue(const std::string& key, const T& value); 62 | 63 | /// specialization of setValue to handle passing c strings 64 | void setValue(const std::string& key, const char* value) { setValue(key, value ? std::string(value) : std::string()); } 65 | 66 | /// get specified value type, return false if value associated with key is not assigned or is not the correct type 67 | template 68 | bool getValue(const std::string& key, T& value) const; 69 | ~~~ 70 | 71 | These setValue(key, value)/getValue(key) methods build upon the setObject(key, value)/getObject(key) functionality using the vsg::Value<> template data class as an adapter. This adaptation is done for users so they can just focus on the basic types they wish to use. Object::getValue(key, value) returns true when the object of the matching Value<> is found in the Auxiliary::userObjects map, otherwise it returns false. It is important to exactly match the types between setValue(key, value) and getValue(key, value) as no implicit type conversion is supported, i.e. setting with a float then attempting to get with a double will not find a match and return false. 72 | 73 | ~~~ cpp 74 | { 75 | float value = 1.0f; 76 | object->setValue("value", value); 77 | } 78 | 79 | { 80 | float float_value; 81 | if (object->getValue("value", float_value)) 82 | { 83 | std::cout<<"found float value = "<getValue("value", double_value)) 92 | { 93 | std::cout<<"found double value = "<getRefObject("value"); 106 | std::cout<<"object = "<getRefObject("value")) 110 | { 111 | std::cout<<"value = "<value()<setValue("name", "Adrian Mole"); 123 | object->setValue("age", 13.75); 124 | 125 | if (auto auxiliary = object->getAuxiliary()) 126 | { 127 | for(auto& [key, object] : auxiliary->userObjects) 128 | { 129 | if (auto s = dynamic_cast(object.get())) std::cout<<"metadata key = "< between types. 141 | 142 | Prev: [Data Types](DataTypes.md)| Next: [Run-Time Type Information (RTTI)](RTTI.md) 143 | -------------------------------------------------------------------------------- /2_Foundations/Object_base_class_and_ref_ptr.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: vsg::ref_ptr<> & vsg::Object base class 4 | permalink: /foundations/BaseClassesAndSmartPointers 5 | --- 6 | 7 | To provide robust, thread-safe, high performance memory management the VulkanSceneGraph uses intrusive reference counting and block memory allocation. The three main classes that provide this functionality are the vsg::Object base class, the vsg::ref_ptr<> smart pointer class and the vsg::Allocator singleton. The vsg::Auxiliary class and vsg::observer_ptr<> smart pointer provide additional meta data and weak pointer functionality. In this section we'll cover the vsg::Object base class and the vsg::ref_ptr<> smart pointer and then cover the vsg::Auxiliary, vsg::observer_ptr<> and vsg::Allocator in the following two sections. 8 | 9 | ## Intrusive vs non-intrusive reference counting 10 | 11 | Reference counting is widely used in applications to facilitate robust sharing of objects allocated on the heap. The two main approaches used in C++ applications are intrusive and non-intrusive reference counting, each have strengths and weaknesses. 12 | 13 | Standard C++ provides the std::shared_ptr<> smart pointer that uses non-intrusive counting. The design requires the shared_ptr<> to hold two pointers, one to the object being managed and one to a shared reference count. The advantage of non intrusive reference counting is that it can be used with all types, from bool to std::vector to user classes. The disadvantage of the shared_ptr<> is that it's twice the size of a C pointer which has significant performance consequences which we'll discuss in detail below. 14 | 15 | With intrusive reference counting the count is placed into the object, in the case of the VulkanSceneGraph this is provided by the vsg::Object base classes atomic _referenceCount member variable which is accessed via the ref() and unref() methods that increment and decrement the count and when the count goes to zero the object is automatically deleted. To ensure that the ref() and unref() methods are called consistently the vsg::ref_ptr<> smart pointer is provided, similar in role to the std::shared_ptr<>, but having the advantage that it only requires a single C pointer so is the same size as a C pointer, and half the memory footprint of the std::shared_ptr<>. The disadvantage with intrusive reference counting is that you can not use it directly with types like bool etc. 16 | 17 | For the case of a scene graph we have a data structure where the internal nodes of the graph are primarily pointers to data objects or other nodes in the scene graph, if you double the size of the pointer you almost double the size of internal nodes in the graph. Increasing the size of the nodes means you require more memory and crucially can fit less nodes into cache which means more cache misses and lower CPU utilization. Benchmarking done comparing the traversal speeds of a scene graph using std::shared_ptr<> vs one with vsg::ref_ptr<> shows that the intrusive reference counted scene graph is 15% faster. 18 | 19 | ## Creating objects and smart pointers 20 | 21 | The standard C++ shared_ptr<> declaration is in the form: 22 | 23 | ~~~ cpp 24 | struct MyClass 25 | { 26 | MyClass(const std::string& in_name) : name(in_name) {} 27 | 28 | std::string name; 29 | double value = 0.0; 30 | }; 31 | 32 | // allocates a MyClass object on the heap and assigns to std::shared_ptr 33 | auto ptr = std::make_shared("fred"); 34 | ~~~ 35 | 36 | The equivalent with the VulkanSceneGraph requires MyClass to be subclassed from vsg::Object to add the intrusive reference counting: 37 | 38 | ~~~ cpp 39 | struct MyClass : public vsg::Object 40 | { 41 | MyClass(const std::string& in_name) : name(in_name) {} 42 | 43 | std::string name; 44 | double value = 0.0; 45 | }; 46 | 47 | // allocates a MyClass object on the heap and assigns to vsg::ref_ptr 48 | vsg::ref_ptr ptr(new MyClass("ginger")); 49 | ~~~ 50 | 51 | The VulkanSceneGraph has another feature that makes it even cleaner to allocate objects robustly and add RTTI features - the vsg::Inherit<> template class. vsg::Inherit is an example of the [Curiously Recurring Template Pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern), while a somewhat non-intuitive idiom it neatly solves a problem of how to implement class specific extensions to a base class in a consistent and robust way. 52 | 53 | We'll cover more of these features of vsg::Object and vsg::Inherit later in the tutorial, for now we'll just focus on the benefits for conveniently allocating objects. With the following revised code we leverage the create() method provided by vsg::Inherit<> that allocates the memory and calls the constructor of the object using the parameters you pass to create(..) and returns a vsg::ref_ptr<> of the appropriate type. Usage is simply: 54 | 55 | ~~~ cpp 56 | struct MyClass : public vsg::Inherit 57 | { 58 | MyClass(const std::string& in_name) : name(in_name) {} 59 | 60 | std::string name; 61 | double value = 0.0; 62 | }; 63 | 64 | // allocates a MyClass object on the heap, using vsg::Allocator, and assigns to vsg::ref_ptr 65 | auto ptr = MyClass::create("ginger"); 66 | ~~~ 67 | 68 | While the declaration of the MyClass is a little more complicated the benefit is cleaner and more expressive object creation syntax, that requires less code to type than either of the ref_ptr(new T()) and std::make_shared() usage. The use of the T::create(..) method is used throughout the VulkanSceneGraph codebase - it doesn't just make it more convenient for end users, it makes life easier for the developers of software as well. 69 | 70 | ## The strengths of smart pointers 71 | 72 | The main reason for smart pointers is to make it easier to develop robust applications, and in the case of the vsg::ref_ptr<>/vsg::Object combination there is no memory or performance overhead over using C pointers except for specific usage cases. The following are a few examples of how smart pointers lead to cleaner and more robust code. 73 | 74 | Addressing memory leaks: 75 | 76 | ~~~ cpp 77 | // 1. Using C pointers 78 | { 79 | MyClass* c_ptr = new MyClass; 80 | 81 | // 82 | // do the processing we want 83 | // 84 | 85 | delete c_ptr; // need to explicitly delete object 86 | } 87 | 88 | // 2. Using C pointers and explicit ref/unref calls 89 | { 90 | MyClass* c_ptr = new MyClass; 91 | c_ptr->ref(); // increment referenceCount to 1 92 | 93 | // 94 | // do the processing we want 95 | // 96 | 97 | c_ptr->unref(); // decrement referenceCount to 0 and delete MyClass 98 | } 99 | 100 | // 3. Using ref_ptr<> 101 | { 102 | auto ptr = MyClass::create(); // allocate MyClass on heap, assign to ref_ptr which increments its referenceCount. 103 | 104 | // 105 | // do the processing we want 106 | // 107 | 108 | } // when ptr destructs it automatically decrements its referenceCount which hits 0 and leads to the object being deleted. 109 | ~~~ 110 | 111 | Cases 1 & 2 are fine as long as the code blocks never exit prematurely, but what happens if there is an early return or an exception thrown in the processing section? It will leak the object allocated on the heap. With case 3 using ref_ptr<> an early return from the block will always invoke the ref_ptr<> destructor and always clean up the memory associated with it - the code isn't just simpler it's far more robust as well. 112 | 113 | Reference counting also helps when passing back objects out from the scope of a code block: 114 | 115 | ~~~ cpp 116 | // 4. Using C pointers can lead to dangling pointers 117 | MyClass* other_ptr = nullptr; 118 | { 119 | MyClass* c_ptr = new MyClass; 120 | 121 | // 122 | // do the processing we want 123 | // 124 | 125 | // take a copy of the pointer 126 | other_ptr = c_ptr; 127 | 128 | delete c_ptr; // explicitly delete object, cleans up memory but causes a dangling pointer 129 | } 130 | other_ptr->value = 10.0; // seg fault as the object has already been deleted 131 | 132 | // 5. Using C pointers using explicit ref/unref calls 133 | MyClass* other_ptr = nullptr; 134 | { 135 | MyClass* c_ptr = new MyClass; 136 | c_ptr->ref(); // increment referenceCount to 1 137 | 138 | // 139 | // do the processing we want 140 | // 141 | 142 | other_ptr = c_ptr; 143 | other_ptr->ref(); // increment referenceCount to 2 144 | 145 | c_ptr->unref(); // decrement referenceCount to 1 so no deletion! 146 | } 147 | other_ptr->value = 10.0; // assignment safe as object is still on the heap 148 | other_ptr->unref(); // decrement referenceCount to 0 and delete MyClass 149 | 150 | // 6. Using ref_ptr<> 151 | vsg::ref_ptr other_ptr; 152 | { 153 | auto ptr = MyClass::create(); // allocate MyClass on heap, assign to ref_ptr which increments its referenceCount to 1. 154 | 155 | // 156 | // do the processing we want 157 | // 158 | 159 | other_ptr = ptr; // smart pointer assignment automatically increases reference count to 2 160 | 161 | } // when ptr destructs it automatically decrements its referenceCount which hits 1, no deletion! 162 | other_ptr->value = 10.0; // assignment safe as object is still on the heap 163 | ~~~ 164 | 165 | In case 4 it is possible to fix the seg fault by moving the delete to after the last use of other_ptr, however if the processing section returns early or throws an exception the memory will leak. Case 5 will work correctly as long as the processing section doesn't return early or throws an exception in which case it will leak the allocated object. Again case 6 is both cleaner and handles the early return case correctly, cleaning up any objects that have been allocated and no longer have an external reference. 166 | 167 | These examples illustrate why smart pointers are so useful and why you'll find them used throughout the VulkanSceneGraph codebase and applications that use it: 168 | 1. Less code to write 169 | 2. More expressive code 170 | 3. More robust code 171 | 172 | ## Don't mix delete, std::shared_ptr<> & std::ref_ptr<> 173 | 174 | The code examples above implement MyClass by subclassing from vsg::Inherit<> which makes it possible to seamlessly use MyClass::create() and ref_ptr<>, but it's possible also to write and compile code that still uses std::shared_ptr<>. We strongly recommend against doing so as you create a situation where there are two independent reference counting mechanisms attempting to manage a single object. 175 | 176 | ~~~ cpp 177 | struct MyClass : public vsg::Inherit 178 | { 179 | MyClass(const std::string& in_name) : name(in_name); 180 | std::string name; 181 | double value = 1.0; 182 | }; 183 | 184 | auto vsg_ptr = MyClass::create("carrie"); 185 | 186 | // will compile but create dangling pointers once either vsg_ptr or std_ptr goes out of scope 187 | std::shared_ptr std_ptr(vsg_ptr.ptr()); 188 | 189 | // we could even just delete the object directly and mess up both vsg_ptr and std_ptr. 190 | delete vsg_ptr.get(); 191 | ~~~ 192 | 193 | The way to prevent this misuse is to use a protected or private destructor when the object is always meant to be allocated on the heap. 194 | 195 | ~~~ cpp 196 | class MyClass : public vsg::Inherit 197 | { 198 | public: 199 | MyClass(const std::string& in_name) : name(in_name); 200 | std::string name; 201 | double value = 1.0; 202 | protected: 203 | virtual ~MyClass() {} // hide the destructor from shared_ptr<> and explicit deletion. 204 | }; 205 | 206 | auto vsg_ptr = MyClass::create("carrie"); 207 | 208 | // will no longer compile 209 | std::shared_ptr std_ptr(vsg_ptr.ptr()); 210 | 211 | // will no longer compile 212 | delete vsg_ptr.get(); 213 | ~~~ 214 | 215 | The VulkanSceneGraph uses this pattern throughout the codebase so when you see the destructor declared in the protected or private section of the class you know that instances of that class are meant to be only declared on the heap and meant to be used with vsg::ref_ptr<>. The T::create() support provided by vsg::Inherit<> achieves both these requirements. 216 | 217 | ## Don't mix stack allocation and reference counting 218 | 219 | Another potential issue when using smart pointers and reference counting is when objects are allocated on the stack rather than on the heap. Stack allocation happens automatically for variables within a scope and all the allocated objects are automatically destructed at the end of the scope. The examples using std::shared_ptr<> and vsg::ref_ptr<> leverage this behavior, using the destruction of the smart pointers to unreference the objects they have shared ownership of. The problem occurs if a user allocates objects on the stack and then attempts to reference count them as well. The following example illustrates this: 220 | 221 | ~~~ cpp 222 | 223 | class MyClass : public vsg::Inherit 224 | { 225 | public: 226 | MyClass(const std::string& in_name) : name(in_name); 227 | std::string name; 228 | double value = 1.0; 229 | }; 230 | 231 | vsg::ref_ptr ptr; 232 | { 233 | MyClass object("carrie"); // object created on the stack in local scope 234 | 235 | // assign object to the ref_ptr<> that increments its ref count to 1. 236 | ptr = &object; 237 | } // object is destructed automatically because it was allocated on stack, it doesn't matter what the ref count is. 238 | 239 | ptr->value += 10.0; // seg fault as object was deleted on exiting its scope 240 | ~~~ 241 | 242 | This same issue occurs for std::shared_ptr<>, you simply can't prevent the destruction of objects on the stack. If you want to manage your objects using smart pointers you must only use them with objects allocated on the heap. Thankfully the same technique of declaring the destructor protected/private that works to prevent use with shared_ptr<> and explicitly deleting an object works to prevent stack construction as well. 243 | ~~~ cpp 244 | class MyClass : public vsg::Inherit 245 | { 246 | public: 247 | MyClass(const std::string& in_name) : name(in_name); 248 | std::string name; 249 | double value = 1.0; 250 | protected: 251 | virtual ~MyClass() {} 252 | }; 253 | 254 | { 255 | // will no longer compile 256 | MyClass object("carrie"); 257 | } 258 | ~~~ 259 | 260 | Most classes in the VulkanSceneGraph are declared with a protected destructor to prevent this problem usage, but there are a couple of classes like subclasses from vsg::Visitor that for convenience and efficiency may be fine to allocate on the stack and let the automatic destruction clean up the objects without needing to allocate on the heap and use smart pointers. For these special cases developers may decide to not declare a protected destructor, but they should be wary of the potential pitfalls in doing this. Later in this chapter we will discuss visitor classes in detail and touch upon the time when stack vs heap allocation will be preferable. 261 | 262 | Prev: [Foundations](index.md)| Next: [vsg::observer_ptr<>](observer_ptr.md) 263 | 264 | -------------------------------------------------------------------------------- /2_Foundations/RTTI.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Run-Time Type Information (RTTI) 4 | permalink: /foundations/RTTI 5 | --- 6 | 7 | The VulkanSceneGraph provides a number of features that provide richer and more efficient RunTime Type Information (RTTI) and type safe operations than are provided by C++ itself. These features are provided by the vsg::Object base class and by two companion base classes, the vsg::Visitor and vsg::ConstVisitor, with the vsg::Inherit CRTP class providing convenient implementations of the required methods. In this section we'll focus on the RTTI features provided by vsg::Object/vsg::Inherit. 8 | 9 | ## RTTI features provided by vsg::Object 10 | 11 | The vsg::Object base class provides the following methods dedicated to RTTI: 12 | 13 | ~~~ cpp 14 | virtual const char* className() const noexcept { return type_name(); } 15 | 16 | /// return the std::type_info of this Object 17 | virtual const std::type_info& type_info() const noexcept { return typeid(Object); } 18 | virtual bool is_compatible(const std::type_info& type) const noexcept { return typeid(Object) == type; } 19 | 20 | template 21 | T* cast() { return is_compatible(typeid(T)) ? static_cast(this) : nullptr; } 22 | 23 | template 24 | const T* cast() const { return is_compatible(typeid(T)) ? static_cast(this) : nullptr; } 25 | 26 | /// compare two objects, return -1 if this object is less than rhs, return 0 if it's equal, return 1 if rhs is greater, 27 | virtual int compare(const Object& rhs) const; 28 | ~~~ 29 | 30 | The vsg::Object::className() method is implemented using the vsg::type_name<> template function, specializations of vsg::type_name<> are in turn provided by the VSG_type_name() and EVSG_type_name() macro functions that can be placed before/after a class definition, both of these features are defined in [include/vsg/core/value_type.h](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/type_name.h). The VSG_type_name() macro can be used for classes within the vsg namespace like [vsg::Group](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/nodes/group.h), while the EVSG_type_name() version can be used for classes defined in other namespaces, such as what you see in [vsgXchange](https://github.com/vsg-dev/vsgXchange/blob/master/include/vsgXchange/all.h#L43). 31 | 32 | The vsg::Object::type_info() method provides a convenient way to access the std::type_info of a particular object, and vsg::Object::is_compatible(const std::type_info&) method provides a method that can not just check whether a type is the same, but whether it may be derived from that type and thus compatible with treatment as that type. The vsg::Inherit<> class can be used to automatically implement the required type_info() and is_compatible() methods. 33 | 34 | The vsg::Object::cast<>() template methods use the Object::is_compatible() method to decide whether one can directly cast to a desired type using static_cast<> without the high CPU overhead of invoking dynamic_cast<>. 35 | 36 | The vsg::Object::compare(..) method provides a way of comparing two objects, both the type and the contents of the object. The int std::memcmp(..) convention is used, with negative for AB. The vsg::Inherit<> class provides a very basic compare(..) implementation but it's recommended to implement this locally for any class that holds anything more than simple types. The [include/vsg/core/compare.h](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/compare.h) header provides a range of convenience template functions to make the task easier. 37 | 38 | To illustrate these features, with the [RTTI example](https://github.com/vsg-dev/vsgTutorial/blob/master/2_Foundations/2_rtti/), we'll declare a custom class in its own namespace and use Inherit to implement the RTTI methods, EVSG_type_name to provide the human readable naming and implement the compare() method. 39 | 40 | ~~~ cpp 41 | namespace astro 42 | { 43 | class Body : public vsg::Inherit 44 | { 45 | public: 46 | 47 | std::string name = "Universe"; 48 | double age = 13.4e9; // years 49 | 50 | int compare(const Object& rhs_object) const override 51 | { 52 | int result = Object::compare(rhs_object); 53 | if (result != 0) return result; 54 | 55 | auto& rhs = static_cast(rhs_object); 56 | if ((result = vsg::compare_value(age, rhs.age))) return result; 57 | return vsg::compare_value(name, rhs.name); 58 | } 59 | }; 60 | } 61 | EVSG_type_name(astro::Body) 62 | ~~~ 63 | 64 | We can then use this functionality in application code, first we create our main objects, assign them to a vector of ref_ptr and print them out: 65 | 66 | ~~~ cpp 67 | // second constructed body object 68 | auto sun = astro::Body::create(); 69 | sun->name = "Sun"; 70 | sun->age = 5.603; // 5.603 billion years 71 | 72 | auto earth = astro::Body::create(); 73 | earth->name = "Earth"; 74 | earth->age = 4.543; // 4.543 billion years 75 | 76 | auto moon = astro::Body::create(); 77 | moon->name = "Moon"; 78 | moon->age = 4.51; // 4.51 billion years 79 | 80 | auto mars = astro::Body::create(); 81 | mars->name = "Mars"; 82 | mars->age = 4.603; // 4.603 billion years 83 | 84 | std::vector> bodies{sun, mars, earth, moon}; 85 | 86 | std::cout<<"Bodies before sorting"<setValue("name", "Apollo 11"); 115 | objects.push_back(spacecraft); 116 | 117 | // use the vsg::DereferenceLess functor which implements the < operator 118 | // that dereferences the ref_ptr<> and compares using the custom Object::compare(), 119 | std::sort(objects.begin(), objects.end(), vsg::DereferenceLess()); 120 | 121 | std::cout<<"Bodies after adding extra objects and sorting"< to ref_ptr 125 | // ref_ptr<>.cast() is implemented using the vsg::Object::cast<>() to efficiently replace a dynamic_cast<>. 126 | if (auto body = object.cast()) 127 | { 128 | std::cout<<" pointer = "< to cast to specific type and ref_ptr pointer 24 | auto data = vsg::read_cast("image.vsgt"); 25 | 26 | // use read_cast to cast to specific type and ref_ptr pointer 27 | auto vertexShader = vsg::read_cast("shader.vert"); 28 | ~~~ 29 | 30 | Write functions are found in [include/vsg/io/write.h](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/io/write.h), usage is in the form of: 31 | 32 | ~~~ cpp 33 | // create an object 34 | auto value = vsg::stringValue("mystring"); 35 | 36 | // write to native ascii file format 37 | vsg::write(value, "value.vsgt"); 38 | ~~~ 39 | 40 | ## Options & vsgXchange intro 41 | 42 | Customization and extension of reading and writing is provided by the [vsg::Options](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/io/Options.h) object that can be passed to the vsg::read(..) and vsg::write(..) methods. You can pass in the ReaderWriters that you wish to use, placing them in the order you want them invoked. vsg::Options is subclassed from vsg::Object so has all the standard meta data capabilities and adds IO specific settings. The most common task will be passing in the paths to search for files, and the ReaderWriters to check, such as adding in support for the ReaderWriters provided by vsgXchange. The usage pattern is: 43 | 44 | ~~~ cpp 45 | #include 46 | #include 47 | 48 | int main(int, char**) 49 | { 50 | // create the options object that will tell the vsg::read() function what to use when reading files/istreams 51 | auto options = vsg::Options::create(); 52 | 53 | // set up the paths 54 | options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); 55 | 56 | // assign the ReaderWriters to use when read/writing 57 | options->add(vsgXchange::all::create()); 58 | 59 | // load GLTF model using vsgXchange::assimp that is included in vsgXchange::all, passing in options so read knows what to use 60 | auto model = vsg::read_cast("FlightHelmet.gltf", options); 61 | 62 | return 0; 63 | } 64 | 65 | ~~~ 66 | 67 | The full options available are: 68 | ~~~ cpp 69 | class VSG_DECLSPEC Options : public Inherit 70 | { 71 | public: 72 | Options(); 73 | explicit Options(const Options& options); 74 | 75 | template 76 | explicit Options(Args... args) 77 | { 78 | (add(args), ...); 79 | } 80 | 81 | Options& operator=(const Options& rhs) = delete; 82 | 83 | /// read command line options, assign values to this options object to later use with reading/writing files 84 | virtual bool readOptions(CommandLine& arguments); 85 | 86 | void read(Input& input) override; 87 | void write(Output& output) const override; 88 | 89 | void add(ref_ptr rw = {}); 90 | void add(const ReaderWriters& rws); 91 | 92 | ref_ptr sharedObjects; 93 | ReaderWriters readerWriters; 94 | ref_ptr operationThreads; 95 | 96 | /// Hint to use when searching for Paths with vsg::findFile(filename, options); 97 | enum FindFileHint 98 | { 99 | CHECK_ORIGINAL_FILENAME_EXISTS_FIRST, /// check the filename exists with its original path before trying to find it in Options::paths. 100 | CHECK_ORIGINAL_FILENAME_EXISTS_LAST, /// check the filename exists with its original path after failing to find it in Options::paths. 101 | ONLY_CHECK_PATHS /// only check the filename exists in the Options::paths 102 | }; 103 | FindFileHint checkFilenameHint = CHECK_ORIGINAL_FILENAME_EXISTS_FIRST; 104 | 105 | Paths paths; 106 | 107 | using FindFileCallback = std::function; 108 | FindFileCallback findFileCallback; 109 | 110 | Path fileCache; 111 | 112 | Path extensionHint; 113 | bool mapRGBtoRGBAHint = true; 114 | 115 | /// Coordinate convention to use for scene graph 116 | CoordinateConvention sceneCoordinateConvention = CoordinateConvention::Z_UP; 117 | 118 | /// Coordinate convention to assume for specified lower case file formats extensions 119 | std::map formatCoordinateConventions; 120 | 121 | /// User defined ShaderSet map, loaders should check the available ShaderSet using the name of the type of ShaderSet. 122 | /// Standard names are : 123 | /// "pbr" will substitute for vsg::createPhysicsBasedRenderingShaderSet() 124 | /// "phong" will substitute for vsg::createPhongShaderSet() 125 | /// "flat" will substitute for vsg::createFlatShadedShaderSet() 126 | /// "text" will substitute for vsg::createTextShaderSet() 127 | std::map> shaderSets; 128 | 129 | protected: 130 | virtual ~Options(); 131 | }; 132 | VSG_type_name(vsg::Options); 133 | ~~~ 134 | 135 | In later chapters we'll revisit the features of vsg::Options in more depth. 136 | 137 | ## ReaderWriter 138 | 139 | The vsg::ReaderWriter base class provides the mechanism for implementing support for both native and 3rd party file formats. The [Chain of Responsibility Design Pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) is used with each ReaderWriter implementation taking responsibility for whether it can handle reading from or writing to a file or stream. The ReaderWriters are invoked by the vsg::read(..)/vsg::write() calls in the order that they appear in the vsg::Options::readerWriters list, and if none can handle the read/write then the built in ReaderWriters are called as fallback. 140 | 141 | There are three types of each of the virtual ReaderWriter::read(..) methods that take filename, istream or a block of memory as the source to read, and two types of virtual ReaderWrite::write(..) methods that take a filename or ostream to write to. A virtual ReaderWriter::getFeatures(..) method provides a way to reporting to applications whether read/write features are supported. The full public interface to [ReaderWriter](https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/io/ReaderWriter.h#L33) is: 142 | 143 | ~~~ cpp 144 | /// Base class for providing support for reading and/or writing various file formats and IO protocols 145 | class VSG_DECLSPEC ReaderWriter : public Inherit 146 | { 147 | public: 148 | using vsg::Object::read; 149 | using vsg::Object::write; 150 | 151 | /// convenience method for casting a read object to a specified type. 152 | template 153 | vsg::ref_ptr read_cast(const vsg::Path& filename, vsg::ref_ptr options = {}) const 154 | { 155 | auto object = read(filename, options); 156 | return vsg::ref_ptr(dynamic_cast(object.get())); 157 | } 158 | 159 | /// convenience method for casting a read object to a specified type. 160 | template 161 | vsg::ref_ptr read_cast(std::istream& fin, vsg::ref_ptr options = {}) const 162 | { 163 | auto object = read(fin, options); 164 | return vsg::ref_ptr(dynamic_cast(object.get())); 165 | } 166 | 167 | /// convenience method for casting a read object to a specified type. 168 | template 169 | vsg::ref_ptr read_cast(const uint8_t* ptr, size_t size, vsg::ref_ptr options = {}) const 170 | { 171 | auto object = read(ptr, size, options); 172 | return vsg::ref_ptr(dynamic_cast(object.get())); 173 | } 174 | 175 | /// read object from file, return object on success, return null ref_ptr<> if format not supported, or return ReadError on failure. 176 | virtual vsg::ref_ptr read(const vsg::Path& /*filename*/, vsg::ref_ptr = {}) const { return vsg::ref_ptr(); } 177 | 178 | /// read object from input stream, return object on success, return null ref_ptr<> if format not supported, or return ReadError on failure. 179 | virtual vsg::ref_ptr read(std::istream& /*fin*/, vsg::ref_ptr = {}) const { return vsg::ref_ptr(); } 180 | 181 | /// read object from memory block, return object on success, return null ref_ptr<> if format not supported, or return ReadError on failure. 182 | virtual vsg::ref_ptr read(const uint8_t* /*ptr*/, size_t /*size*/, vsg::ref_ptr = {}) const { return vsg::ref_ptr(); } 183 | 184 | /// write object to file, return true on success, return false on failure. 185 | virtual bool write(const vsg::Object* /*object*/, const vsg::Path& /*filename*/, vsg::ref_ptr = {}) const { return false; } 186 | 187 | /// write object to output stream, return true on success, return false on failure. 188 | virtual bool write(const vsg::Object* /*object*/, std::ostream& /*fout*/, vsg::ref_ptr = {}) const { return false; } 189 | 190 | /// read the command line arguments for any options appropriate for this ReaderWriter 191 | virtual bool readOptions(Options&, CommandLine&) const { return false; } 192 | 193 | enum FeatureMask 194 | { 195 | READ_FILENAME = (1 << 0), 196 | READ_ISTREAM = (1 << 1), 197 | READ_MEMORY = (1 << 2), 198 | WRITE_FILENAME = (1 << 3), 199 | WRITE_OSTREAM = (1 << 4) 200 | }; 201 | 202 | struct Features 203 | { 204 | std::map protocolFeatureMap; 205 | std::map extensionFeatureMap; 206 | std::map optionNameTypeMap; 207 | }; 208 | 209 | /// get the Features supported by this ReaderWriter 210 | virtual bool getFeatures(Features&) const { return false; } 211 | }; 212 | VSG_type_name(vsg::ReaderWriter); 213 | ~~~ 214 | 215 | Prev: [Visitors](Visitors.md) | Next: [vsgXchange](../2_Foundations/vsgXchange.md) 216 | 217 | -------------------------------------------------------------------------------- /2_Foundations/Serialization.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Serialization 4 | permalink: /foundations/Serializaton 5 | --- 6 | 7 | The VulkanSceneGraph provides extensible serialization support so that all scene graph objects can be read from/written to files and streams. This can be used with the native .vsgb binary and .vsgt ascii formats as well as work with user defined input/output through to reading data compiled directly into applications as illustrated in the use of the vsgXchange::cpp ReaderWriter in the previous section on vsgXchange. 8 | 9 | ## vsg::Object, Input and Output base classes 10 | 11 | The serialization support is built upon the vsg::Object base class that provides virtual read(Input&) and write(Output&) methods that users override to implement support for their own member variables, and the vsg::Input and vsg::Output classes that provide a standardized interface for reading and writing data. The [vsg::Object](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/core/Object.h#L88) serialization methods are: 12 | 13 | ~~~ cpp 14 | virtual void read(Input& input); 15 | virtual void write(Output& output) const; 16 | ~~~ 17 | 18 | The [vsg::Input](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Input.h#L37) base class provides the low level pure virtual methods that are implemented by the concrete implementations of Input like vsg::BinaryInput and vsg::AsciiInput as well as a set of template methods that are meant to be used by the read(Input&) methods to implement the serialization of class members. The latter methods take the form of input.read(propertyName, value): 19 | 20 | ~~~ cpp 21 | /// treat non standard type as raw data, 22 | template 23 | void read(const char* propertyName, ref_ptr& arg); 24 | 25 | template 26 | void readObjects(const char* propertyName, T& values); 27 | 28 | template 29 | void readValues(const char* propertyName, std::vector& values); 30 | 31 | template 32 | void readValues(const char* propertyName, std::set& values); 33 | 34 | /// match property name and read value(s) 35 | template 36 | void read(const char* propertyName, Args&... args); 37 | 38 | /// read object of a particular type 39 | ref_ptr readObject(const char* propertyName); 40 | 41 | /// read object of a particular type 42 | template 43 | ref_ptr readObject(const char* propertyName); 44 | 45 | /// read object of a particular type 46 | template 47 | void readObject(const char* propertyName, ref_ptr& arg); 48 | 49 | /// read a value of particular type 50 | template 51 | T readValue(const char* propertyName); 52 | 53 | /// read a value as a type, then cast it to another type 54 | template 55 | void readValue(const char* propertyName, T& value); 56 | ~~~ 57 | 58 | The [vsg::Output](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Output.h#L37) base class methods mirror those in vsg::Input, providing the pure virtual methods that are used to implement the low level serialization, and then higher level user facing methods that are used by end users, the latter take the form output.write(property, value): 59 | 60 | ~~~ cpp 61 | template 62 | void write(const char* propertyName, const ref_ptr& object); 63 | 64 | template 65 | void writeObjects(const char* propertyName, const T& values); 66 | 67 | template 68 | void writeValues(const char* propertyName, const std::vector& values); 69 | 70 | template 71 | void writeValues(const char* propertyName, const std::set& values); 72 | 73 | /// match propertyName and write value(s) 74 | template 75 | void write(const char* propertyName, Args&... args); 76 | 77 | void writeObject(const char* propertyName, const Object* object); 78 | 79 | /// write a value casting it to specified type i.e. output.write("Value", value); 80 | template 81 | void writeValue(const char* propertyName, T value); 82 | ~~~ 83 | 84 | 85 | ## vsg::ObjectFactory 86 | 87 | When writing out objects you can simply call **object->write(output)** and the appropriate serialization will be invoked, but when you need to serialize a file back in, the appropriate objects have to be created before their **object->read(input)** method can be invoked to read the object members. The way the VulkanSceneGraph provides a means for creating objects on demand is via the [vsg::ObjectFactory](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/ObjectFactory.h#L24) singleton class, where only one instance of the Factory exists. The vsg::ObjectFactory is an example of the [Factory Method Design Pattern](https://en.wikipedia.org/wiki/Factory_method_pattern) and [Singleton Design Pattern](https://en.wikipedia.org/wiki/Singleton_pattern). 88 | 89 | The core scene graph classes found in the VulkanSceneGraph library have creation methods automatically assigned to the vsg::ObjectFactory, and the native VSG loaders internally use the ObjectFactory to create all the required objects, so for native .vsgt and .vsgb files one doesn't need to concern oneself with the ObjectFactory - it's simply something used internally by the VSG when loading files. 90 | 91 | For cases where applications extend the scene graph objects (like with the native::Animal example below), users have to register their class with the ObjectFactory so that loaders can create an instance of it for each object of that type that the loader needs to create and read into. The ObjectFactory.h header provides the [vsg::RegisterWithObjectFactoryProxy](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/ObjectFactory.h#L54) template helper class to make this task straightforward. The following example includes a static declaration of the proxy object so that during initialization of the application, the required creation method will be automatically registered with the vsg::ObjectFactory singleton. 92 | 93 | ## Example of implementing serialization 94 | 95 | While the range of methods in Input and Output is extensive and potentially overwhelming, usage of these classes is usually quite straightforward, with the template<> methods automatically handling support for you. The [serialization](https://github.com/vsg-dev/vsgTutorial/tree/master/2_Foundations/2_serialization) example illustrates how to implement custom serialization. 96 | 97 | ~~~ cpp 98 | {% include_relative 2_serialization/serialization.cpp %} 99 | ~~~ 100 | 101 | The output from running this example is: 102 | 103 | ~~~ sh 104 | $ more animal.vsgt 105 | #vsga 1.0.5 106 | Root id=1 nature::Animal 107 | { 108 | name "Fido" 109 | age 3.5 110 | } 111 | ~~~ 112 | 113 | ## vsg::Input and vsg::Output subclasses 114 | 115 | Orthogonal to the task of implementing serializers for user defined classes the underlying vsg::Input and vsg::Output that implement the integration with the underlying file/stream/memory are also extensible. The native .vsgt Ascii and .vsgb Binary file formats that the [vsg::VSG ReaderWriter](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/VSG.h#L24) provides are implemented via subclassing from vsg::Input and vsg::Output, these subclasses provide a good illustration of what is required, the following table provides links to the relevant header and source files for each of these subclasses: 116 | 117 | | base class | subclass | header | source | 118 | | [vsg::Input](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Input.h#L40) | vsg::AsciiInput | [include/vsg/io/AsciiInput.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/AsciiInput.h#L26) | [src/vsg/io/AsciiInput.cpp](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/src/vsg/io/AsciiInput.cpp#L13) | 119 | | [vsg::Output](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Output.h#L37) | vsg::AsciiOutput | [include/vsg/io/AsciiOutput.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/AsciiOutput.h#L24) | [src/vsg/io/AsciiOutput.cpp](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/src/vsg/io/AsciiOutput.cpp#L13) | 120 | | [vsg::Input](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Input.h#L40) | vsg::BinaryInput | [include/vsg/io/BinaryInput.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/BinaryInput.h#L26) | [src/vsg/io/BinaryInput.cpp](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/src/vsg/io/BinaryInput.cpp#L13) | 121 | | [vsg::Output](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/Output.h#L37) | vsg::BinaryOutput | [include/vsg/io/BinaryOutput.h](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/BinaryOutput.h#L24) | [src/vsg/io/BinaryOutput.cpp](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/src/vsg/io/BinaryOutput.cpp#L13) | 122 | 123 | The [vsg::VSG ReaderWriter selects](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/src/vsg/io/VSG.cpp#L94) the appropriate Input/Output implementation based on the file extension, so for most use cases there is never any need to create and invoke the Input/Output classes directly in your application. For most use cases there will also be no need to write your own subclasses from vsg::Input and vsg::Output, a possible exception would be subclassing from vsg::Input/vsg::Output to implement the reflection support required when integrating with 3rd party languages such as Lua or Python. This type of usage is an advanced topic beyond the scope of this online book, the existing implementations linked to above will be a good starting place for seeing what would be required. 124 | 125 | Prev: [vsgXchange](vsgXchange.md) | Next: [File Systems](FileSystem.md) 126 | 127 | -------------------------------------------------------------------------------- /2_Foundations/StreamsAndLogger.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Streams & Logger 4 | permalink: /foundations/StreamsAndLogger 5 | --- 6 | 7 | A significant part of developing software is the process of reporting and logging results for the purpose of QA and debugging. C++ provides the std::ostream/std::istream operators for convenient textual formatting of output and input of standard types, and the VulkanSceneGraph extends this to include native types. The library then adds additional support for textual recording with an extensible thread safe logger class. 8 | 9 | ## istream & ostream operators 10 | 11 | The [include/vsg/io/stream.h header](https://github.com/vsg-dev/VulkanSceneGraph/tree/master/include/vsg/io/stream.h#L31) provides a collection of << and >> operators for a range of types making it convenient to use them with input/output streams: 12 | 13 | ~~~ cpp 14 | vsg::vec3 position = {1.0, 2.0, 3.0}; 15 | 16 | vsg::dquat rotation(vsg::radians(90.0), vsg::dvec3(0.0, 0.0, 1.0)); 17 | 18 | vsg::mat4 matrix = { 2.0, 0.0, 0.0, 0.0, 19 | 0.0, 2.0, 0.0, 0.0, 20 | 0.0, 0.0, 2.0, 0.0, 21 | 0.0, 0.0, 0.0, 1.0}; 22 | 23 | auto place = vsg::stringValue::create("Paris"); 24 | 25 | std::cout<<"position = "< 6 | .container { 7 | margin: 10px auto; 8 | max-width: 600px; 9 | text-align: center; 10 | } 11 | h1 { 12 | margin: 30px 0; 13 | font-size: 4em; 14 | line-height: 1; 15 | letter-spacing: -1px; 16 | } 17 | 18 | 19 |
20 |

404

21 | 22 |

Page not found :(

23 |

The requested page could not be found.

24 |
25 | -------------------------------------------------------------------------------- /4_Application/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Application 4 | permalink: /application/ 5 | --- 6 | 7 | **Sorry, not yet written.** 8 | 9 | 10. Viewer - creating application viewer 10 | 11. Utilities - operating on the scene graph 11 | 12. Threading - different ways of threading 12 | -------------------------------------------------------------------------------- /5_DevelopingSkills/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Developing Skills 4 | permalink: /skills/ 5 | --- 6 | 7 | **Sorry, not yet written.** 8 | 9 | 13. Trouble Shooting - debugging VulkanSceneGraph applications 10 | 14. Optimization - how to improve performance & lower power consumption 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(vsgTutorial) 4 | 5 | # build all examples into the bin directory 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) 7 | 8 | add_subdirectory(1_SettingTheScene/01_hello_world) 9 | add_subdirectory(2_Foundations/2_observer_ptr) 10 | add_subdirectory(2_Foundations/2_rtti) 11 | add_subdirectory(2_Foundations/2_serialization) 12 | add_subdirectory(2_Foundations/2_streams) 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Hello! This is where you manage which Jekyll version is used to run. 4 | # When you want to use a different version, change it below, save the 5 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 6 | # 7 | # bundle exec jekyll serve 8 | # 9 | # This will help ensure the proper Jekyll version is running. 10 | # Happy Jekylling! 11 | # gem "jekyll", "~> 3.9.0" 12 | 13 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 14 | gem "minima", "~> 2.0" 15 | 16 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 17 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 18 | gem "github-pages", "~> 228", group: :jekyll_plugins 19 | 20 | # If you have any plugins, put them here! 21 | group :jekyll_plugins do 22 | gem "jekyll-feed", "~> 0.6" 23 | end 24 | 25 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 26 | # and associated library. 27 | platforms :mingw, :x64_mingw, :mswin, :jruby do 28 | gem "tzinfo", "~> 1.2" 29 | gem "tzinfo-data" 30 | end 31 | 32 | # Performance-booster for watching directories on Windows 33 | gem "wdm", "~> 0.1.0", :platforms => [:mingw, :x64_mingw, :mswin] 34 | 35 | # kramdown v2 ships without the gfm parser by default. If you're using 36 | # kramdown v1, comment out this line. 37 | gem "kramdown-parser-gfm" 38 | 39 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (7.0.4.2) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 1.6, < 2) 7 | minitest (>= 5.1) 8 | tzinfo (~> 2.0) 9 | addressable (2.8.1) 10 | public_suffix (>= 2.0.2, < 6.0) 11 | coffee-script (2.4.1) 12 | coffee-script-source 13 | execjs 14 | coffee-script-source (1.11.1) 15 | colorator (1.1.0) 16 | commonmarker (0.23.8) 17 | concurrent-ruby (1.2.2) 18 | dnsruby (1.61.9) 19 | simpleidn (~> 0.1) 20 | em-websocket (0.5.3) 21 | eventmachine (>= 0.12.9) 22 | http_parser.rb (~> 0) 23 | ethon (0.16.0) 24 | ffi (>= 1.15.0) 25 | eventmachine (1.2.7) 26 | execjs (2.8.1) 27 | faraday (2.7.4) 28 | faraday-net_http (>= 2.0, < 3.1) 29 | ruby2_keywords (>= 0.0.4) 30 | faraday-net_http (3.0.2) 31 | ffi (1.15.5) 32 | forwardable-extended (2.6.0) 33 | gemoji (3.0.1) 34 | github-pages (228) 35 | github-pages-health-check (= 1.17.9) 36 | jekyll (= 3.9.3) 37 | jekyll-avatar (= 0.7.0) 38 | jekyll-coffeescript (= 1.1.1) 39 | jekyll-commonmark-ghpages (= 0.4.0) 40 | jekyll-default-layout (= 0.1.4) 41 | jekyll-feed (= 0.15.1) 42 | jekyll-gist (= 1.5.0) 43 | jekyll-github-metadata (= 2.13.0) 44 | jekyll-include-cache (= 0.2.1) 45 | jekyll-mentions (= 1.6.0) 46 | jekyll-optional-front-matter (= 0.3.2) 47 | jekyll-paginate (= 1.1.0) 48 | jekyll-readme-index (= 0.3.0) 49 | jekyll-redirect-from (= 0.16.0) 50 | jekyll-relative-links (= 0.6.1) 51 | jekyll-remote-theme (= 0.4.3) 52 | jekyll-sass-converter (= 1.5.2) 53 | jekyll-seo-tag (= 2.8.0) 54 | jekyll-sitemap (= 1.4.0) 55 | jekyll-swiss (= 1.0.0) 56 | jekyll-theme-architect (= 0.2.0) 57 | jekyll-theme-cayman (= 0.2.0) 58 | jekyll-theme-dinky (= 0.2.0) 59 | jekyll-theme-hacker (= 0.2.0) 60 | jekyll-theme-leap-day (= 0.2.0) 61 | jekyll-theme-merlot (= 0.2.0) 62 | jekyll-theme-midnight (= 0.2.0) 63 | jekyll-theme-minimal (= 0.2.0) 64 | jekyll-theme-modernist (= 0.2.0) 65 | jekyll-theme-primer (= 0.6.0) 66 | jekyll-theme-slate (= 0.2.0) 67 | jekyll-theme-tactile (= 0.2.0) 68 | jekyll-theme-time-machine (= 0.2.0) 69 | jekyll-titles-from-headings (= 0.5.3) 70 | jemoji (= 0.12.0) 71 | kramdown (= 2.3.2) 72 | kramdown-parser-gfm (= 1.1.0) 73 | liquid (= 4.0.4) 74 | mercenary (~> 0.3) 75 | minima (= 2.5.1) 76 | nokogiri (>= 1.13.6, < 2.0) 77 | rouge (= 3.26.0) 78 | terminal-table (~> 1.4) 79 | github-pages-health-check (1.17.9) 80 | addressable (~> 2.3) 81 | dnsruby (~> 1.60) 82 | octokit (~> 4.0) 83 | public_suffix (>= 3.0, < 5.0) 84 | typhoeus (~> 1.3) 85 | html-pipeline (2.14.3) 86 | activesupport (>= 2) 87 | nokogiri (>= 1.4) 88 | http_parser.rb (0.8.0) 89 | i18n (1.12.0) 90 | concurrent-ruby (~> 1.0) 91 | jekyll (3.9.3) 92 | addressable (~> 2.4) 93 | colorator (~> 1.0) 94 | em-websocket (~> 0.5) 95 | i18n (>= 0.7, < 2) 96 | jekyll-sass-converter (~> 1.0) 97 | jekyll-watch (~> 2.0) 98 | kramdown (>= 1.17, < 3) 99 | liquid (~> 4.0) 100 | mercenary (~> 0.3.3) 101 | pathutil (~> 0.9) 102 | rouge (>= 1.7, < 4) 103 | safe_yaml (~> 1.0) 104 | jekyll-avatar (0.7.0) 105 | jekyll (>= 3.0, < 5.0) 106 | jekyll-coffeescript (1.1.1) 107 | coffee-script (~> 2.2) 108 | coffee-script-source (~> 1.11.1) 109 | jekyll-commonmark (1.4.0) 110 | commonmarker (~> 0.22) 111 | jekyll-commonmark-ghpages (0.4.0) 112 | commonmarker (~> 0.23.7) 113 | jekyll (~> 3.9.0) 114 | jekyll-commonmark (~> 1.4.0) 115 | rouge (>= 2.0, < 5.0) 116 | jekyll-default-layout (0.1.4) 117 | jekyll (~> 3.0) 118 | jekyll-feed (0.15.1) 119 | jekyll (>= 3.7, < 5.0) 120 | jekyll-gist (1.5.0) 121 | octokit (~> 4.2) 122 | jekyll-github-metadata (2.13.0) 123 | jekyll (>= 3.4, < 5.0) 124 | octokit (~> 4.0, != 4.4.0) 125 | jekyll-include-cache (0.2.1) 126 | jekyll (>= 3.7, < 5.0) 127 | jekyll-mentions (1.6.0) 128 | html-pipeline (~> 2.3) 129 | jekyll (>= 3.7, < 5.0) 130 | jekyll-optional-front-matter (0.3.2) 131 | jekyll (>= 3.0, < 5.0) 132 | jekyll-paginate (1.1.0) 133 | jekyll-readme-index (0.3.0) 134 | jekyll (>= 3.0, < 5.0) 135 | jekyll-redirect-from (0.16.0) 136 | jekyll (>= 3.3, < 5.0) 137 | jekyll-relative-links (0.6.1) 138 | jekyll (>= 3.3, < 5.0) 139 | jekyll-remote-theme (0.4.3) 140 | addressable (~> 2.0) 141 | jekyll (>= 3.5, < 5.0) 142 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) 143 | rubyzip (>= 1.3.0, < 3.0) 144 | jekyll-sass-converter (1.5.2) 145 | sass (~> 3.4) 146 | jekyll-seo-tag (2.8.0) 147 | jekyll (>= 3.8, < 5.0) 148 | jekyll-sitemap (1.4.0) 149 | jekyll (>= 3.7, < 5.0) 150 | jekyll-swiss (1.0.0) 151 | jekyll-theme-architect (0.2.0) 152 | jekyll (> 3.5, < 5.0) 153 | jekyll-seo-tag (~> 2.0) 154 | jekyll-theme-cayman (0.2.0) 155 | jekyll (> 3.5, < 5.0) 156 | jekyll-seo-tag (~> 2.0) 157 | jekyll-theme-dinky (0.2.0) 158 | jekyll (> 3.5, < 5.0) 159 | jekyll-seo-tag (~> 2.0) 160 | jekyll-theme-hacker (0.2.0) 161 | jekyll (> 3.5, < 5.0) 162 | jekyll-seo-tag (~> 2.0) 163 | jekyll-theme-leap-day (0.2.0) 164 | jekyll (> 3.5, < 5.0) 165 | jekyll-seo-tag (~> 2.0) 166 | jekyll-theme-merlot (0.2.0) 167 | jekyll (> 3.5, < 5.0) 168 | jekyll-seo-tag (~> 2.0) 169 | jekyll-theme-midnight (0.2.0) 170 | jekyll (> 3.5, < 5.0) 171 | jekyll-seo-tag (~> 2.0) 172 | jekyll-theme-minimal (0.2.0) 173 | jekyll (> 3.5, < 5.0) 174 | jekyll-seo-tag (~> 2.0) 175 | jekyll-theme-modernist (0.2.0) 176 | jekyll (> 3.5, < 5.0) 177 | jekyll-seo-tag (~> 2.0) 178 | jekyll-theme-primer (0.6.0) 179 | jekyll (> 3.5, < 5.0) 180 | jekyll-github-metadata (~> 2.9) 181 | jekyll-seo-tag (~> 2.0) 182 | jekyll-theme-slate (0.2.0) 183 | jekyll (> 3.5, < 5.0) 184 | jekyll-seo-tag (~> 2.0) 185 | jekyll-theme-tactile (0.2.0) 186 | jekyll (> 3.5, < 5.0) 187 | jekyll-seo-tag (~> 2.0) 188 | jekyll-theme-time-machine (0.2.0) 189 | jekyll (> 3.5, < 5.0) 190 | jekyll-seo-tag (~> 2.0) 191 | jekyll-titles-from-headings (0.5.3) 192 | jekyll (>= 3.3, < 5.0) 193 | jekyll-watch (2.2.1) 194 | listen (~> 3.0) 195 | jemoji (0.12.0) 196 | gemoji (~> 3.0) 197 | html-pipeline (~> 2.2) 198 | jekyll (>= 3.0, < 5.0) 199 | kramdown (2.3.2) 200 | rexml 201 | kramdown-parser-gfm (1.1.0) 202 | kramdown (~> 2.0) 203 | liquid (4.0.4) 204 | listen (3.8.0) 205 | rb-fsevent (~> 0.10, >= 0.10.3) 206 | rb-inotify (~> 0.9, >= 0.9.10) 207 | mercenary (0.3.6) 208 | minima (2.5.1) 209 | jekyll (>= 3.5, < 5.0) 210 | jekyll-feed (~> 0.9) 211 | jekyll-seo-tag (~> 2.1) 212 | minitest (5.18.0) 213 | nokogiri (1.14.2-x86_64-linux) 214 | racc (~> 1.4) 215 | octokit (4.25.1) 216 | faraday (>= 1, < 3) 217 | sawyer (~> 0.9) 218 | pathutil (0.16.2) 219 | forwardable-extended (~> 2.6) 220 | public_suffix (4.0.7) 221 | racc (1.6.2) 222 | rb-fsevent (0.11.2) 223 | rb-inotify (0.10.1) 224 | ffi (~> 1.0) 225 | rexml (3.2.5) 226 | rouge (3.26.0) 227 | ruby2_keywords (0.0.5) 228 | rubyzip (2.3.2) 229 | safe_yaml (1.0.5) 230 | sass (3.7.4) 231 | listen (~> 3.0) 232 | sawyer (0.9.2) 233 | addressable (>= 2.3.5) 234 | faraday (>= 0.17.3, < 3) 235 | simpleidn (0.2.1) 236 | unf (~> 0.1.4) 237 | terminal-table (1.8.0) 238 | unicode-display_width (~> 1.1, >= 1.1.1) 239 | typhoeus (1.4.0) 240 | ethon (>= 0.9.0) 241 | tzinfo (2.0.6) 242 | concurrent-ruby (~> 1.0) 243 | unf (0.1.4) 244 | unf_ext 245 | unf_ext (0.0.8.2) 246 | unicode-display_width (1.8.0) 247 | 248 | PLATFORMS 249 | x86_64-linux 250 | 251 | DEPENDENCIES 252 | github-pages (~> 228) 253 | jekyll-feed (~> 0.6) 254 | kramdown-parser-gfm 255 | minima (~> 2.0) 256 | tzinfo (~> 1.2) 257 | tzinfo-data 258 | wdm (~> 0.1.0) 259 | 260 | BUNDLED WITH 261 | 2.3.5 262 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Robert Osfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **CURRENTLY IN DEVELOPMENT.** 2 | 3 | The VulkanSceneGraph Tutorial provides a multi-part tutorial that introduces developers to the VulkanSceneGraph project and how to use it in their graphics and compute applications. 4 | 5 | This repository provides presentations that can be browsed online and exercise programs that can provide a starting place for developers to test out their growing knowledge of how to use the VulkanSceneGraph. 6 | 7 | The website can be browsed at: [https://vsg-dev.github.io/vsgTutorial](https://vsg-dev.github.io/vsgTutorial) 8 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | title: vsgTutorial 17 | description: >- # this means to ignore newlines until "baseurl:" 18 | Vulkan Made Easy 19 | baseurl: "vsgTutorial" # the subpath of your site, e.g. /blog 20 | url: "https://vsg-dev.github.io/vsgTutorial" # the base hostname & protocol for your site, e.g. http://example.com 21 | twitter_username: dev_vsg 22 | github_username: vsg-dev 23 | 24 | # Build settings 25 | markdown: kramdown 26 | theme: minima 27 | plugins: 28 | - jekyll-feed 29 | 30 | header_pages: 31 | - 1_SettingTheScene/index.md 32 | - 2_Foundations/index.md 33 | - 3_SceneGraph/index.md 34 | - 4_Application/index.md 35 | - 5_DevelopingSkills/index.md 36 | 37 | # Exclude from processing. 38 | # The following items will not be processed, by default. Create a custom list 39 | # to override the default setting. 40 | # exclude: 41 | # - Gemfile 42 | # - Gemfile.lock 43 | # - node_modules 44 | # - vendor/bundle/ 45 | # - vendor/cache/ 46 | # - vendor/gems/ 47 | # - vendor/ruby/ 48 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /_includes/includelines: -------------------------------------------------------------------------------- 1 | {% comment %}{% raw %}{% endraw %}{% endcomment %} 16 | 17 | {% capture filecontent %} 18 | {% include {{include.filename}} %} 19 | {% endcapture %} 20 | 21 | {% assign lines = filecontent | newline_to_br | split: '
' %} 22 | 23 | {% for line in lines offset:{{include.start}} limit:{{include.count}} %}{{ line }}{% endfor %} 24 | -------------------------------------------------------------------------------- /images/VSGlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsg-dev/vsgTutorial/a63f6ef275d7dd8bd74699806a5aa2940dbf5a9f/images/VSGlogo.png -------------------------------------------------------------------------------- /images/hello_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsg-dev/vsgTutorial/a63f6ef275d7dd8bd74699806a5aa2940dbf5a9f/images/hello_world.png -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # Feel free to add content and custom Front Matter to this file. 3 | # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults 4 | 5 | layout: home 6 | --- 7 | 8 | **CURRENTLY IN DEVELOPMENT: expect missing sections and rough and ready state.** 9 | 10 | The goal of this tutorial is to teach developers how to use the [VulkanSceneGraph](https://github.com/vsg-dev/VulkanSceneGraph) effectively in their graphics and compute applications. The tutorial assumes developers have prior knowledge of using CMake and C++ to build and write applications. Knowledge of scene graphs, real-time graphics and Vulkan are not assumed, though teaching real-time graphics and Vulkan at depth is beyond the scope of this tutorial, links to 3rd party resources for further learning will be provided. 11 | 12 | Each chapter of the tutorial is coupled with exercises so that developers can learn about each topic then test out what they have learned. The topics covered are: 13 | 14 | 1. [Setting The Scene](1_SettingTheScene/index.md) **First Draft** 15 | We introduce you to the world of scene graphs with a brief tour of low and high-level APIs from IrisGL & Inventor to Vulkan & VulkanSceneGraph. The chapter then turns to the software design and performance principles used in the development of the VulkanSceneGraph, the ecosystem building up around the project, high-level features and conventions you'll get with the core VulkanSceneGraph library and wraps up with how to build the software and run the first exercise - a scene graph take on Hello World. 16 | 17 | 2. [Foundations](2_Foundations/index.md) **First Draft** 18 | This chapter covers the foundational base classes, memory management, maths and IO support that the rest of the scene graph functionality is built upon. 19 | 20 | 3. [Scene Graph](3_SceneGraph/index.md) **Currently being written** 21 | This chapter introduces the scene graph classes - the internal nodes through to the geometry and state. 22 | 23 | 4. [Application](4_Application/index.md) **To be written** 24 | This chapter focuses on application level classes - the viewer, windows, views, cameras, event handling, rendering loop and threading. 25 | 26 | 5. [Developing Skills](5_DevelopingSkills/index.md) **To be written** 27 | The final chapter wraps up with guidance on trouble shooting and debugging VulkanSceneGraph applications, through to how to improve performance & lower power consumption. 28 | --------------------------------------------------------------------------------