Skip to content

Instantly share code, notes, and snippets.

@Themaister
Created October 4, 2025 11:01
Show Gist options
  • Save Themaister/a60044a1fd292621bb08cd200e871016 to your computer and use it in GitHub Desktop.
Save Themaister/a60044a1fd292621bb08cd200e871016 to your computer and use it in GitHub Desktop.
Bringup sample #2 - Most basic compute
#version 460
// The version doesn't matter as much in Vulkan GLSL. This is the last version.
// In GLSL we have to enable special features as we use them.
#extension GL_EXT_buffer_reference : require
// For every workgroup, there is 16 threads.
layout(local_size_x = 16) in;
// Declares a pointer to data.
layout(buffer_reference, buffer_reference_align = 16) writeonly buffer Output
{
uint data[];
};
layout(buffer_reference, buffer_reference_align = 16) readonly buffer Input
{
uint data[];
};
// Push constants is the most straight forward way to send data to a dispatch.
layout(push_constant) uniform Registers
{
Output o; // 64-bit pointers.
Input i;
};
void main()
{
o.data[gl_GlobalInvocationID.x] = i.data[gl_GlobalInvocationID.x];
}
#include "device.hpp"
#include "context.hpp"
#include "logging.hpp"
#include <stdlib.h>
static const uint32_t copy_shader[] =
#include "copy.h"
;
static void run_some_code(Vulkan::Device &dev)
{
if (!dev.get_device_features().vk12_features.bufferDeviceAddress)
{
LOGE("This device does not support BDA for some reason ...\n");
return;
}
Vulkan::BufferCreateInfo info = {};
info.size = 64;
// Memory should live in VRAM.
info.domain = Vulkan::BufferDomain::Device;
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
info.misc = Vulkan::BUFFER_MISC_ZERO_INITIALIZE_BIT;
auto gpu_buffer = dev.create_buffer(info);
dev.set_name(*gpu_buffer, "gpu-buffer");
// Memory should live in system memory. For "write-only" memory like this,
// write-combined memory is the way to go. CachedHost is important for reading.
info.domain = Vulkan::BufferDomain::Host;
info.misc = 0;
uint32_t initial_data[16];
for (uint32_t i = 0; i < 16; i++)
initial_data[i] = i;
auto cpu_buffer = dev.create_buffer(info, initial_data);
dev.set_name(*cpu_buffer, "cpu-buffer");
Vulkan::Program *prog = dev.request_program(copy_shader, sizeof(copy_shader));
struct
{
VkDeviceAddress dst_addr; // GPU pointers are always 64-bit.
VkDeviceAddress src_addr;
} push = {};
// Copy CPU memory into VRAM, but using shaders.
Vulkan::CommandBufferHandle cmd = dev.request_command_buffer(Vulkan::CommandBuffer::Type::Generic);
cmd->set_program(prog);
// Send GPU pointers as push parameters.
// We can send at least 128 bytes this way.
// We're juggling with scissors now. If this goes wrong, we can crash the GPU.
push.dst_addr = gpu_buffer->get_device_address();
push.src_addr = cpu_buffer->get_device_address();
cmd->push_constants(&push, 0, sizeof(push));
cmd->dispatch(1, 1, 1);
dev.submit(cmd);
// This functions as garbage collection. Memory is not reclaimed unless this is called regularly.
dev.next_frame_context();
}
int main()
{
// Loads the Vulkan loader dynamically.
// The loader is the entry point to figure out
// which drivers are available on the system.
// The default is:
// - libvulkan.so.1 on Linux (installed with vulkan-icd-loader)
// - vulkan-1.dll (Installed on Windows by your driver or standalone with Vulkan SDK)
// - libvulkan.so on Android (included on OS if it's not comically old)
if (!Vulkan::Context::init_loader(nullptr))
return EXIT_FAILURE;
// The context owns a VkInstance, VkPhysicalDevice and VkDevice.
// Don't enable any optional extensions and fancy features for now.
Vulkan::Context ctx;
if (!ctx.init_instance_and_device(nullptr, 0, nullptr, 0, 0))
return EXIT_FAILURE;
// Doesn't do much real work, but instantiates a logical device that borrows the handles from Context.
Vulkan::Device dev;
dev.set_context(ctx);
// Capturing Vulkan in debuggers is typically done in two ways:
// - Frame captures. Requires the debugger to understand what a "frame" is, typically requires presenting on screen,
// which we're not going to do.
// - Using the RenderDoc API to trigger captures programmatically.
// Needs RenderDoc to be loaded into the process already.
bool has_rdoc = Vulkan::Device::init_renderdoc_capture();
if (has_rdoc)
dev.begin_renderdoc_capture();
run_some_code(dev);
if (has_rdoc)
dev.end_renderdoc_capture();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment