Integration Guide
Learn how to integrate GPULang into your game engine
Overview
GPULang is designed from the ground up to make shader development more efficient and maintainable. This guide covers integration patterns, performance optimization, and best practices for using GPULang in real-time rendering applications.
Loading a GPULang output file and extracting a single program
struct Program
{
char* memory;
GPULang::Loader loader;
GPULang::VulkanPipelineInfo pipelineInfo;
};
//------------------------------------------------------------------------------
/**
*/
Program
LoadShader(char* fileBuffer, size_t fileBufferSize, VkDevice device, std::string shader)
{
GPULang::Loader loader;
loader.Load(fileBuffer, fileBufferSize);
auto prog = loader.Get("TestProgram");
GPULang::VulkanPipelineInfo vulkanPipeline = GPULang::SetupVulkan(
device
, prog
, loader.variables.data()
, loader.variables.size()
, GPULang::VulkanFunctions{ .vkCreateShaderModule = vkCreateShaderModule, .vkCreatePipelineLayout = vkCreatePipelineLayout, .vkCreateDescriptorSetLayout = vkCreateDescriptorSetLayout }
);
Program ret;
ret.pipelineInfo = vulkanPipeline;
ret.memory = buf;
ret.loader = loader;
return ret;
}
//------------------------------------------------------------------------------
/**
*/
void main()
{
... // Setup vulkan
Program computeProgram = LoadShader(device, "computewithstore.gplb");
VkComputePipelineCreateInfo cmpPsoInfo = GPULang::GetComputePipeline(computeProgram.pipelineInfo);
}
Iterate and and setup dynamically
//------------------------------------------------------------------------------
/**
*/
Program
LoadShader(char* fileBuffer, size_t fileBufferSize, VkDevice device, std::string shader)
{
GPULang::Loader loader;
loader.Load(fileBuffer, fileBufferSize);
// Map for looking up symbols by name
for (auto& [name, object] : info.loader->nameToObject)
{
if (object->type == GPULang::Serialize::Type::VariableType)
{
auto variable = (GPULang::Deserialize::Variable*)object;
// If variable is a uniform buffer, store it in the reflection data
if (variable->bindingType == GPULang::BindingType::Buffer)
{
// Setup read-only buffer (uniform/constant)
}
else if (variable->bindingType == GPULang::BindingType::MutableBuffer)
{
// Setup writeable buffer (storage/rw)
}
}
if (object->type == GPULang::Serialize::Type::ProgramType)
{
auto program = (GPULang::Deserialize::Program*)object;
VkShaderModule vs,hs,ds,gs,ps,cs;
VulkanCreateShader(device, &vs, program->shaders[GPULang::Deserialize::Program::ShaderStages::VertexShader]);
VulkanCreateShader(device, &hs, program->shaders[GPULang::Deserialize::Program::ShaderStages::HullShader]);
VulkanCreateShader(device, &ds, program->shaders[GPULang::Deserialize::Program::ShaderStages::DomainShader]);
VulkanCreateShader(device, &gs, program->shaders[GPULang::Deserialize::Program::ShaderStages::GeometryShader]);
VulkanCreateShader(device, &ps, program->shaders[GPULang::Deserialize::Program::ShaderStages::PixelShader]);
VulkanCreateShader(device, &cs, program->shaders[GPULang::Deserialize::Program::ShaderStages::ComputeShader]);
}
}
}
Using static reflection
#include "histogram.h"
//------------------------------------------------------------------------------
/**
*/
void
BindHistogram(const std::vector<VkDescriptorSet>& sets, VkBuffer buf)
{
WriteDescriptor(sets[Histogram::HistogramBuffer::GROUP], Histogram::HistogramBuffer::BINDING, buf);
}
//------------------------------------------------------------------------------
/**
*/
void
RunHistogram(VkCmdBuffer& cmd, int ScreenSize[2])
{
vkCmdDispatch(cmd
, HistogramProgram::csMain::WORKGROUP_SIZE[0] / ScreenSize[0]
, HistogramProgram::csMain::WORKGROUP_SIZE[1] / ScreenSize[1]
, HistogramProgram::csMain::WORKGROUP_SIZE[2]
)
}