Vortex2D

2D real-time fluid engine

Introduction

This is a 2D engine with the goal of being used in real-time scenarios, e.g. video games, in the same manner as a Box2D.

It is a hybrid engine that uses particles for the advection part (moving the particles given their velocities) and a grid to resolve the incompressible constrains. However, the particles are not visible to the user and you only work with the grid directly.

The engine runs directly on the GPU using Vulkan compute shaders. The rendering is then also done with Vulkan. The grids are represented by textures and operations by the user are all done by drawing shapes on the textures. The engine itself is written in C++ and it provides a simple wrapper around Vulkan and the basic rendering functionalities: shapes, textures, sprites, blending, render textures, etc.

Table of content

Setup

Vortex2D is multi-platform and currently supports the following:

  • Windows
  • Linux
  • macOS
  • iOS

CMake is used to generate the appropriate build scripts for each platform. The dependencies, which are fetched when calling cmake are:

  • glm
  • SPIRV-cross

for the tests:

  • gtest

for the examples:

  • glfw

The only dependency required is python. There a several variables that can be used to configure:

  • VORTEX2D_ENABLE_TESTS builds the tests
  • VORTEX2D_ENABLE_EXAMPLES builds the examples
  • VORTEX2D_ENABLE_DOCS builds the documentation

The main library is built as a dll on windows, shared library on linux and (dynamic) framework on macOS/iOS.

Prerequisite
Following are necessary:
  • Python
  • glslangValidator (comes with Vulkan SDK)
Windows

To build on windows, cmake-gui is the easiest to use. Only the variables specified above should be changed.

Linux

Again, regular cmake commands should be use to configure cmake:

cmake ..
macOS

In addition to the normal variables, we need to specify the location of MoltenVK:

cmake .. -DMOLTENVK_DIR=../../MoltenVK/Package/Latest/MoltenVK/ -DVORTEX2D_ENABLE_EXAMPLES=On
iOS

The framework needs to signed on iOS, so the following variables need to be defined:

  • CODE_SIGN_IDENTITY should be set to “iPhone Developer”
  • DDEVELOPMENT_TEAM_ID should be set to the team id, can be found on the apple developer portal
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DIOS_PLATFORM=OS -DIOS_ARCH=arm64 -DENABLE_VISIBILITY=true -DMOLTENVK_DIR=../../MoltenVK/Package/Latest/MoltenVK/ -DCODE_SIGN_IDENTITY="iPhone Developer" -DDEVELOPMENT_TEAM_ID=XXXXXX
Documentation

To build the documentation the following is required:

  • doxygen
  • sphinx
  • breath sphinx theme

Rendering

Initialization

The rendering API is very basic and supports only the most basic functionality.

Create an instance of Vortex2D::Renderer::Instance which is then used to create an instance of Vortex2D::Renderer::Device.

The device is then used to create any other object. The main one is the Vortex2D::Renderer::RenderWindow which is a window where to render sprites and polygons. The function Vortex2D::Fluid::RenderWindow::Display() is then used to present the result to the screen.

Vortex2D::Renderer::Instance instance("Application name", extensions); // pass list of required extensions
Vortex2D::Renderer::Device device(instance.GetPhysicalDevice(), surface);

Vortex2D::Renderer::RenderWindow window(device, surface, width, height);

Note that the instance requires a list of extensions necessary to create a window. With GLFW they can be retrived as:

std::vector<const char*> GetGLFWExtensions()
{
    std::vector<const char*> extensions;
    unsigned int glfwExtensionCount = 0;
    const char** glfwExtensions;

    // get the required extensions from GLFW
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
    for (unsigned int i = 0; i < glfwExtensionCount; i++)
    {
        extensions.push_back(glfwExtensions[i]);
    }

    return extensions;
}

In addition, you also need to create a surface which can be also done with the help of GLFW:

vk::UniqueSurfaceKHR GetGLFWSurface(GLFWwindow* window, vk::Instance instance)
{
    // create surface
    VkSurfaceKHR surface;
    if (glfwCreateWindowSurface(static_cast<VkInstance>(instance), window, nullptr, &surface) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create window surface!");
    }

    return vk::UniqueSurfaceKHR(surface, vk::SurfaceKHRDeleter{instance});
}
Rendering shapes

We are now ready to draw things on the screen. Let’s start with some shapes like rectangles and circles:

Vortex2D::Renderer::Rectangle rectangle(device, {100.0f, 100.0f});
Vortex2D::Renderer::Ellipse circle(device, {50.0f, 50.0f});

rectangle.Colour = {1.0f, 0.0f, 0.0f, 1.0f};
rectangle.Position = {200.0f, 100.0f};

circle.Colour = {0.0f, 0.0f, 1.0f, 1.0f};
circle.Position = {500.0f, 400.0f};

auto blendMode = vk::PipelineColorBlendAttachmentState()
    .setBlendEnable(true)
    .setAlphaBlendOp(vk::BlendOp::eAdd)
    .setColorBlendOp(vk::BlendOp::eAdd)
    .setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
    .setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
    .setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
    .setDstAlphaBlendFactor(vk::BlendFactor::eZero);

// note that rectangle, circle and render need to be alive for the duration of the rendering
auto render = renderTarget.Record({rectangle, circle}, blendMode);
render.Submit();
Rendering textures

Of course we can also render textures, using sprites.

Vortex2D::Renderer::Texture texture(device, 100, 100, vk::Format::eR8G8B8A8Unorm);
Vortex2D::Renderer::Sprite sprite(device, texture);

Level sets

A level set is a signed distance field. It’s a field containing positive or negative value, where the values are 0 represent a contour, or border. This is used to represent shapes, the numbers give you the distance to the shape border. It’s the fundamental way that we represent the area of a fluid and the area of the obstacles, i.e. the boundaries.

Signed distance field
Basic shapes

There are several shapes that will set a level set, we draw them like we draw any shape. The format of the Vortex2D::Renderer::RenderTarget() must be vk::Format::eR32Sfloat. The class Vortex2D::Fluid::LevelSet() fullfills this.

Vortex2D::Fluid::Rectangle rectangle(device, {100.0f, 100.0f});
rectangle.Position = {40.0f, 60.0f};

Vortex2D::Fluid::LevelSet levelSet(device, {400, 400});
auto renderCmd = levelSet.Record({rectangle});
renderCmd.Submit(); // note that renderCmd and rectangle have to be alive untill the rendering is done
Re-initialization

We can also draw any other shape, and then transform the data into a correct level set. This operation is call re-initialization.

World

The world classes are the centre of the engine, where the fluid gets animated.

Velocity Field

The velocity field is field where each entry gives the current speed of the fluid and its direction. This field is used to evolve the fluid, which means that additional velocity can be added (or substracted) to it.

Smoke World

This is a type of fluid simulation where the fluid area doesn’t move.

Water World

This is a classical water type of fluid simulation.

Rigid body

Rigid bodies are the way to have dynamic interations with the fluid (other then changing the velocity field directly)

Static bodies

Static bodies act on the fluid, but the fluid doesn’t act on the fluid. This is called a weak coupling between the rigid body and the fluid. They have a velocity that is imparted on the fluid. Think of motorized objects pushing through the fluid.

Dynamic bodies

Dynamic bodies are affected by the fluid. They can also in turn, affect the fluid, which is called a strong coupling with the fluid.

Renderer API reference

API Reference
namespace Renderer

Typedefs

using Vortex2D::Renderer::DescriptorTypeBindings = typedef std::map<uint32_t, vk::DescriptorType>
typedef std::vector<glm::vec2> Path

Functions

void BufferBarrier(vk::Buffer buffer, vk::CommandBuffer commandBuffer, vk::AccessFlags oldAccess, vk::AccessFlags newAccess)

Inserts a barrier for the given buffer, command buffer and access.

Parameters
  • buffer: the vulkan buffer handle
  • commandBuffer: the command buffer to inserts the barrier
  • oldAccess: old access
  • newAccess: new access

template <template< typename > class BufferType, typename T>
void CopyTo(BufferType<T> &buffer, T &t)

Copy the content of a buffer in an object.

template <template< typename > class BufferType, typename T>
void CopyTo(BufferType<T> &buffer, std::vector<T> &t)

Copy the content of a buffer to a vector. Vector needs to have the correct size already.

