High performance modular plugin-based Vulkan rendering engine in C++17 with minimal dependencies.
run setup.bat
or setup.sh
, then build Stratum with CMake
Instance
- Wraps
VkInstance
- Useful functions:
Instance::Device()
: The device being used by StratumInstance::Window()
: The window being used by StratumInstance::MaxFramesInFlight()
: Tells the total number of frames in flight on the CPU
- Wraps
Device
- Wraps
VkDevice
- Accessible through
Instance::Device()
- Tracks current frame data
- Useful for grabbing a
Buffer
orDescriptorSet
used for a particular render pass
- Useful for grabbing a
- Useful functions:
Device::GetTempBuffer()
: Get a buffer that is valid for this frameDevice::GetTempDescriptorSet
: Get a descriptor set that is valid for the current frameDevice::MaxFramesInFlight()
: Tells the total number of frames in flight on the CPUDevice::FrameContextIndex()
: Tells the index of the current frame. Between 0 and MaxFramesInFlight-1
- Wraps
Buffer
- Wraps
VkBuffer
- Represents a buffer of data on the GPU
- Automatically handles staging buffers depending on the supplied usage flags
- Wraps
DescriptorSet
- Wraps
VkDescriptorSet
- Descriptor writes are buffered
- Must call
DescriptorSet::FlushWrites()
to actually write them
- Must call
- Wraps
CommandBuffer
- Wraps
VkCommandBuffer
- Passed in by most rendering functions
- Tracks active
RenderPass
,Camera
,Material
,Shader
vertex buffer, index buffer, and more to avoid unecessary API calls - Useful functions:
CommandBuffer::BindShader()
: Binds a shader pipeline. Tracks activeShader
, andCamera
CommandBuffer::BindMaterrial()
: Binds the shader pipline from the material. Tracks activeMaterial
,Shader
, andCamera
CommandBuffer::BindVertexBuffer()
: Binds a vertex buffer and sets it as activeCommandBuffer::BindIndexBuffer()
: Binds an index buffer and sets it as activeCommandBuffer::BeginRenderPass()
: Begins aRenderPass
and sets it as active.CommandBuffer::EndRenderPass()
: Ends aRenderPass
and un-sets it as active.CommandBuffer::PushConstant()
: Pushes a PushConstant value. Checks to see if the provided shader supports the PushConstant before attempting to push.
- Wraps
RenderPass
- Wraps
VkRenderPass
- Active
RenderPass
tracked byCommandBuffer
- Stores a
Framebuffer
- Automatically created by each
Camera
(unless manually supplied)
- Wraps
Framebuffer
- Wraps
VkFramebuffer
- Automatically created by each
RenderPass
(unless manually supplied)
- Wraps
Sampler
- Wraps
VkSampler
- Wraps
Window
- Provides an abstraction for the window being used to render Stratum
Scene
- Stores a collection of Objects
- See
Scene::AddObject()
andScene::RemoveObject()
(objects wont work unless they are first added to the scene)
- See
- Computes a binary BVH for the whole scene
- Allows for raycasting for objects that implement
Object::Intersect()
- Allows for raycasting for objects that implement
- Computes active lights and shadows, which can be references with
Scene::LightBuffer()
,Scene::ShadowBuffer()
, andScene::ShadowAtlas()
- Stores an
AssetManager
,InputManager
, andPluginManager
- Use
Scene::LoadModelScene()
to efficiently load multipleMeshRenderer
s (orSkinnedMeshRenderer
s) from one 3D file- Stores all vertices and indices in the same buffer
- Computes timing
Scene::TotalTime()
: Total time in seconds since Stratum has startedScene::DeltaTIme()
: Delta time in seconds between last frame and the current frame
- Stores a collection of Objects
Object
- Base class for all Scene Objects. Stores a Position, Rotation, and Scale that is used to compute an object-to-parent matrix (see
Object::ObjectToParent()
). Objects can have other Objects within them as children, allowing for hierarchical transforms. - Almost completely virtual, designed to be inhereted from
- Supports "masking" (see
Object::LayerMask()
) in order to classify different objects
- Base class for all Scene Objects. Stores a Position, Rotation, and Scale that is used to compute an object-to-parent matrix (see
Camera
- Inherets
Object
. Computes View matrices based on its transform as anObject
- Represents a camera in 3D space. Provides functionality for stereo rendering, and more
- Note: For precision, Camera view matrices are always centered at the origin. Thus the
- Inherets
Renderer
- Base class for all Renderers. Inherit and override this to implement a custom renderer.
MeshRenderer
- Renders a
Mesh
with aMaterial
. The Scene will try to batch together MeshRenderers that use the same Mesh, Material and RenderQueue and draw them using Instancing.
- Renders a
SkinnedMeshRenderer
- Renders a skinned
Mesh
with aMaterial
. InheretsMeshRenderer
, computes skinning from anAnimationRig
.
- Renders a skinned
ClothRenderer
- Renders a
Mesh
simulating soft-body spring physics along triangle edges, and aerodynamic drag along triangle faces.
- Renders a
Light
- Defines a light within the Scene's lighting system.
Gizmos
- Gizmos are a way to quickly draw simple 3D shapes such as boxes and lines, and are only drawn during the main pass when
Scene::DrawGizmos()
istrue
. To use these properly, only callGizmos
functions duringDrawGizmos()
events.
- Gizmos are a way to quickly draw simple 3D shapes such as boxes and lines, and are only drawn during the main pass when
GUI
- Provides a basic immediate-mode GUI system, similar to Unity's EditorGUI. It can draw GUI content in world-space and in screen-space.
Asset
- Represents any Asset loadable by the
AssetManager
- Used internally by the
AssetManager
- Inherit this if you intend to implement a custom Asset type (custom asset loaders within
AssetManager
planned)
- Represents any Asset loadable by the
AssetManager
- Loads and tracks Assets. Assets loaded using this do not have to be manually deleted. Use this to load common assets.
Mesh
- Stores a collection of vertices and indices on the GPU
- This means vertices and indices are not accessible unless otherwise stored
- Also stores weight data and shape key data for animations
- Also can store a triangle BVH (for raycasting)
- Static functions for creating cubes and planes (
Mesh::CreateCube()
andMesh::CreatePlane()
)
- Stores a collection of vertices and indices on the GPU
Font
- Represents a rasterized TrueType (*.ttf) font at a specific pixel size
- Can draw strings in the world or on the screen with
DrawString()
orDrawString()
Texture
- Stores a texture on the GPU
- Can compute mipmaps in the constructor
Shader
- Represents a shader compiled with Stratums ShaderCompiler. The ShaderCompiler uses reflection to determine the layout, passes, and other metadata included within shaders
- Stores both compute and graphics shaders
- Use
GetGraphics()
andGetCompute()
to get usable shader variants.
Material
- Represents a Shader with a collection of parameters. Used by
MeshRenderer
- Represents a Shader with a collection of parameters. Used by
Stratum provides a custom shader compiler. It uses SPIRV reflection and custom directives to support automatic generation of data such as descriptor and pipeline layouts. It also provides macro variants, similar to Unity. Here is a list of all supported directives:
#pragma vertex <function> <main/depth>
- Specifies vertex shader entrypoint.
pass
is optional, and defaults tomain
- Specifies vertex shader entrypoint.
#pragma fragment <function> <main/depth>
- Specifies fragment shader entrypoint.
pass
is optional, and defaults tomain
- Specifies fragment shader entrypoint.
#pragma kernel <function>
- Specifies a kernel entrypoint for a compute shader
#pragma multi_compile <keyword1> <keyword2> ...
- Specifies shader variants. The shader is compiled multiple times, defining a different keyword each time. These different compilations are referred to as variants.
#pragma render_queue <number>
- Specifies the render queue to be used by this shader. Used by
Material
.
- Specifies the render queue to be used by this shader. Used by
#pragma color_mask <mask>
- Specifies a color write mask for the fragment shader.
<mask>
must contain subset of the charactersrgba
. Examples:#pragma color_mask rgb
#pragma color_mask rgba
#pragma color_mask rg
- Specifies a color write mask for the fragment shader.
#pragma zwrite <true/false>
- Specifies whether this shader writes to the zbuffer
#pragma ztest <true/false>
- Specifies whether this shader uses depth-testing
#pragma depth_op <less/greater/lequal/gequal/equal/nequal/never/always
>`- Specifies the depth compare op.
#pragma cull <front/back/false>
- Specifies the culling mode
#pragma fill <solid/line/point>
- Specifes the fill mode
#pragma blend <opaque/alpha/add/multiply>
- Specifies the blend mode
#pragma array <name> <number>
- Specifies that the descriptor named
<name>
is an array of size<number>
. This is used in addition to specifying the descriptor as an array in native syntax (GLSL or HLSL)
- Specifies that the descriptor named
#pragma static_sampler <name> <magFilter=linear> <minFilter=linear> <filter=linear> <addressModeU=repeat> <addressModeV=repeat> <addressModeW=repeat> <addressMode=repeat> <maxAnisotropy=2> <borderColor=int_opaque_black> <unnormalizedCoordinates=false> <compareOp=always> <mipmapMode=linear> <minLod=0> <maxLod=12> <mipLodBias=0>
- Specifies that the sampler descriptor named
<name>
is a static/immutable sampler. All arguments after<name>
are optional and defaulted to the above values, and can be specified asargument=value
Examples:#pragma static_sampler ShadowSampler maxAnisotropy=0 maxLod=0 addressMode=clamp_border borderColor=float_opaque_white compareOp=less
- Specifies that the sampler descriptor named
Each frame follows the following sequence of events:
InputDevice::NextFrame()
- Acquire SwapChain image
- Get a
CommandBuffer
- Scene Update
- Fixed Update Loop
Object::FixedUpdate()
Plugin::FixedUpdate()
Plugin::PreUpdate()
Plugin::Update()
Plugin::PostUpdate()
- Fixed Update Loop
- Render
- Scene PreFrame
Renderer::PreFrame()
- Sort Cameras, use highest-priority Camera as the main camera
- Compute active lights & shadow cameras
- For each shadow-casting light Render
PASS_DEPTH
- Resolve ShadowAtlas
- Render
PASS_MAIN
for each camera (highest priorty first) - Resolve cameras
Plugin::PostProcess()
- Copy cameras with
TargetWindow
set to the screen Camera::PostRender()
- Scene PreFrame
- Execute
CommandBuffer
Plugin::PrePresent()
- Wait for GPU to finish the oldest buffered frame before continuing (triple-buffering)
Each "Render <PASS>
" call above follows the following sequence of events:
- Use Scene BVH to find Renderers in view
- Sort Renderers based on
RenderQueue
Camera::PreRender()
(Updates Camera Framebuffer and Viewport)Plugin::PreRender()
Renderer::PreRender()
- Begin RenderPass, clear Framebuffer if
clear
istrue
Camera::Set()
(Updates Camera Uniform buffer and sets Viewport and Scissor)Plugin::PreRenderScene()
- Render Skybox (only for
PASS_MAIN
) - Render loop
- For
MeshRenderer
s with the sameRenderQueue
,Material
, andMesh
(given theMaterial
's shader supports Instancing by using theInstances
uniform):MeshRenderer::DrawInstanced()
- For other
Renderer
s:Renderer::Draw()
- For
- Draw Gizmos (only if pass is
PASS_MAIN
andScene::DrawGizmos
istrue
)Object::DrawGizmos()
Plugin::DrawGizmos()
- Draw GUI (only for
PASS_MAIN
) Plugin::PostRenderScene()
- End RenderPass
Camera
s have a StereoMode
property which is implemented as such:
- While rendering a
Renderer
:- For each
STEREO_EYE
- Call
Camera::SetStereo()
- This sets the correct Scissor and
StereoEye
push constant
- This sets the correct Scissor and
- Render your Renderer
- See
Render()
inScene/MeshRenderer.cpp
for an example
- See
- Call
- For each
- View matrices for each eye computed by multiplying the Camera's non-stereo View matrix by the EyeTransform of each eye (see
Camera::EyeTransform
) - Projection matrices are either manually supplied (see
Camera::Projection
) or computed from theNear
,Far
, andFieldOfView
parameters (orOrthographicSize
ifOrthographic
is set)