Buffer creation

Structure

First of all to simulate a lot of particles, you need data for a lot of particles. In this example here, it means you need positions, velocities, masses and lifetime. So you can start by defining a structure in C++ for a particle like this:

#include<glm/glm.hpp>
struct Particle {
  glm::vec3 position;
  float mass;
  glm::vec3 velocity;
  float lifetime;
};

Notice that vec3 attributes and float attributes are used alternately. This is actually intentional! Different types of your attributes in structures will be aligned differently on GPU and CPU. So depending on the order of your attributes a structure may cost more memory in a buffer for each entry which results in wasting bandwidth and even worse: When you don't make sure alignment on CPU and GPU match for each attribute, your application might not work properly at all.

In the code example above you get the same alignment on CPU and GPU because the mass and lifetime fill the gap between two vec3 on a GPU perfectly. Those 3-dimensional vectors usually get alignment in memory as vec4 (most likely for bit-reduction of their addresses in instructions on the GPU) but on the CPU that's not the case as default (alignment on the CPU can be adjusted though to be less packed - see alignas).

Since those issues with alignment are always very specific to your own usecase, here won't be a detailed explaination how to "just solve it". But you can find guides online providing help for UBOs (uniform buffers) and SSBOs (shared storage buffers). It also depends on the memory layout used in your shaders for those buffers (more about that in a later part about the compute pipeline of this guide). Just notice that it's important even if it doesn't look like it in your code. Unfortunately there is no automatic solution for this problem.


Storage buffer

With the structure defined you can create a shared storage buffer.

const size_t particleCount = 100000;

vkcv::Buffer<Particle> particleBuffer = vkcv::buffer<Particle>(
  core,                      // core instance
  vkcv::BufferType::STORAGE, // type of buffer
  particleCount              // count of elements
);

So this is all you need to create a shared storage buffer with the VkCV framework. Pratically it is not much different to create uniform buffers or even vertex buffers. You just need to specify the type of buffer, their element type as template parameter and its count of elements. Technically it is also possible to specify the memory type of your buffer which allows to use host-visible memory that can directly be mapped inside your CPU-side code to memory during runtime. But that should not be important for most usecases.

Instead of mapping your buffer to memory you can just use the provided methods from the framework to fill your buffer with data:

// create list of particles
std::vector<Particle> particles;
particles.resize(particleCount);

// set initial values for each particle
for (auto& particle : particles) {
  particle.position = glm::vec3(0.0f);
  particle.mass = 1.0f;
  particle.velocity = glm::vec3(0.0f);
  particle.lifetime = 0.0f;
}

/* Giving each particle different (potentially random) values
 * might make the simulation more interesting.
 */

// fill the buffer with initial values
particleBuffer.fill(particles);

Previous

Next

Popular posts from this blog

Introduction

Application development

First setup