Using TZGLP's SSBO Module - harrand/Topaz-2 GitHub Wiki

Shader Storage Buffers

Consider the following vertex-shader for a total of three vertices (to render a simple triangle):

#version 430

layout(std430, binding = 0) buffer position_data
{
    float positions[9];
};

void main()
{
    gl_Position = vec4(positions[gl_VertexID*3], positions[(gl_VertexID*3) + 1], positions[(gl_VertexID*3) + 2], 1.0);
}

Naïve Approach

On the C++ side, you must do the following:

  • Add an SSBO to an existing tz::gl::Object and provide a binding ID to the SSBO. This id must be the same as the binding in the shader.
  • Doing this naively means that this is likely to be hard-coded.
  • Hard-coding IDs like this is a nightmare for maintenance.

Luckily, TZGLP (tz::gl::ShaderPreprocessor) has a bespoke module specifically to ease this annoyance. Using this module provides access to a new preprocessor-directive in shader component source-code: #ssbo.

TZGLP Approach

The vertex-shader is now:

#version 430

#ssbo position_data
{
    float positions[9];
};

void main()
{
    gl_Position = vec4(positions[gl_VertexID*3], positions[(gl_VertexID*3) + 1], positions[(gl_VertexID*3) + 2], 1.0);
}

The binding ID will be automatically generated by the TZGLP module. You can access it by obtaining a pointer to the module via the ShaderPreprocessor.

Minimal Verifiable Example

Note: This demo already exists in the repository and you can easily run this by cloning Topaz-2 and building the target called 'topaz_ssbo_tzglp_demo'

#include "core/core.hpp"
#include "core/debug/print.hpp"
#include "gl/shader.hpp"
#include "gl/shader_preprocessor.hpp"
#include "gl/shader_compiler.hpp"
#include "gl/object.hpp"
#include "gl/buffer.hpp"
#include "gl/frame.hpp"
#include "gl/modules/ssbo.hpp"
#include "GLFW/glfw3.h"

const char *vertexShaderSource = "#version 430\n"
    "layout (location = 0) in uint pos_index;\n"
    "#ssbo position_buffer\n"
    "{\n"
    "   float positions[9];\n"
    "};\n"
    "\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(positions[gl_VertexID*3], positions[(gl_VertexID*3) + 1], positions[(gl_VertexID*3) + 2], 1.0);\n"
    "}\0";
const char *fragmentShaderSource = "#version 430\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "   FragColor = vec4(0.8f, 0.15f, 0.0f, 1.0f);\n"
    "}\n\0";

int main()
{
	// Minimalist Graphics Demo.
	tz::core::initialise("Topaz TZGLP SSBO Demo");
	{
        tz::gl::Object o;
        tz::gl::p::SSBOModule* ssbo_module = nullptr;

        tz::gl::ShaderPreprocessor pre{vertexShaderSource};
        {
            std::size_t ssbo_module_id = pre.emplace_module<tz::gl::p::SSBOModule>(&o);
            pre.preprocess();
            // Get the module after use.
            ssbo_module = static_cast<tz::gl::p::SSBOModule*>(pre[ssbo_module_id]);
        }
        std::size_t ssbo_id = ssbo_module->get_buffer_id(ssbo_module->size() - 1);
        tz::gl::SSBO* ssbo = o.get<tz::gl::BufferType::ShaderStorage>(ssbo_id);

	tz::gl::ShaderCompiler cpl;
	tz::gl::ShaderProgram prg;
	tz::gl::Shader* vs = prg.emplace(tz::gl::ShaderType::Vertex);
        // We want to upload the preprocessed source.
	vs->upload_source(pre.result());
	tz::gl::Shader* fs = prg.emplace(tz::gl::ShaderType::Fragment);
	fs->upload_source(fragmentShaderSource);

	cpl.compile(*vs);
	cpl.compile(*fs);
	cpl.link(prg);

		const float vertices[] = {
        -0.5f, -0.5f, 0.0f, // left  
         0.5f, -0.5f, 0.0f, // right 
         0.0f,  0.5f, 0.0f  // top   
    	};

	ssbo->bind();
	ssbo->terminal_resize(sizeof(vertices));
	tz::mem::UniformPool<float> vertex_pool = ssbo->map_pool<float>();
	for(std::size_t i = 0; i < vertex_pool.capacity(); i++)
		vertex_pool.set(i, vertices[i]);
	auto add_pos = [&vertex_pool](float x, float y, float z)
	{
		vertex_pool[0] += x;
		vertex_pool[3] += x;
		vertex_pool[6] += x;

		vertex_pool[1] += y;
		vertex_pool[4] += y;
		vertex_pool[7] += y;

		vertex_pool[2] += z;
		vertex_pool[5] += z;
		vertex_pool[8] += z;
	};

	ssbo->unbind();
	o.unbind();

	tz::core::IWindow& wnd = tz::core::get().window();
	wnd.register_this();
	wnd.emplace_custom_key_listener([&add_pos](tz::input::KeyPressEvent e)
	{
		switch(e.key)
		{
		case GLFW_KEY_W:
			add_pos(0.0f, 0.05f, 0.0f);
			tz::debug_printf("moving forward.\n");
		break;
		case GLFW_KEY_S:
			add_pos(0.0f, -0.05f, 0.0f);
			tz::debug_printf("moving backward.\n");
		break;
		case GLFW_KEY_A:
			add_pos(-0.05f, 0.0f, 0.0f);
			tz::debug_printf("moving left\n");
		break;
		case GLFW_KEY_D:
			add_pos(0.05f, 0.0f, 0.0f);
			tz::debug_printf("moving right\n");
		break;
		}
	});

	glClearColor(0.0f, 0.3f, 0.15f, 1.0f);
	while(!wnd.is_close_requested())
	{
        wnd.get_frame()->clear();
		prg.bind();
		o.bind();
		ssbo->bind();
		o.render(1);

		wnd.update();
		tz::core::update();
	}
	tz::core::terminate();
}
⚠️ **GitHub.com Fallback** ⚠️