Foreword
This is a spare time project. There's no support. API changes are done in an time efficient way, meaning without thinking about backward compatibility.
Unrimp Description
Unified renderer implementation ("Un r imp") with separation into rendering hardware interface (RHI), renderer and toolkit for asset cooking
- RHI abstracts way the underlying API like Vulkan/OpenGL/Direct3D
- Renderer designed with end-user and middleware-user in mind
- Efficiency and responsiveness over flexibility (were it isn't useful in practice)
- Intended to be controlled by a high-level entity-component system, no unused implementation feature overkill in the basic renderer
- Toolkit designed with developer fast iterations in mind: Asset source flexibility, asset background compilation, hot-reloading
Examples from basic to advanced are provided.
Originally started as modern renderer replacement for the PixelLight engine which was in active development between 2002-2012. The first public Unrimp source code release after starting the project on Friday, 1 June 2012 was on January 03, 2013.
Screenshots
General
- C++20 and above, no legacy compiler support, compiled with wall warning level
- Compact user-header for the RHI
- A single all in one header for ease-of-use and optimized compile times
- No need to links against the RHI library itself, load RHI implementations dynamically during runtime
- Usage of amalgamated/unity builds for optimized compile times
- Using CMake for the build process
- Using Doxygen for code documentation
- Lightweight RHI implementations
- Designed with AZDO ("Almost Zero Driver Overhead") in mind
- Implementations try to stick as best as possible close-to-the-metal and as a result are just a few KiB instead of MiB in memory size
- Implementations load the entry points of Vulkan, Direct3D, OpenGL and so on during runtime, meaning it's possible to react on system failures by e.g. dynamically switching to another RHI implementation
- Support for static and shared build
- RTTI and C++ exceptions are not used by RHI and renderer
- Interfaces for log, assert, memory allocator, graphics debugger, profiler and file so the user has the control over those things
- Standard implementations are provided
- Standard memory allocator as well as mimalloc memory allocator implementation is provided
- Standard graphics debugger implementation using RenderDoc is provided
- Standard profiler implementation using Remotery is provided
- Standard file implementation using UTF-8 STD file streams as well as PhysicsFS for shipping packed asset packages are provided
- Cross-platform
- Microsoft Windows x86 and x64
- Currently unmaintained
- Linux
- Android
- Mac is currently no target platform due no dropped OpenGL support and no official Vulkan support, implementing Metal is too much effort for a spare time pet project
Rendering hardware interface (RHI) and implementations
- RHI implementations for
- Vulkan (not feature complete, yet)
- Direct3D 12 (not feature complete, yet)
- Direct3D 11
- OpenGL (by default a OpenGL 4.1 context is created, the best OpenGL version Mac OS X 10.11 supports, so lowest version we have to support)
- OpenGL ES 3
- Legacy = critical features like uniform buffer, texture buffer and/or compute shaders are missing which are required for modern efficient renderers which provide e.g. automatic instancing or clustered deferred rendering, still maintained for curiosity reasons
- Direct3D 10
- Direct3D 9
- Render targets
- Swap chains, render into one or multiple operation system windows
- Framebuffer object (FBO) used for render to texture, support for multiple render targets (MRT), support for multisample (MSAA)
- Shaders
- Shader types
- Vertex shader (VS)
- Tessellation control shader (TCS, "hull shader" in Direct3D terminology)
- Tessellation evaluation shader (TES, "domain shader" in Direct3D terminology)
- Geometry shader (GS)
- Fragment shader (FS, "pixel shader" in Direct3D terminology)
- Task shader (TS, "amplification shader" in Direct3D terminology)
- Mesh shader (MS)
- Compute shader (CS)
- Shader data sources
- Shader bytecode (aka shader microcode, binary large object (BLOB))
- Vulkan and OpenGL: SPIR-V support for cross-platform vendor and GPU driver independent shader bytecodes
- Shader source code
- Shader bytecode (aka shader microcode, binary large object (BLOB))
- Shader types
- Buffers
- Vertex array object (VAO, input-assembler (IA) stage) with support for multiple vertex streams
- Vertex buffer object (VBO, input-assembler (IA) stage)
- Index buffer object (IBO, input-assembler (IA) stage)
- Texture buffer object (TBO)
- Structured buffer object (SBO)
- Indirect buffer object with optional internal emulation, draw methods always use an indirect buffer to have an unified draw call API
- Uniform buffer object (UBO, "constant buffer" in Direct3D terminology)
- Command buffer mandatory by design, not just build on top
- Vertex array object (VAO, input-assembler (IA) stage) with support for multiple vertex streams
- Textures: 1D, 1D array, 2D, 2D array, 3D, cube, cube array
- State objects with mapping to API specific settings during creation, not runtime
- Graphics pipeline state object (PSO) which directly maps to Direct3D 12, other RHI implementations internally subdivide into
- Rasterizer state object (rasterizer stage (RS))
- Depth stencil state object (output-merger (OM) stage)
- Blend state object (output-merger (OM) stage)
- Sampler state object (SO)
- Graphics pipeline state object (PSO) which directly maps to Direct3D 12, other RHI implementations internally subdivide into
- Instancing support
- Instanced arrays (shader model 3 feature, vertex array element advancing per-instance instead of per-vertex)
- Draw instanced (shader model 4 feature, build in shader variable holding the current instance ID)
- Debug methods and RHI debug resource names
- When using Direct3D <11.1, those methods map to the Direct3D 9 PIX functions (D3DPERF_* functions, also works directly within VisualStudio 2019 out-of-the-box)
- Used inside the RHI implementations for better RHI debugging
- Supported asynchronous queries: Occlusion, pipeline statistics and timestamp
- RHI implementation specific optimizations:
- OpenGL: Usage of direct state access (DSA), if available
Renderer (e.g. "The Game")
- During runtime, only platform optimized and compressed binary assets are used
- Asynchronous loading for all resources: To fight lags, micro stutter / judder, especially for virtual reality applications one needs a constant framerate
- Material and shader blueprint system which was designed from ground up for pipeline state object (PSO) architecture
- New material types can be added without a single line of C++ source code, meaning technical artists can create and fine-tune the shaders in realtime
- Materials reference material blueprints and are just a list of key-value-pairs
- Shader language specifics are abstracted away: Write shaders once, use them across multiple RHI implementations
- Support for shader combinations (Uber-shaders)
- Support for reusable shader pieces
- Material inheritance for materials which should share common properties, but differ in other properties
- Using MojoShader as shader preprocessor so the resulting shader source codes are compact and easy to debug
- Asynchronous pipeline state compilation, including a fallback system to reduce visual artifacts in case of pipeline cache misses
- Compositor: Setup your overall rendering flow without a single line of C++ source code
- The compositor is using the material blueprint system, meaning compact C++ implementation while offering mighty possibilities
- Using Reversed-Z for improved depth buffer precision to reduce z-fighting
- Using camera relative rendering for rendering large scale scenes without jittering/wobbling
- Using 64 bit world space position
- Blurred stabilized cascaded (CSM) exponential variance (EVSM) shadow mapping basing on "A Sampling of Shadow Techniques" by Matt Pettineo
- Resolution scale support
- Scene as most top-level concept: Fancy-feature set kept simple because more complex applications / games usually add an entity-component-system
- Sorted render queue fed with generic renderables to decouple concrete constructs like meshes, particles, terrain etc. from the generic rendering
- Using mathematics library GLM
- Using xsimd for SIMD intrinsics
- Cache efficient SIMD multi-threaded frustum culling basing on "The Implementation of Frustum Culling in Stingray"
- ImGui is used as debug GUI to be able to quickly add interactive GUI elements for debugging, prototyping or visualization
- Usage of ImGuizmo 3D gizmo extension
- Virtual reality manager which is internally using OpenVR for head-mounted display support
- Animated controller visualization supported
- Single pass stereo rendering via instancing
- Hidden area mesh supported
- Texture top mipmap removal support while loading textures for efficient texture quality reduction
- Light
- Types: Directional, point and spot
- High-level sunlight controlled via time-of-day
- Support for Illuminating Engineering Society (IES) light profiles (photometric light data, use e.g. IESviewer as viewer)
- Mesh
- Level of detail (LOD) support
- Optional position-only vertex array object (VAO) which can reduce the number of processed vertices up to half, can be used for position-only rendering (e.g. shadow map rendering) using the same vertex data that the original vertex array object (VAO) uses
- Skeleton animation
- ACL compressed skeleton animation clip
- GPU dual quaternion skinning (DQS), linear blend skinning (LBS) using matrices path is available as well
- Particles rendering
- Terrain
- This software contains source code provided by NVIDIA Corporation. The height map terrain tessellation implementation is basing on "DirectX 11 Terrain Tessellation" by Iain Cantlay and the concrete implementation "TerrainTessellation"-sample inside "NVIDIA Direct3D SDK 11".
- Terrain data created by Marcel Gonzales
- Procedural shader splatting for elevation/slope based blending with additional splash map
- Height map based texture layer blending
- Triplanar texture mapping
- Sky
- Traditional environment cube map skybox
- Procedural sky
- Hosek-Wilkie sky which is also used to derive a sun color
- Distance clouds
- Sun
- Volume rendering
- Debug draw functionality
Renderer Toolkit (e.g. "The Editor")
- Project compiler will transform source data into runtime data and while doing so tries to detect editing issues at tooltime as early as possible to reduce runtime harm and long debugging seasons
- Asynchronous resource compilation and hot reloading for all resources if the toolkit is enabled (true for production, probably not true for shipped titles)
- Shader-resource example: It's possible to develop shaders while the application is running and see changes instantly
- Most source file formats are using JSON: RapidJSON is used for parsing
- Performs optimizations and validations at tooltime instead of runtime. Examples:
- Strips comments from shader source codes
- Checks the material blueprint resources for valid uniform buffer packing rules
- Mesh compiler
- Using Assimp (Open Asset Import Library) for mesh import and post processing like joining identical vertices
- Using MikkTSpace by Morten S. Mikkelsen for semi-standard mesh tangent space generation
- Using meshoptimizer for mesh optimization and LOD generation
- Texture compiler
- Using enhanced Unity Crunch (better encoder performance and ETC2 support) version of Crunch for mipmap generation and compression
- Support for normal map compression
- Support for alpha mipmaps
- Support for creating a cube-map out of six provided individual textures
- Support for 2D-LUT to 3D-LUT conversion
- Support for texture channel packing
- Support for defining texture arrays
- Toksvig specular anti-aliasing basing on "Specular Showdown in the Wild West" by Stephen Hill to reduce shimmering/sparkling via texture modifications during texture asset compilation
- Sketchfab asset importer without the need to unzip the downloaded meshes first
Examples (just some high level keywords)
- Memory leaks: On Microsoft Windows, "_CrtMemCheckpoint()" and "_CrtMemDumpAllObjectsSince()" is used by default to detect memory leaks while developing and not later on. In case something triggers, use the diagnostic tools provided by Visual Studio 2019 or third parts tools to locate the memory leak in detail.
- Hierarchical depth buffer (aka Hi-Z map or HZB), useful for GPU occlusion culling, screen space reflections as well as using the second depth buffer mipmap for e.g. a half-sized volumetric light/fog bilateral upsampling
- Physically based shading (PBS) using "metallic workflow" (aka "metal-rough-workflow" aka "Albedo/Metallic/Roughness") instead of "specular workflow" (aka "specular-gloss-workflow" aka "Diffuse/Specular/Glossines")
- Microsoft Windows: "NVIDIA Optimus" and "AMD Dynamic Switchable Graphic" awareness to reduce the risk of getting the integrated graphics unit assigned when there's a dedicated graphics card as well
- Volumetric light/fog (aka crepuscular rays, god rays, sunbeams, sunbursts, light shafts or star flare)
- Dynamic rain accumulated water in a hole/cracks with puddle and rain drops and water streaks
- Custom resolved MSAA for antialiased deferred rendering and temporal anti-aliasing
- Distortion which can e.g. be used for refraction and heat haze / heat shimmer
- General Purpose Computation on Graphics Processing Unit (GPGPU)
- Gaussian blur, used to e.g. blur the transparent ImGui background
- High dynamic range (HDR) rendering with adaptive luminance
- Tangent space BC5/3DC/ATI2N stored normal maps
- Color correction via 3D lookup table (LUT)
- Screen space ambient occlusion (SSAO)
- Fast Approximate Anti-Aliasing (FXAA)
- Parallax occlusion mapping (POM)
- Old CRT post processing effect
- Screen space reflections (SSR)
- Physically based wet surfaces
- Gamma correct rendering
- Bloom with dirty lens
- Chromatic aberration
- Clustered shading
- Alpha to coverage
- Pseudo lens flare
- Depth of field
- Soft particles
- Motion blur
- Film grain
- RGB dither
- Depth fog
- Vignette
- Sharpen
Terminology and Acronyms
- General
- Plain old data (POD)
- Rendering hardware interface (RHI)
- Compute shader (CS)
- Fragment shader (FS), "pixel shader" in Direct3D terminology
- Geometry shader (GS)
- Index buffer object (IBO)
- Mesh shader (MS)
- Pipeline state object (PSO, there's a graphics PSO and a compute PSO)
- Root signature (Direct3D terminology) = pipeline layout in Vulkan terminology
- Sampler state object (SO)
- Shader resource view (SRV)
- Structured buffer object (SBO)
- Task shader (TS, "amplification shader" in Direct3D terminology)
- Texture buffer object (TBO)
- Tessellation control shader (TCS), "hull shader" in Direct3D terminology
- Tessellation evaluation shader (TES), "domain shader" in Direct3D terminology
- Uniform buffer object (UBO), "constant buffer" in Direct3D terminology
- Uniform buffer view (UBV)
- Unordered access view (UAV)
- Vertex array object (VAO)
- Vertex buffer object (VBO)
- Vertex shader (VS)
- Renderer
- Asset: Lightweight content metadata like ID, type and location (texture, mesh, shader etc. - on this abstraction level everything is an asset)
- Gamma space = Perceptual color space = sRGB = Not linear
- HMD: Head mounted display
- LOD: Level of detail
- Mesh: 3D-model consisting of a vertex- and index-buffer, geometry subdivided into sub-meshes
- Material and shader blueprint: High-level rendering description
- Material: Just a property-set used as shader input
- Resource: A concrete asset type used during runtime in-memory (texture, mesh, shader etc.)
Microsoft Windows: First Example Kickoff
- Open Visual Studio 2019 and select "File -> Open -> CMake..." -> "unrimp/CMakeLists.txt"
- Build "Windows_x64_Shared" project settings
- Compile the runtime assets by starting "unrimp/Binary/Windows_x64_Shared/ExampleProjectCompiler.exe"
- Run "unrimp/Binary/Windows_x64_Shared/Examples.exe" (is using default command line arguments "unrimp/Binary/Windows_x64_Shared/Examples.exe ImGuiExampleSelector -r Direct3D11")
- For debugging with Visual Studio 2019, use "Examples.exe (Install with Arguments)" or "ExampleProjectCompiler.exe (Install with Arguments)" as startup item
- Modify "launch.vs.json" to change the Visual Studio application launch options, e.g. to start the "FirstScene"-example by default
SDL2
Microsoft Windows: Using the Unrimp examples together with- Download e.g. "SDL2-devel-2.0.10-VC.zip" from https://www.libsdl.org/download-2.0.php and extract it into "unrimp/External/Example/SDL2" (directory contains "include" and "lib")
- Click on Visual Studio 2019 -> Menu bar -> "Project" -> "Generate Cache for Unrimp", the minimal SDL2 standalone example "ExampleSDL2" is now also available (RHI only, no renderer nor renderer toolkit)
- For debugging with Visual Studio 2019, use "ExampleSDL2.exe (Install with Arguments)" as startup item
- Build and run the examples, when setting a break point inside Visual Studio 2019 at "ApplicationImplSdl2::onInitialization()" it should trigger now so you know it worked
Microsoft Windows: Targeting Android
- Install the Visual Studio 2019 "Mobile development with C++"-workload
- Build and run the "Native Activity Application (Android)"-template to verify your installation and setup
- Unrimp needs at least "android-ndk-r17" due to the usage of C++17 features like the filesystem, download this Android NDK if needed
- Open Visual Studio 2019 and select "File -> Open -> CMake..." -> "unrimp/CMakeLists.txt"
- Build "Android_arm64_Static" project settings
- TODO(co) Work-in-progress: Compile data for mobile target, upload to device to test and debug
Useful Microsoft Windows Developer Tools
- When profiling a product
- Memory
- Diagnostic tools provided by Visual Studio 2019
- Free and open-source: MTuner
- As of October 14, 2019: Doesn't work with Visual Studio 2017 (v141) x86 but works with x64
- Graphics
- Direct3D 11 graphics debugging can be done directly inside Visual Studio 2019
- CPU
- For CPU profiling the tool Very Sleepy is easy to use while providing useful results
- Compile time
- Binary size
- "Sizer - executable size breakdown (2007)": "Command line tool that reports size of things (functions, data, classes, templates, object files) in a Visual Studio compiled exe/dll. Extracts info from debug information (.pdb) file."
- As of October 14, 2017: Doesn't work with Visual Studio 2017 (v141), compile for Visual Studio 2015 (v140) if you want to analyze the binaries using Sizer
- "Sizer - executable size breakdown (2007)": "Command line tool that reports size of things (functions, data, classes, templates, object files) in a Visual Studio compiled exe/dll. Extracts info from debug information (.pdb) file."
- Memory
- Static code analysis
- Checking external dependencies of exe and dll: Dependencies which is a rewrite of the old legacy software Dependency Walker
- Texture handling related: Compressonator
- When shipping a product, use a static build and e.g. UPX to get executables even more compact on end-user-systems
- OpenGL ES 3 development: PowerVR SDK works well. Copy "libEGL.dll" and "libGLESv2.dll" from e.g. "https://github.com/powervr-graphics/Native_SDK/lib/Windows_x86_32" e.g. into "unrimp/Binary/Windows_x86d_Shared".
- "Visual Studio Spell Checker" extension to reduce the risk automatically detectable typos get into APIs were they might stay forever
- Visual Studio Markdown Editor Plugin for editing "README.md"
Useful Online Asset Data Sources
- Realtime ready meshes with textures and a web-browser realtime preview: Sketchfab, search for downloadable
- Realtime ready meshes with textures: NVIDIA ORCA: Open Research Content Archive
- Realtime ready shaders and a web-browser realtime preview: Shadertoy
- Realtime ready post-processing shaders: reshade-shaders
- Open Game Art
Asset Prefixes
The asset prefixes are just used inside the Unrimp examples and are not fixed build in. Feel free to use other asset prefixes or no prefixes at all in your projects.
- "CN" = "Compositor node"
- "CW" = "Compositor workspace"
- "M" = "Material"
- "MB" = "Material blueprint"
- "S" = "Scene"
- "SA" = "Skeleton animation"
- "SB" = "Shader blueprint"
- "SM" = "Static or skinned mesh"
- "SP" = "Shader piece"
- "T" = "Texture"
- "VA" = "Vertex attributes"
Assets
- Each source asset which gets compiled into an runtime usable asset has a ".asset"-file which defines which asset compiler should be used as well as an optional asset compiler configuration. The rule is to not manipulate the source asset itself for asset compilation but to just decorate it with additional information. For ease of use there's support for automatically in-memory generated ".asset"-files which works for asset compilers which accept just a single unique filename extension.
- Assets are referenced by using
- Renderer toolkit source asset ID naming scheme
"<name>.asset"
- Absolute: "${PROJECT_NAME}" inserts the name of the project the currently processed asset is in, only valid at the beginning of source asset IDs
- Relative: "./" uses the directory the currently processed asset is in, only valid at the beginning of source asset IDs
- Relative: "../" switches into the parent directory the currently processed asset is in, only valid at the beginning of source asset IDs
- Compiled or runtime generated asset ID naming scheme
"<project name>/<asset directory>/<asset name>"
- Renderer toolkit source asset ID naming scheme
- Examples for asset references inside source assets
- Referencing a source asset inside the same project as the currently compiled source asset
- "${PROJECT_NAME}/Blueprint/Sky/M_Sky.asset": Referencing a source asset which is inside the same project as the currently compiled source asset
- "./MB_Debug.asset": Referencing a source asset which is inside the same directory as the currently compiled source asset
- "./MyDirectory/MB_Debug.asset": Referencing a source asset which is inside a sub-directory named "MyDirectory" which is inside same directory as the currently compiled source asset
- "../MB_Debug.asset": Referencing a source asset which is inside the parent directory of the directory the currently compiled source asset is in
- "../../MB_Debug.asset": Referencing a source asset which is inside the parent directory of the parent directory of the directory the currently compiled source asset is in
- "../../MyDirectory/MB_Debug.asset": Referencing a source asset which is inside a sub-directory named "MyDirectory" which is inside the parent directory of the parent directory of the directory the currently compiled source asset is in
- "MyProject/Blueprint/Sky/M_Sky.asset": Referencing a source asset which is inside another project named "MyProject"
- "Unrimp/Texture/DynamicByCode/BlackMap2D": Referencing an asset which is dynamically created during runtime without having a compiled source asset
- Referencing a source asset inside the same project as the currently compiled source asset
Hints
- Error strategy
- Inside renderer toolkit: Exceptions in extreme, up to no error tolerance. If something smells odd, blame it to make it possible to detect problems as early as possible in the production pipeline.
- Inside renderer: The show must go on. If the floor breaks, just keep smiling and continue dancing.
- Windows using Visual Studio 2019 C++ Open Folder and CMake: IntelliSense keeps failing
- Visual Studio 2019 -> Menu bar -> "Options" -> "Text Editor" -> "C/C++" -> "Advanced" -> "Inactive Platform IntelliSense Limit" -> Set it to e.g. 16 (see https://blogs.msdn.microsoft.com/vcblog/2018/01/10/intellisense-enhancements-for-cpp-open-folder-and-cmake/ )
- How to test the 64 bit world space position support?
- Inside "SceneResourceLoader.cpp" -> "nodeDeserialization()" after reading a node, add an 100.000.000 offset to the node transform position
- A few words about Euler angles
- We use the YXZ rotation order as default (
glm::eulerAngleYXZ()
/glm::extractEulerAngleYXZ()
)- "yaw" represents a rotation around the Y-axis (= up vector)
- Then "pitch" is applied as a rotation around the local (i.e. already rotated) X-axis (= right vector)
- Finally "roll" rotates around the local Z-axis (= front vector)
- When dealing with Euler angles keep care of 'gimbal lock', at http://www.sjbaker.org/steve/omniv/eulers_are_evil.html you will find some good information about this topic.
- We use the YXZ rotation order as default (
The unified RHI can't unify some RHI implementation behaviour differences. Here's a list of hints you might want to know:
- Texel coordinate system origin
- OpenGL: Left/bottom
- Direct3D: Left/top
- See "Coordinate Systems (Direct3D 10)" at MSDN
- Pixel coordinate system
- Direct3D: See "Coordinate Systems (Direct3D 10)" at MSDN
- Clip space coordinate system [-1, -1] ... [1, 1]
- Vulkan, Direct3D, OpenGL with "GL_ARB_clip_control"-extension: Left-handed coordinate system with clip space depth value range 0..1
- OpenGL without "GL_ARB_clip_control"-extension: Right-handed coordinate system with clip space depth value range -1..1
- Additional information: "The Cg Tutorial"-book, section "4.1.8 The Projection Transform"
- Physically based shading (PBS)
MIT License
Copyright (c) 2012-2022 The Unrimp Team
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.