template <template< typename > class BufferType, typename T>
void CopyFrom(BufferType<T> &buffer, const T &t)

Copy the content of an object to the buffer.

template <template< typename > class BufferType, typename T>
void CopyFrom(BufferType<T> &buffer, const std::vector<T> &t)

Copy the content of a vector to the buffer.

void VORTEX2D_API Vortex2D::Renderer::ExecuteCommand(const Device & device, CommandBuffer::CommandFn commandFn)

Runs immediately a set of commands and waits for them to finish.

Parameters
  • device: vulkan device
  • commandFn: lambda that runs the commands.

bool operator==(const ShaderLayout &left, const ShaderLayout &right)
bool operator==(const PipelineLayout &left, const PipelineLayout &right)
vk::DescriptorType GetDescriptorType(uint32_t bind, const PipelineLayout &layout)
VORTEX2D_API void Vortex2D::Renderer::Bind(const Device & device, vk::DescriptorSet dstSet, const PipelineLayout & layout, const std::vector< BindingInput > & bindingInputs)

Bind the resources (buffer or texture/sampler) to a DescriptorSet.

Parameters
  • device: vulkan device
  • dstSet: vulkan descriptor set
  • layout: pipeline layout
  • bindingInputs: list of resources (buffer or texture/sampler)

bool HasLayer(const char *extension, const std::vector<vk::LayerProperties> &availableExtensions)
bool HasExtension(const char *extension, const std::vector<vk::ExtensionProperties> &availableExtensions)
VORTEX2D_API vk::UniquePipeline Vortex2D::Renderer::MakeComputePipeline(vk::Device device, vk::ShaderModule shader, vk::PipelineLayout layout, uint32_t localX, uint32_t localY)
bool operator==(const RenderState &left, const RenderState right)
VORTEX2D_API ComputeSize Vortex2D::Renderer::MakeStencilComputeSize(const glm::ivec2 & size, int radius)

Create a ComputeSize for a stencil type shader.

Return
calculate ComputeSize
Parameters
  • size: the domain size
  • radius: the stencil size

VORTEX2D_API ComputeSize Vortex2D::Renderer::MakeCheckerboardComputeSize(const glm::ivec2 & size)

Create a ComputeSize for a checkerboard type shader.

Return
calculate ComputeSize
Parameters
  • size: the domain size

class AbstractShape : public Vortex2D::Renderer::Drawable, public Vortex2D::Renderer::Transformable
#include <Shapes.h>

An polygonal shape where the fragment shader can be specified for customisation.

Subclassed by Vortex2D::Renderer::IntRectangle, Vortex2D::Renderer::Rectangle

Public Functions

AbstractShape(const Device &device, const SpirvBinary &fragShader, const std::vector<glm::vec2> &vertices)
AbstractShape(AbstractShape &&other)
virtual VORTEX2D_API ~AbstractShape()
void Initialize(const RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

Public Members

glm::vec4 Colour
class AbstractSprite : public Vortex2D::Renderer::Drawable, public Vortex2D::Renderer::Transformable
#include <Sprite.h>

a Sprite, i.e. a drawable that can render a texture. The fragment shader can be specified for customisation.

Subclassed by Vortex2D::Fluid::DistanceField, Vortex2D::Renderer::Sprite

Public Functions

AbstractSprite(const Device &device, const SpirvBinary &fragShaderName, Texture &texture)
AbstractSprite(AbstractSprite &&other)
virtual VORTEX2D_API ~AbstractSprite()
void Initialize(const RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

template <typename T>
void PushConstant(vk::CommandBuffer commandBuffer, uint32_t offset, const T &data)

Public Members

glm::vec4 Colour = {1.0f, 1.0f, 1.0f, 1.0f}
struct BindingInput
#include <DescriptorSet.h>

The texture/sampler or buffer that can be binded to a shader.

Public Functions

BindingInput(Renderer::GenericBuffer &buffer, uint32_t bind = DefaultBind)
BindingInput(Renderer::Texture &texture, uint32_t bind = DefaultBind)
BindingInput(vk::Sampler sampler, Renderer::Texture &texture, uint32_t bind = DefaultBind)

Public Members

uint32_t Bind
mpark::variant<Renderer::GenericBuffer *, DescriptorImage> Input

Public Static Attributes

constexpr uint32_t DefaultBind = static_cast<uint32_t>(-1)
template <typename T>
class Buffer : public Vortex2D::Renderer::GenericBuffer
#include <Buffer.h>

a storage buffer type of buffer

Public Functions

Buffer(const Device &device, std::size_t size = 1, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)
class Clear : public Vortex2D::Renderer::Drawable
#include <Shapes.h>

A drawable that simply clears the target.

Public Functions

Clear(const glm::vec4 &colour)
void Initialize(const RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

class CommandBuffer
#include <CommandBuffer.h>

Can record commands, then submit them (multiple times). A fence can used to wait on the completion of the commands.

Public Types

using CommandFn = std::function<void(vk::CommandBuffer)>

Public Functions

CommandBuffer(const Device &device, bool synchronise = true)

Creates a command buffer which can be synchronized.

Parameters
  • device: vulkan device
  • synchronise: flag to determine if the command buffer can be waited on.

~CommandBuffer()
CommandBuffer(CommandBuffer &&other)
CommandBuffer &operator=(CommandBuffer &&other)
void Record(CommandFn commandFn)

Record some commands. The commads are recorded in the lambda which is immediately executed.

Parameters
  • commandFn: a functor, or simply a lambda, where commands are recorded.

void Record(const RenderTarget &renderTarget, vk::Framebuffer framebuffer, CommandFn commandFn)

Record some commands inside a render pass. The commads are recorded in the lambda which is immediately executed.

Parameters
  • renderTarget: the render target which contains the render pass to record into
  • framebuffer: the frame buffer where the render pass will render.
  • commandFn: a functor, or simply a lambda, where commands are recorded.

void Wait()

Wait for the command submit to finish. Does nothing if the synchronise flag was false.

void Reset()

Reset the command buffer so it can be recorded again.

void Submit(const std::initializer_list<vk::Semaphore> &waitSemaphores = {}, const std::initializer_list<vk::Semaphore> &signalSemaphores = {})

submit the command buffer

operator bool() const

explicit conversion operator to bool, indicates if the command was properly recorded and can be sumitted.

struct ComputeSize
#include <Work.h>

Used for a compute shader, and defines the group size, local size and domain size.

Public Functions

ComputeSize(const glm::ivec2 &size)

Creates a ComputeSize using a 2D domain size and the default 2D local size.

Parameters
  • size: the domain size

ComputeSize(int size)

Creates a ComputeSize using a 1D domain size and the default 1D local size.

Parameters
  • size: the domain size

Public Members

glm::ivec2 DomainSize
glm::ivec2 WorkSize
glm::ivec2 LocalSize

Public Static Functions

glm::ivec2 GetLocalSize2D()

The default local size for 2D compute shaders.

Return
a 2d vector

int GetLocalSize1D()

The default local size for 1D compute shaders.

Return
a integer value

glm::ivec2 GetWorkSize(const glm::ivec2 &size)

Computes the 2D group size given a domain size.

Return
the group size
Parameters
  • size: the domain size of the shader

glm::ivec2 GetWorkSize(int size)

Computes the 1D group size given a domain size.

Return
the group size
Parameters
  • size: the domain size of the shader

ComputeSize Default2D()

A default ComputeSize using the default 2D local size. The domain size is (1,1)

Return
a default compute size

ComputeSize Default1D()

A default ComputeSize using the default 1D local size. The domain size is (1,1)

Return
a default compute size

struct DescriptorImage
#include <DescriptorSet.h>

The texture or sampler that can be binded to a shader.

Public Functions

DescriptorImage(vk::Sampler sampler, Renderer::Texture &texture)
DescriptorImage(Renderer::Texture &texture)

Public Members

vk::Sampler Sampler
Renderer::Texture *Texture
struct DescriptorSet
#include <DescriptorSet.h>

The binding of an object for a shader.

Public Members

vk::UniqueDescriptorSet descriptorSet
vk::PipelineLayout pipelineLayout
vk::DescriptorSetLayout descriptorSetLayout
class Device
#include <Device.h>

Encapsulation around the vulkan device. Allows to create command buffers, layout, bindings, memory and shaders.

Public Functions

Device(vk::PhysicalDevice physicalDevice, bool validation = true)
Device(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, bool validation = true)
Device(vk::PhysicalDevice physicalDevice, int familyIndex, bool validation = true)
~Device()
Device(Device&&)
Device &operator=(Device&&)
vk::Device Handle() const
vk::Queue Queue() const
vk::PhysicalDevice GetPhysicalDevice() const
int GetFamilyIndex() const
std::vector<vk::CommandBuffer> CreateCommandBuffers(uint32_t size) const
void FreeCommandBuffers(vk::ArrayProxy<const vk::CommandBuffer> commandBuffers) const
VmaAllocator Allocator() const
LayoutManager &GetLayoutManager() const
vk::ShaderModule GetShaderModule(const SpirvBinary &spirv) const
struct DispatchParams
#include <Work.h>

Parameters for indirect compute: group size, local size, etc.

Public Functions

DispatchParams(int count)

Public Members

vk::DispatchIndirectCommand workSize
uint32_t count
struct Drawable
#include <Drawable.h>

Interface of a drawable object.

Subclassed by Vortex2D::Fluid::Circle, Vortex2D::Fluid::Polygon, Vortex2D::Renderer::AbstractShape, Vortex2D::Renderer::AbstractSprite, Vortex2D::Renderer::Clear, Vortex2D::Renderer::Ellipse

Public Functions

virtual ~Drawable()
virtual void Initialize(const RenderState &renderState) = 0

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

virtual void Update(const glm::mat4 &projection, const glm::mat4 &view) = 0

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

virtual void Draw(vk::CommandBuffer commandBuffer, const RenderState &renderState) = 0

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

class Ellipse : public Vortex2D::Renderer::Drawable, public Vortex2D::Renderer::Transformable
#include <Shapes.h>

A solid colour ellipse. Implements the Drawable interface and Transformable interface.

Public Functions

Ellipse(const Device &device, const glm::vec2 &radius)
void Initialize(const RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

Public Members

glm::vec4 Colour
class GenericBuffer
#include <Buffer.h>

A vulkan buffer which can be on the host or the device.

Subclassed by Vortex2D::Renderer::Buffer< float >, Vortex2D::Renderer::Buffer< glm::ivec2 >, Vortex2D::Renderer::Buffer< glm::vec2 >, Vortex2D::Renderer::Buffer< int >, Vortex2D::Renderer::Buffer< Renderer::DispatchParams >, Vortex2D::Renderer::Buffer< Vortex2D::Fluid::Particle >, Vortex2D::Renderer::Buffer< Vortex2D::Fluid::RigidBody::Velocity >, Vortex2D::Renderer::Buffer< Vortex2D::Fluid::RigidBody::Vortex2D::Fluid::Velocity >, Vortex2D::Renderer::IndirectBuffer< Renderer::DispatchParams >, Vortex2D::Renderer::UniformBuffer< glm::mat4 >, Vortex2D::Renderer::UniformBuffer< glm::vec4 >, Vortex2D::Renderer::UniformBuffer< Size >, Vortex2D::Renderer::UniformBuffer< Vortex2D::Fluid::RigidBody::Velocity >, Vortex2D::Renderer::VertexBuffer< glm::vec2 >, Vortex2D::Renderer::VertexBuffer< Vortex2D::Renderer::AbstractSprite::Vertex >, Vortex2D::Renderer::Buffer< T >, Vortex2D::Renderer::IndirectBuffer< T >, Vortex2D::Renderer::UniformBuffer< T >, Vortex2D::Renderer::VertexBuffer< T >

Public Functions

GenericBuffer(const Device &device, vk::BufferUsageFlags usageFlags, VmaMemoryUsage memoryUsage, vk::DeviceSize deviceSize)
~GenericBuffer()
GenericBuffer(GenericBuffer &&other)
void CopyFrom(vk::CommandBuffer commandBuffer, GenericBuffer &srcBuffer)

Copy a buffer to this buffer.

Parameters
  • commandBuffer: command buffer to run the copy on.
  • srcBuffer: the source buffer.

void CopyFrom(vk::CommandBuffer commandBuffer, Texture &srcTexture)

Copy a texture to this buffer.

Parameters
  • commandBuffer: command buffer to run the copy on.
  • srcTexture: the source texture

vk::Buffer Handle() const

The vulkan handle.

vk::DeviceSize Size() const

The size in bytes of the buffer.

void Barrier(vk::CommandBuffer commandBuffer, vk::AccessFlags oldAccess, vk::AccessFlags newAccess)

Inserts a barrier for this buffer.

Parameters
  • commandBuffer: the command buffer to run the barrier
  • oldAccess: old access
  • newAccess: new access

void Clear(vk::CommandBuffer commandBuffer)

Clear the buffer with 0.

Parameters
  • commandBuffer: the command buffer to clear on

Friends

template <template< typename > class BufferType, typename T>
void CopyFrom(BufferType<T> &buffer, const T &t)

Copy the content of an object to the buffer.

template <template< typename > class BufferType, typename T>
void CopyTo(BufferType<T> &buffer, T &t)

Copy the content of a buffer in an object.

template <template< typename > class BufferType, typename T>
void CopyTo(BufferType<T> &buffer, std::vector<T> &t)

Copy the content of a buffer to a vector. Vector needs to have the correct size already.

template <template< typename > class BufferType, typename T>
void CopyFrom(BufferType<T> &buffer, const std::vector<T> &t)

Copy the content of a vector to the buffer.

class GraphicsPipeline
#include <Pipeline.h>

graphics pipeline which caches the pipeline per render states.

Public Functions

GraphicsPipeline()
GraphicsPipeline(Builder builder)
void Create(vk::Device device, const RenderState &renderState)
void Bind(vk::CommandBuffer commandBuffer, const RenderState &renderState)
class Builder
#include <Pipeline.h>

Builder for graphics pipeline.

Public Functions

Builder()
GraphicsPipeline::Builder &Shader(vk::ShaderModule shader, vk::ShaderStageFlagBits shaderStage)

Set the shader.

Return
*this
Parameters
  • shader: the loaded shader
  • shaderStage: shader state (vertex, fragment or compute)

GraphicsPipeline::Builder &VertexAttribute(uint32_t location, uint32_t binding, vk::Format format, uint32_t offset)

Sets the vertex attributes.

Return
*this
Parameters
  • location: location in the shader
  • binding: binding in the shader
  • format: vertex format
  • offset: offset in the vertex

GraphicsPipeline::Builder &VertexBinding(uint32_t binding, uint32_t stride, vk::VertexInputRate inputRate = vk::VertexInputRate::eVertex)

Sets the vertex binding.

Return
*this
Parameters
  • binding: binding in the shader
  • stride: stride in bytes
  • inputRate: inpute rate

GraphicsPipeline::Builder &Topology(vk::PrimitiveTopology topology)
GraphicsPipeline::Builder &Layout(vk::PipelineLayout pipelineLayout)
vk::UniquePipeline Create(vk::Device device, const RenderState &renderState)
template <typename T>
class IndirectBuffer : public Vortex2D::Renderer::GenericBuffer
#include <Buffer.h>

an indirect buffer type of buffer, used for compute indirect dispatch

Public Functions

IndirectBuffer(const Device &device, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)
class Instance
#include <Instance.h>

Vulkan instance, which extensions enabled.

Public Functions

Instance(const std::string &name, std::vector<const char *> extensions, bool validation)
~Instance()
vk::PhysicalDevice GetPhysicalDevice() const
vk::Instance GetInstance() const
class IntRectangle : public Vortex2D::Renderer::AbstractShape
#include <Shapes.h>

A solid colour rectangle as Rectangle, however uses integer colors and is meant to be drawn to a framebuffer wiht integer colours.

Public Functions

IntRectangle(const Device &device, const glm::vec2 &size)
class LayoutManager
#include <DescriptorSet.h>

Caches and creates layouts and bindings.

Public Functions

LayoutManager(const Device &device)
void CreateDescriptorPool()

Create or re-create the descriptor pool, will render invalid existing descriptor sets.

DescriptorSet MakeDescriptorSet(const PipelineLayout &layout)

Create the descriptor set given the layout.

Return
built descriptor set
Parameters
  • layout: pipeline/shader layout

vk::DescriptorSetLayout GetDescriptorSetLayout(const PipelineLayout &layout)

Create, cache and return a descriptor layout given the pipeline layout.

Return
cached descriptor set layout
Parameters
  • layout: pipeline layout

vk::PipelineLayout GetPipelineLayout(const PipelineLayout &layout)

create, cache and return a vulkan pipeline layout given the layout

Return
vulkan pipeline layout
Parameters
  • layout: pipeline layout

struct PipelineLayout
#include <DescriptorSet.h>

Represents the layout of a pipeline: vertex + fragment or compute.

Public Members

std::vector<ShaderLayout> layouts
template <typename T>
class property
#include <Transformable.h>

Simple class to simulate properties like in C#.

Public Functions

property(Transformable &t)
T &operator=(const T &i)
operator T const&() const
class Rectangle : public Vortex2D::Renderer::AbstractShape
#include <Shapes.h>

A solid colour rectangle defined by two triangles. Implements the Drawable interface and Transformable interface.

Public Functions

Rectangle(const Device &device, const glm::vec2 &size)
class RenderCommand
#include <CommandBuffer.h>

A special command buffer that has been recorded by a RenderTarget. It can be used to submit the rendering. The object has to stay alive untill rendering is complete.

Public Functions

RenderCommand()
~RenderCommand()
RenderCommand(RenderCommand &&other)
RenderCommand &operator=(RenderCommand &&other)
RenderCommand &Submit(const glm::mat4 &view = glm::mat4(1.0f))

Submit the render command with a transform matrix.

Return
*this
Parameters
  • view: a transform matrix

void Wait()

Wait for the render command to complete.

Friends

friend Vortex2D::Renderer::RenderTexture
friend Vortex2D::Renderer::RenderWindow
class RenderpassBuilder
#include <RenderTarget.h>

Factory for a vulkan render pass.

Public Functions

RenderpassBuilder &Attachement(vk::Format format)

Format of the render pass.

Return
Parameters
  • format:

RenderpassBuilder &AttachementLoadOp(vk::AttachmentLoadOp value)

operation to perform when loading the framebuffer (clear, load, etc)

Return
Parameters
  • value:

RenderpassBuilder &AttachementStoreOp(vk::AttachmentStoreOp value)

operation to perform when storing the framebuffer (clear, save, etc)

Return
Parameters
  • value:

RenderpassBuilder &AttachementInitialLayout(vk::ImageLayout layout)

Layout of the image to be before render pass.

Return
Parameters
  • layout:

RenderpassBuilder &AttachementFinalLayout(vk::ImageLayout layout)

Layout of the image to be after render pass.

Return
Parameters
  • layout:

RenderpassBuilder &Subpass(vk::PipelineBindPoint bindPoint)

Define subpass of the render pass.

Return
Parameters
  • bindPoint:

RenderpassBuilder &SubpassColorAttachment(vk::ImageLayout layout, uint32_t attachment)

Set the color attachment with index.

Return
Parameters
  • layout:
  • attachment: index of the attachment

RenderpassBuilder &Dependency(uint32_t srcSubpass, uint32_t dstSubpass)

Dependency of the subpasses.

Return
Parameters
  • srcSubpass:
  • dstSubpass:

RenderpassBuilder &DependencySrcStageMask(vk::PipelineStageFlags value)
RenderpassBuilder &DependencyDstStageMask(vk::PipelineStageFlags value)
RenderpassBuilder &DependencySrcAccessMask(vk::AccessFlags value)
RenderpassBuilder &DependencyDstAccessMask(vk::AccessFlags value)
vk::UniqueRenderPass Create(vk::Device device)

Create the render pass.

Return
Parameters
  • device:

class RenderState
#include <RenderState.h>

the various state to render to a target: size, render pass and blend.

Public Functions

RenderState(const RenderTarget &renderTarget)

Initialize for a render target with default blend.

Parameters
  • renderTarget:

RenderState(const RenderTarget &renderTarget, vk::PipelineColorBlendAttachmentState colorBlend)

Initialize for a render target with a given blend.

Parameters
  • renderTarget:
  • colorBlend:

Public Members

uint32_t Width
uint32_t Height
vk::RenderPass RenderPass
vk::PipelineColorBlendAttachmentState ColorBlend
struct RenderTarget
#include <RenderTarget.h>

A target that can be rendered to. This is implemented by the RenderWindow and the RenderTexture.

Subclassed by Vortex2D::Renderer::RenderTexture, Vortex2D::Renderer::RenderWindow

Public Types

using DrawableList = std::initializer_list<std::reference_wrapper<Drawable>>

Public Functions

RenderTarget(uint32_t width, uint32_t height)
RenderTarget(RenderTarget &&other)
~RenderTarget()
virtual VORTEX2D_API RenderCommand Vortex2D::Renderer::RenderTarget::Record(DrawableList drawables, vk::PipelineColorBlendAttachmentState blendMode = {}) = 0
virtual VORTEX2D_API void Vortex2D::Renderer::RenderTarget::Submit(RenderCommand & renderCommand) = 0

Public Members

uint32_t Width
uint32_t Height
glm::mat4 Orth
glm::mat4 View
vk::UniqueRenderPass RenderPass
class RenderTexture : public Vortex2D::Renderer::RenderTarget, public Vortex2D::Renderer::Texture
#include <RenderTexture.h>

A render target that renders into a texture.

Subclassed by Vortex2D::Fluid::Density, Vortex2D::Fluid::LevelSet, Vortex2D::Fluid::ParticleCount, Vortex2D::Fluid::Velocity

Public Functions

RenderTexture(const Device &device, uint32_t width, uint32_t height, vk::Format format)
RenderCommand Record(DrawableList drawables, vk::PipelineColorBlendAttachmentState blendMode = {})
void Submit(RenderCommand &renderCommand)
class RenderWindow : public Vortex2D::Renderer::RenderTarget
#include <RenderWindow.h>

Render to a swapchain, i.e. to the window/surface.

Public Functions

RenderWindow(const Device &device, vk::SurfaceKHR surface, uint32_t width, uint32_t height)

Initialize with a given surface and size.

Parameters
  • device: vulkan device
  • surface: vulkan surface
  • width:
  • height:

RenderCommand Record(DrawableList drawables, vk::PipelineColorBlendAttachmentState blendMode = {})
void Submit(RenderCommand &renderCommand)
void Display()

Submits all the render command and present the surface for display.

class SamplerBuilder
#include <Texture.h>

Factory for a vullkan sampler.

Public Functions

SamplerBuilder()
SamplerBuilder &AddressMode(vk::SamplerAddressMode mode)

Mode of the sampler: repeat, clamp, etc.

Return
*this
Parameters
  • mode: vulkan mode

SamplerBuilder &Filter(vk::Filter filter)

Filter of the sampler: linear, nearest, etc.

Return
*this
Parameters
  • filter: vulkan filter

vk::UniqueSampler Create(vk::Device device)

Create the vulkan sampler.

Return
unique sampler
Parameters
  • device: vulkan device

struct ShaderLayout
#include <DescriptorSet.h>

Represents the layout of a shader (vertex, fragment or compute)

Public Functions

ShaderLayout(const SPIRV::Reflection &reflection)

Public Members

vk::ShaderStageFlags shaderStage
DescriptorTypeBindings bindings
unsigned pushConstantSize
class SpirvBinary
#include <Device.h>

A binary SPIRV shader, to be feed to vulkan.

Public Functions

template <std::size_t N>
SpirvBinary(const uint32_t (&spirv)[N])
const uint32_t *data() const
std::size_t size() const
std::size_t words() const
class Sprite : public Vortex2D::Renderer::AbstractSprite
#include <Sprite.h>

A sprite that renders a texture with a simple pass-through fragment shader.

Subclassed by Vortex2D::Fluid::Density

Public Functions

Sprite(const Device &device, Texture &texture)
struct SwapChainSupportDetails

Public Functions

SwapChainSupportDetails(vk::PhysicalDevice device, vk::SurfaceKHR surface)
bool IsValid() const

Public Members

vk::SurfaceCapabilitiesKHR capabilities
std::vector<vk::SurfaceFormatKHR> formats
std::vector<vk::PresentModeKHR> presentModes
class Texture
#include <Texture.h>

A texture, or in vulkan terms, an image.

Subclassed by Vortex2D::Renderer::RenderTexture

Public Functions

Texture(const Device &device, uint32_t width, uint32_t height, vk::Format format, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)
~Texture()
Texture(Texture &&other)
template <typename T>
void CopyFrom(const std::vector<T> &data)
template <typename T>
void CopyTo(std::vector<T> &data)
void CopyFrom(vk::CommandBuffer commandBuffer, Texture &srcImage)
void Barrier(vk::CommandBuffer commandBuffer, vk::ImageLayout oldLayout, vk::AccessFlags oldAccess, vk::ImageLayout newLayout, vk::AccessFlags newAccess)
vk::ImageView GetView() const
uint32_t GetWidth() const
uint32_t GetHeight() const
vk::Format GetFormat() const
void Clear(vk::CommandBuffer commandBuffer, const std::array<int, 4> &colour)
void Clear(vk::CommandBuffer commandBuffer, const std::array<float, 4> &colour)

Friends

friend Vortex2D::Renderer::GenericBuffer
class Timer
#include <Timer.h>

Calculates the ellapsed time on the GPU.

Public Functions

Timer(const Device &device)
void Start()

Start the timer after the current last command buffer.

void Stop()

Stop the timer after the current last command buffer.

uint64_t GetElapsedNs()

Get the elapsed time between the Start and Stop calls. Blocking function which will download the timestamps from the GPU.

Return
timestamp in nanoseconds.

struct Transformable
#include <Transformable.h>

Class to represent the transformation of an object: position, scale, rotation and anchor.

Subclassed by Vortex2D::Fluid::Circle, Vortex2D::Fluid::Polygon, Vortex2D::Fluid::RigidBody, Vortex2D::Renderer::AbstractShape, Vortex2D::Renderer::AbstractSprite, Vortex2D::Renderer::Ellipse

Public Functions

Transformable()
virtual ~Transformable()
const glm::mat4 &GetTransform() const

Returns the transform matrix.

void Update()

Update the transform matrix with the position, scale, rotation and anchor.

Public Members

property<glm::vec2> Position

absolute position

property<glm::vec2> Scale

scale for the x and y components

property<float> Rotation

Rotation in radians.

property<glm::vec2> Anchor

An offset to the position (used for centering a shape)

template <typename T>
class UniformBuffer : public Vortex2D::Renderer::GenericBuffer
#include <Buffer.h>

a uniform buffer type of buffer

Public Functions

UniformBuffer(const Device &device, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)
template <typename T>
class VertexBuffer : public Vortex2D::Renderer::GenericBuffer
#include <Buffer.h>

a vertex buffer type of buffer

Public Functions

VertexBuffer(const Device &device, std::size_t size, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)
class Work
#include <Work.h>

Represents a compute shader. It simplifies the process of binding, setting push constants and recording.

Public Functions

Work(const Device &device, const ComputeSize &computeSize, const SpirvBinary &spirv)

Constructs an object using a SPIRV binary. It is not bound to any buffers or textures.

Parameters
  • device: vulkan device
  • computeSize: the compute size. Can be a default one with size (1,1) or one with an actual size.
  • spirv: binary spirv

Work::Bound Bind(const std::vector<BindingInput> &inputs)

Bind the buffers and/or textures.

Return
a bound object, ready to be recorded in a command buffer.
Parameters
  • inputs: a list of buffers and/or textures

VORTEX2D_API Bound Vortex2D::Renderer::Work::Bind(ComputeSize computeSize, const std::vector< BindingInput > & inputs)

Bind the buffers and/or textures. This overrides the provided compute size in Work.

Return
a bound object, ready to be recorded in a command buffer.
Parameters
  • computeSize: the compute shader compute size.
  • inputs: a list of buffers and/or textures

class Bound
#include <Work.h>

Is a bound version of Work. This means a buffer or texture was bound and this can be recorded in a command buffer.

Public Functions

Bound()
template <typename... Ts>
void PushConstant(vk::CommandBuffer commandBuffer, const Ts&... data)

Adds a constant value, i.e. a push constant.

Parameters
  • commandBuffer: the command buffer where the compute work will also be recorded.
  • data: the data to push. A total of 128 bytes can be used.

void Record(vk::CommandBuffer commandBuffer)

Record the compute work in this command buffer. This will also set two additional push constants: the 2D domain size.

Parameters
  • commandBuffer: the command buffer to record into.

void RecordIndirect(vk::CommandBuffer commandBuffer, IndirectBuffer<DispatchParams> &dispatchParams)

Record the compute work in this command buffer. Use the provided parameters to run the compute shader.

Parameters
  • commandBuffer: the command buffer to record into.
  • dispatchParams: the indirect buffer containing the parameters.

Friends

friend Vortex2D::Renderer::Work

Fluid API reference

API Reference
namespace Fluid

Functions

Renderer::ComputeSize MakeLocalSize(const glm::ivec2 &size)

Variables

VORTEX2D_API vk::PipelineColorBlendAttachmentState Vortex2D::Fluid::IntersectionBlend

= vk::PipelineColorBlendAttachmentState()

.setBlendEnable(true)

.setColorBlendOp(vk::BlendOp::eMax)

.setSrcColorBlendFactor(vk::BlendFactor::eOne)

.setDstColorBlendFactor(vk::BlendFactor::eOne)

.setColorWriteMask(vk::ColorComponentFlagBits::eR)

VORTEX2D_API vk::PipelineColorBlendAttachmentState Vortex2D::Fluid::UnionBlend

= vk::PipelineColorBlendAttachmentState()

.setBlendEnable(true)

.setColorBlendOp(vk::BlendOp::eMin)

.setSrcColorBlendFactor(vk::BlendFactor::eOne)

.setDstColorBlendFactor(vk::BlendFactor::eOne)

.setColorWriteMask(vk::ColorComponentFlagBits::eR)

VORTEX2D_API Renderer::Clear Vortex2D::Fluid::BoundariesClear = Vortex2D::Renderer::Clear({10000.0f, 0.0f, 0.0f, 0.0f})
class Advection
#include <Advection.h>

Advects particles, velocity field or any field using a velocity field.

Public Functions

Advection(const Renderer::Device &device, const glm::ivec2 &size, float dt, Velocity &velocity)

Initialize advection kernels and related object.

Parameters
  • device: vulkan device
  • size: size of velocity field
  • dt: delta time for integration
  • velocity: velocity field

void AdvectVelocity()

Self advect velocity.

void AdvectBind(Density &density)

Binds a density field to be advected.

Parameters
  • density: density field

void Advect()

Performs an advection of the density field. Asynchronous operation.

void AdvectParticleBind(Renderer::GenericBuffer &particles, Renderer::Texture &levelSet, Renderer::IndirectBuffer<Renderer::DispatchParams> &dispatchParams)

Binds praticles to be advected. Also use a level set to project out the particles if they enter it.

Parameters
  • particles: particles to be advected
  • levelSet: level set to project out particles
  • dispatchParams: contains number of particles

void AdvectParticles()

Advect particles. Asynchrounous operation.

class Cfl
#include <Cfl.h>

Calculates the CFL number of the velocity field. It’s an indication on how to choose your time step size. Ideally, the time step should be smaller than the CFL number.

Public Functions

Cfl(const Renderer::Device &device, const glm::ivec2 &size, Velocity &velocity)
void Compute()

Compute the CFL number. Non-blocking.

float Get()

Returns the CFL number. Blocking.

Return
cfl number

class Circle : public Vortex2D::Renderer::Transformable, public Vortex2D::Renderer::Drawable
#include <Boundaries.h>

Signed distance field of circle.

Public Functions

Circle(const Renderer::Device &device, float radius, float extent = 10.0f)

Initialize the circle with radius and extend of signed distance.

Parameters
  • device: vulkan device.
  • radius: radius of circle.
  • extent: extend how far from the circle the signed distance field is calculated.

void Initialize(const Renderer::RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const Renderer::RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

class ConjugateGradient : public Vortex2D::Fluid::LinearSolver
#include <ConjugateGradient.h>

An iterative preconditioned conjugate linear solver. The preconditioner can be specified.

Public Functions

ConjugateGradient(const Renderer::Device &device, const glm::ivec2 &size, Preconditioner &preconditioner)

Initialize the solver with a size and preconditioner.

Parameters
  • device: vulkan device
  • size:
  • preconditioner:

void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the buffers for the linear solver.

Parameters
  • d: the diagonal of the matrxi
  • l: the lower matrix
  • b: the right hand side
  • x: the unknowns

void Solve(Parameters &params)

Solve iteratively solve the linear equations in data.

class Density : public Vortex2D::Renderer::RenderTexture, public Vortex2D::Renderer::Sprite
#include <Density.h>

Density field, used to represent smoke swirling.

Public Functions

Density(const Renderer::Device &device, const glm::ivec2 &size, vk::Format format)

Friends

friend Vortex2D::Fluid::Advection
class Depth
#include <Multigrid.h>

Contains the sizes of the multigrid hierarchy.

Public Functions

Depth(const glm::ivec2 &size)

Initialize with the finest size.

Parameters
  • size: the base size.

int GetMaxDepth() const

The calculated depth of the multigrid.

Return
the depth.

glm::ivec2 GetDepthSize(std::size_t i) const

Gets the depth for a given level.

Return
the size
Parameters
  • i: the level

class Diagonal : public Vortex2D::Fluid::Preconditioner
#include <Diagonal.h>

Diagonal preconditioner. Simplest of preconditioner, useful to verify if the preconditioned conjugate gradient works.

Public Functions

Diagonal(const Renderer::Device &device, const glm::ivec2 &size)
void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

struct Dimensions
#include <Size.h>

Represents the size of the fluid grid. This is useful if we want a different resolution or scale than one grid cell per pixel.

Public Functions

Dimensions(const glm::ivec2 &size, const float scale)

The grid size will be: size/scale.

Parameters
  • size: the size at which we will draw boundaries, forces, etc
  • scale: the scale of the actual grid

Public Members

const float Scale
const glm::ivec2 Size
const glm::mat4 InvScale
class DistanceField : public Vortex2D::Renderer::AbstractSprite
#include <Boundaries.h>

Sprite of a distance field.

Public Functions

DistanceField(const Renderer::Device &device, Renderer::RenderTexture &levelSet, float scale = 1.0f)

Initialize the price with the level set and scale.

Parameters
  • device: vulkan device
  • levelSet: level set to use as sprite
  • scale: scale of the level set

void Draw(vk::CommandBuffer commandBuffer, const Renderer::RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

class Extrapolation
#include <Extrapolation.h>

Class to extrapolate values into the neumann and/or dirichlet boundaries.

Public Functions

Extrapolation(const Renderer::Device &device, const glm::ivec2 &size, Renderer::GenericBuffer &valid, Velocity &velocity, int iterations = 10)
void Extrapolate()

Will extrapolate values from buffer into the dirichlet and neumann boundaries.

void ConstrainBind(Renderer::Texture &solidPhi)

Binds a solid level set to use later and constrain the velocity against.

Parameters
  • solidPhi: solid level set

void ConstrainVelocity()

Constrain the velocity, i.e. ensure that the velocity normal to the solid level set is 0.

class GaussSeidel : public Vortex2D::Fluid::LinearSolver, public Vortex2D::Fluid::Preconditioner
#include <GaussSeidel.h>

An iterative black and red successive over relaxation linear solver.

Public Functions

GaussSeidel(const Renderer::Device &device, const glm::ivec2 &size)
void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the buffers for the linear solver.

Parameters
  • d: the diagonal of the matrxi
  • l: the lower matrix
  • b: the right hand side
  • x: the unknowns

void Solve(Parameters &params)

Iterative solving of the linear equations in data.

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

void Record(vk::CommandBuffer commandBuffer, int iterations)

Record a determined number of iterations.

Parameters
  • commandBuffer:
  • iterations:

void SetW(float w)

Set the w factor of the GS iterations : x_new = w * x_new + (1-w) * x_old.

Parameters
  • w:

void SetPreconditionerIterations(int iterations)

set number of iterations to be used when GS is a preconditioner

Parameters
  • iterations:

class IncompletePoisson : public Vortex2D::Fluid::Preconditioner
#include <IncompletePoisson.h>

Incomplete poisson preconditioner. Slightly better than a simple diagonal preconditioner.

Public Functions

IncompletePoisson(const Renderer::Device &device, const glm::ivec2 &size)
void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

struct J

Public Members

glm::vec2 linear
float angular
class Jacobi : public Vortex2D::Fluid::Preconditioner
#include <Jacobi.h>

An iterative jacobi linear solver.

Public Functions

Jacobi(const Renderer::Device &device, const glm::ivec2 &size)
void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

void Record(vk::CommandBuffer commandBuffer, int iterations)
void SetW(float w)

Set the w factor of the GS iterations : x_new = w * x_new + (1-w) * x_old.

Parameters
  • w:

void SetPreconditionerIterations(int iterations)

set number of iterations to be used when GS is a preconditioner

Parameters
  • iterations:

class LevelSet : public Vortex2D::Renderer::RenderTexture
#include <LevelSet.h>

A signed distance field, which can be re-initialized. In other words, a level set.

Public Functions

LevelSet(const Renderer::Device &device, const glm::ivec2 &size, int reinitializeIterations = 100)
void ExtrapolateBind(Renderer::Texture &solidPhi)

Bind a solid level set, which will be used to extrapolate into this level set.

Parameters
  • solidPhi:

void Reinitialise()

Reinitialise the level set, i.e. ensure it is a correct signed distance field.

void Extrapolate()

Extrapolate this level set into the solid level set it was attached to. This only performs a single cell extrapolation.

void ExtrapolateRecord(vk::CommandBuffer commandBuffer)

Same as Extrapolate but to be recorded as part of other commands.

Parameters
  • commandBuffer:

struct LinearSolver
#include <LinearSolver.h>

An interface to represent a linear solver.

Subclassed by Vortex2D::Fluid::ConjugateGradient, Vortex2D::Fluid::GaussSeidel

Public Functions

virtual ~LinearSolver()
virtual void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x) = 0

Bind the buffers for the linear solver.

Parameters
  • d: the diagonal of the matrxi
  • l: the lower matrix
  • b: the right hand side
  • x: the unknowns

virtual void Solve(Parameters &params) = 0

Solves the linear equations.

struct Data
#include <LinearSolver.h>

The various parts of linear equations.

Public Functions

Data(const Renderer::Device &device, const glm::ivec2 &size, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY)

Public Members

Renderer::Buffer<float> Diagonal
Renderer::Buffer<glm::vec2> Lower
Renderer::Buffer<float> B
Renderer::Buffer<float> X
struct Parameters
#include <LinearSolver.h>

Parameters for an iterative linear solvers.

Public Functions

Parameters(unsigned iterations, float errorTolerance = 0.0f)

Construct parameters with max iterations and max error.

Parameters
  • iterations: max number of iterations to perform
  • errorTolerance: solver stops when the error is smaller than this.

bool IsFinished(float initialError) const

Checks if we’ve reacched the parameters.

Return
if we can stop the linear solver.
Parameters
  • initialError: the initial error

Public Members

unsigned Iterations
float ErrorTolerance
unsigned OutIterations
float OutError
class LocalGaussSeidel : public Vortex2D::Fluid::Preconditioner
#include <GaussSeidel.h>

A version of the gauss seidel that can only be applied on sizes (16,16) or smaller.

Public Functions

LocalGaussSeidel(const Renderer::Device &device, const glm::ivec2 &size)
void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

class Multigrid : public Vortex2D::Fluid::Preconditioner
#include <Multigrid.h>

Multigrid preconditioner. It creates a hierarchy of twice as small set of linear equations. It applies a few iterations of jacobi on each level and transfers the error on the level above. It then copies the error down, adds to the current solution and apply a few more iterations of jacobi.

Public Functions

Multigrid(const Renderer::Device &device, const glm::ivec2 &size, float delta)

Initialize multigrid for given size and delta.

Parameters
  • device: vulkan device
  • size: of the linear equations
  • delta: timestep delta

void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x)

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

void BuildHierarchiesBind(Pressure &pressure, Renderer::Texture &solidPhi, Renderer::Texture &liquidPhi)

Bind the level sets from which the hierarchy is built.

Parameters
  • pressure: The current linear equations
  • solidPhi: the solid level set
  • liquidPhi: the liquid level set

void BuildHierarchies()

Computes the hierarchy to be used by the multigrid. Asynchronous operation.

void Record(vk::CommandBuffer commandBuffer)

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

struct Particle

Public Members

glm::vec2 Position
glm::vec2 Velocity
class ParticleCount : public Vortex2D::Renderer::RenderTexture
#include <Particles.h>

Container for particles used in the advection of the fluid simulation. Also a level set that is built from the particles.

Public Functions

ParticleCount(const Renderer::Device &device, const glm::ivec2 &size, Renderer::GenericBuffer &particles, const Renderer::DispatchParams &params = {0}, float alpha = 1.0f)
void Scan()

Count the number of particles and update the internal data structures.

int GetTotalCount()

Calculate the total number of particles and return it.

Return

Renderer::IndirectBuffer<Renderer::DispatchParams> &GetDispatchParams()

Calculate the dispatch parameters to use on the particle buffer.

Return

void LevelSetBind(LevelSet &levelSet)

Bind a solid level set, which will be used to interpolate the particles out of.

Parameters
  • levelSet:

void Phi()

Calculate the level set from the particles.

void VelocitiesBind(Velocity &velocity, Renderer::GenericBuffer &valid)

Bind the velocities, used for advection of the particles.

Parameters
  • velocity:
  • valid:

void TransferToGrid()

Interpolate the velocities of the particles to the velocities field.

void TransferFromGrid()

Interpolate the velocities field in to the particles’ velocity.

class Polygon : public Vortex2D::Renderer::Transformable, public Vortex2D::Renderer::Drawable
#include <Boundaries.h>

Signed distance field of a poylgon.

Subclassed by Vortex2D::Fluid::Rectangle

Public Functions

Polygon(const Renderer::Device &device, std::vector<glm::vec2> points, bool inverse = false, float extent = 10.0f)

Initialize polygon with set of points and extent of signed distance.

Parameters
  • device: vulkan device
  • points: clockwise oriented set of points (mininum 3).
  • inverse: flag if the distance field should be inversed.
  • extent: extend how far from the poylon the signed distance field is calculated.

void Initialize(const Renderer::RenderState &renderState)

Initialize the drawable for a particular state. This might include creating the correct pipeline. If it was already initialized, it will do nothing.

Parameters
  • renderState: the state to initialize with.

void Update(const glm::mat4 &projection, const glm::mat4 &view)

Update the MVP matrix of the drawable.

Parameters
  • projection: the projection matrix
  • view: the view matrix

void Draw(vk::CommandBuffer commandBuffer, const Renderer::RenderState &renderState)

Draw for the given render state. This has to be initialized before.

Parameters
  • commandBuffer: the command buffer to record into.
  • renderState: the render state to use.

struct Preconditioner
#include <Preconditioner.h>

An interface to represent a linear solver preconditioner.

Subclassed by Vortex2D::Fluid::Diagonal, Vortex2D::Fluid::GaussSeidel, Vortex2D::Fluid::IncompletePoisson, Vortex2D::Fluid::Jacobi, Vortex2D::Fluid::LocalGaussSeidel, Vortex2D::Fluid::Multigrid

Public Functions

virtual ~Preconditioner()
virtual void Bind(Renderer::GenericBuffer &d, Renderer::GenericBuffer &l, Renderer::GenericBuffer &b, Renderer::GenericBuffer &x) = 0

Bind the linear equation buffers.

Parameters
  • d: the diagonal of the matrix
  • l: the lower matrix
  • b: the right hand side
  • x: the unknown buffer

virtual void Record(vk::CommandBuffer commandBuffer) = 0

Record the preconditioner.

Parameters
  • commandBuffer: the command buffer to record into.

class PrefixScan
#include <PrefixScan.h>

The prefix sum operator.

void PrefixSym(int input[], int n, int output[])
{
    output[0] = input[0];

    for (int i = 1; i < n; i++)
        output[i] = output[i-1] + input[i];
}

Public Functions

PrefixScan(const Renderer::Device &device, const glm::ivec2 &size)
PrefixScan::Bound Bind(Renderer::GenericBuffer &input, Renderer::GenericBuffer &output, Renderer::GenericBuffer &dispatchParams)
class Bound
#include <PrefixScan.h>

A prefix scan object bound with input/output buffers, ready to be dispatched.

Public Functions

void Record(vk::CommandBuffer commandBuffer)

Friends

friend Vortex2D::Fluid::PrefixScan
class Pressure
#include <Pressure.h>

build the linear equation and compute the divergence from the resulting solution.

Public Functions

Pressure(const Renderer::Device &device, float dt, const glm::ivec2 &size, LinearSolver::Data &data, Velocity &velocity, Renderer::Texture &solidPhi, Renderer::Texture &liquidPhi, Renderer::GenericBuffer &valid)
Renderer::Work::Bound BindMatrixBuild(const glm::ivec2 &size, Renderer::GenericBuffer &diagonal, Renderer::GenericBuffer &lower, Renderer::Texture &liquidPhi, Renderer::Texture &solidPhi)

Bind the various buffes for the linear system Ax = b.

Return
Parameters
  • size: size of the linear system
  • diagonal: diagonal of A
  • lower: lower matrix of A
  • liquidPhi: liquid level set
  • solidPhi: solid level set

void BuildLinearEquation()

Build the matrix A and right hand side b.

void ApplyPressure()

Apply the solution of the equation Ax = b, i.e. the pressure to the velocity to make it non-divergent.

class Rectangle : public Vortex2D::Fluid::Polygon
#include <Boundaries.h>

Signed distance field of a rectangle.

Public Functions

Rectangle(const Renderer::Device &device, const glm::vec2 &size, bool inverse = false, float extent = 10.0f)

Initialize rectangle with size and extend of signed distance.

Parameters
  • device: vulkan device.
  • size: rectangle size
  • inverse: flag if the distance field should be inverted.
  • extent: extent how far from the rectangle the signed distance field is calculated.

class Reduce
#include <Reduce.h>

Parallel reduction of a buffer into one value. The operator and type of data is specified by inheriting the class.

Subclassed by Vortex2D::Fluid::ReduceJ, Vortex2D::Fluid::ReduceMax, Vortex2D::Fluid::ReduceSum

Public Functions

virtual ~Reduce()
Reduce::Bound Bind(Renderer::GenericBuffer &input, Renderer::GenericBuffer &output)

Bind the reduce operation.

Return
a bound object that can be recorded in a command buffer.
Parameters
  • input: input buffer
  • output: output buffer

class Bound
#include <Reduce.h>

Bound input and output buffer for a reduce operation.

Public Functions

Bound()
void Record(vk::CommandBuffer commandBuffer)

Record the reduce operation.

Parameters
  • commandBuffer: the command buffer to record into.

Friends

friend Vortex2D::Fluid::Reduce
class ReduceJ : public Vortex2D::Fluid::Reduce
#include <Reduce.h>

Reduce operation on a struct with a 2d vector and 1 float (i.e. 3 floats) with addition.

Public Functions

ReduceJ(const Renderer::Device &device, const glm::ivec2 &size)

Initialize reduce with device and 2d size.

Parameters
  • device:
  • size:

class ReduceMax : public Vortex2D::Fluid::Reduce
#include <Reduce.h>

Reduce operation on float with max of absolute.

Public Functions

ReduceMax(const Renderer::Device &device, const glm::ivec2 &size)

Initialize reduce with device and 2d size.

Parameters
  • device:
  • size:

class ReduceSum : public Vortex2D::Fluid::Reduce
#include <Reduce.h>

Reduce operation on float with addition.

Public Functions

ReduceSum(const Renderer::Device &device, const glm::ivec2 &size)

Initialize reduce with device and 2d size.

Parameters
  • device:
  • size:

class RigidBody : public Vortex2D::Renderer::Transformable
#include <Rigidbody.h>

Rigidbody that can interact with the fluid: either be push by it, or influence it, or both.

Public Types

enum Type

Values:

eStatic = 0x01
eWeak = 0x02
eStrong = 0x3

Public Functions

RigidBody(const Renderer::Device &device, const Dimensions &dimensions, Renderer::Drawable &drawable, const glm::vec2 &centre, Renderer::RenderTexture &phi, vk::Flags<Type> type)
void SetVelocities(const glm::vec2 &velocity, float angularVelocity)

sets the velocities and angular velocities of the body

Parameters
  • velocity:
  • angularVelocity:

void UpdatePosition()

Upload the transform matrix to the GPU.

void RenderPhi()

Render the current object orientation in an internal texture and the external one.

void BindDiv(Renderer::GenericBuffer &div, Renderer::GenericBuffer &diagonal, Renderer::Texture &fluidLevelSet)

Bind a the right hand side and diagonal of the linear system Ax = b. This is to apply the rigid body influence to the system.

Parameters
  • div: right hand side of the linear system Ax=b
  • diagonal: diagonal of matrix A
  • fluidLevelSet: fluid level set

void BindVelocityConstrain(Fluid::Velocity &velocity)

Bind velocities to constrain based on the body’s velocity.

Parameters
  • velocity:

void BindPressure(Renderer::Texture &fluidLevelSet, Renderer::GenericBuffer &pressure, Renderer::GenericBuffer &force)

Bind pressure, to have the pressure update the body’s forces.

Parameters
  • fluidLevelSet: fluid level set, to know if the pressure is applicable
  • pressure: solved pressure buffer
  • force: a scratch buffer where the forces will be set

void Div()

Apply the body’s velocities to the linear equations matrix A and right hand side b.

void Pressure()

Apply the pressure to body, updating its forces.

void VelocityConstrain()

Constrain the velocities field based on the body’s velocity.

RigidBody::Velocity GetForces()

Download the forces from the GPU and return them.

Return

vk::Flags<RigidBody::Type> GetType()

Type of this body.

Return

Renderer::RenderTexture &Phi()

the local level set of the body

Return

struct Velocity

Public Members

glm::vec2 velocity
float angular_velocity
class SmokeWorld : public Vortex2D::Fluid::World
#include <World.h>

A concrete implementation of World to simulate ‘smoke’, or more accurately dye in a liquid. The liquid cannot change location or size.

Public Functions

SmokeWorld(const Renderer::Device &device, Dimensions dimensions, float dt)
void Solve()

Perform one step of the simulation.

void FieldBind(Density &density)

Bind a density field to be moved around with the fluid.

Parameters
  • density: the density field

class Transfer
#include <Transfer.h>

Prolongates or restrict a level set on a finer or coarser level set.

Public Functions

Transfer(const Renderer::Device &device)

Initialize prolongate and restrict compute pipelines.

Parameters
  • device:

void ProlongateBind(std::size_t level, const glm::ivec2 &fineSize, Renderer::GenericBuffer &fine, Renderer::GenericBuffer &fineDiagonal, Renderer::GenericBuffer &coarse, Renderer::GenericBuffer &coarseDiagonal)

Prolongate a level set on a finer level set. Setting the 4 cells to the value of the coarser grid. Multiple level sets can be bound and indexed.

Parameters
  • level: the index of the bound level set to prolongate
  • fineSize: size of the finer level set
  • fine: the finer level set
  • fineDiagonal: the diagonal of the linear equation matrix at size fineSize
  • coarse: the coarse level set
  • coarseDiagonal: the diagonal of the linear equation matrix at size half of fineSize

void RestrictBind(std::size_t level, const glm::ivec2 &fineSize, Renderer::GenericBuffer &fine, Renderer::GenericBuffer &fineDiagonal, Renderer::GenericBuffer &coarse, Renderer::GenericBuffer &coarseDiagonal)

Restricing the level set on a coarser level set. Averages 4 cells into one. Multiple level sets can be bound and indexed.

Parameters
  • level: the index of the bound level set to prolongate
  • fineSize: size of the finer level set
  • fine: the finer level set
  • fineDiagonal: the diagonal of the linear equation matrix at size fineSize
  • coarse: the coarse level set
  • coarseDiagonal: the diagonal of the linear equation matrix at size half of fineSize

void Prolongate(vk::CommandBuffer commandBuffer, std::size_t level)

Prolongate the level set, using the bound level sets at the specified index.

Parameters
  • commandBuffer: command buffer to record into.
  • level: index of bound level sets.

void Restrict(vk::CommandBuffer commandBuffer, std::size_t level)

Restrict the level set, using the bound level sets at the specified index.

Parameters
  • commandBuffer: command buffer to record into.
  • level: index of bound level sets.

class Velocity : public Vortex2D::Renderer::RenderTexture
#include <Velocity.h>

The Velocity field. Can be used to calculate a difference between different states. Contains three fields: intput and output, used for ping-pong algorithms, and d, the difference between two velocity fields.

Public Functions

Velocity(const Renderer::Device &device, const glm::ivec2 &size)
Renderer::Texture &Output()

An output texture used for algorithms that used the velocity as input and need to create a new velocity field.

Return

Renderer::Texture &D()

A difference velocity field, calculated with the difference between this velocity field, and the output velocity field.

Return

void CopyBack(vk::CommandBuffer commandBuffer)

Copy the output field to the main field.

Parameters
  • commandBuffer:

void Clear(vk::CommandBuffer commandBuffer)

Clear the velocity field.

Parameters
  • commandBuffer:

void SaveCopy()

Copy to the difference field.

void VelocityDiff()

Calculate the difference between the difference field and this velocity field, store it in the diference field.

class WaterWorld : public Vortex2D::Fluid::World
#include <World.h>

A concrete implementation of World to simulate water.

Public Functions

WaterWorld(const Renderer::Device &device, Dimensions dimensions, float dt)
void Solve()

Perform one step of the simulation.

Renderer::RenderCommand RecordParticleCount(Renderer::RenderTarget::DrawableList drawables)

The water simulation uses particles to define the water area. In fact, the level set is built from the particles. This means to be able to set an area, we can’t use RecordLiquidPhi. To define the particle area, simply draw a regular shape. The colour r is used to determine if we add or remove particles, use r = 4 to add and r = -4 to remove.

Return
render command
Parameters
  • drawables: list of drawables object with colour 4 or -4

class World
#include <World.h>

The main class of the framework. Each instance manages a grid and this class is used to set forces, define boundaries, solve the incompressbility equations and do the advection.

Subclassed by Vortex2D::Fluid::SmokeWorld, Vortex2D::Fluid::WaterWorld

Public Functions

World(const Renderer::Device &device, Dimensions dimensions, float dt)

Construct an Engine with a size and time step.

Parameters
  • device: vulkan device
  • dimensions: dimensions of the simulation
  • dt: timestamp of the simulation, e.g. 0.016 for 60FPS simulations.

virtual ~World()
virtual void Solve() = 0

Perform one step of the simulation.

Renderer::RenderCommand RecordVelocity(Renderer::RenderTarget::DrawableList drawables)

Record drawables to the velocity field. The colour (r,g) will be used as the velocity (x, y)

Return
render command
Parameters
  • drawables: a list of drawable field

void SubmitVelocity(Renderer::RenderCommand &renderCommand)

submit the render command created with RecordVelocity

Parameters
  • renderCommand: the render command

Renderer::RenderCommand RecordLiquidPhi(Renderer::RenderTarget::DrawableList drawables)

Record drawables to the liquid level set, i.e. to define the fluid area. The drawables need to make a signed distance field, if not the result is undefined.

Return
render command
Parameters
  • drawables: a list of signed distance field drawables

Renderer::RenderCommand RecordStaticSolidPhi(Renderer::RenderTarget::DrawableList drawables)

Record drawables to the solid level set, i.e. to define the boundary area. The drawables need to make a signed distance field, if not the result is undefined.

Return
render command
Parameters
  • drawables: a list of signed distance field drawables

DistanceField LiquidDistanceField()

Create sprite that can be rendered to visualize the liquid level set.

Return
a sprite

DistanceField SolidDistanceField()

Create sprite that can be rendered to visualize the solid level set.

Return
a sprite

RigidBody *CreateRigidbody(vk::Flags<RigidBody::Type> type, Renderer::Drawable &drawable, const glm::vec2 &centre)

Create a rigid body and add it to the World. The lifetime of the rigidbody is tied to the lifetime of the World.

Return
a pointer to the rigid body.
Parameters
  • type: type of the rigidbody: static, weak or strong
  • drawable: a drawable that renders a signed distance field. This can be the sprite of a level set.
  • centre: the centre of the drawable. Use for rotations.

float GetCFL()

Calculate the CFL number, i.e. the width divided by the max velocity.

Return
CFL number