| name | hlsl |
| description | HLSL code generation, StringBuilder patterns, builtin headers, and DXIL embedding. |
HLSL Code Generation
Codegen utilities using vstd::StringBuilder and formatting helpers.
StringBuilder
#include <luisa/vstl/string_builder.h>
vstd::StringBuilder builder;
builder.clear();
size_t sz = builder.size();
vstd::string_view view = builder.view();
char* data = builder.data();
Appending
str.append("Hello");
str += "World"sv;
str.append(' ');
str << '\n';
str.append(other_builder);
str << "func" << '(' << ")";
Numbers
vstd::to_string(42, str);
vstd::to_string(3.14f, str);
str << luisa::format("{}", 42);
str << luisa::format("{}, {}!", "Hello", "World");
str << luisa::format("{:016X}", hash);
String View Literals
using namespace std::string_view_literals;
str += "void"sv;
Code Generation Patterns
Function Declaration
void GetFunctionDecl(Function func, vstd::StringBuilder &str) {
vstd::StringBuilder data;
if (func.return_type()) CodegenUtility::GetTypeName(*func.return_type(), data, Usage::READ);
else data += "void"sv;
data += " "sv;
CodegenUtility::GetFunctionName(func, data);
data += '(';
for (auto &&arg : func.arguments()) {
Usage usage = func.variable_usage(arg.uid());
CodegenUtility::GetTypeName(*arg.type(), data, usage);
data << ' ';
vstd::StringBuilder varName;
CodegenUtility::GetVariableName(func, arg, varName);
data << varName << ',';
}
if (!func.arguments().empty()) data[data.size() - 1] = ')';
else data += ')';
str << '\n' << data;
}
Template Parameters
str << "template<"sv;
for (uint64 i = 0; i < tempIdx; ++i) {
str << "typename T"sv; vstd::to_string(static_cast<int64_t>(i), str); str << ',';
}
if (tempIdx > 0) *(str.end() - 1) = '>';
Call Expression
str << "func_name"sv << '(';
if (!args.empty()) {
for (size_t i = 0; i < args.size() - 1; ++i) { args[i]->accept(vis); str << ','; }
args.back()->accept(vis);
}
str << ')';
Type-Aware Generation
if (t->is_texture() || t->is_buffer()) { str << 'T'; vstd::to_string(tempIdx++, str); }
else if (t->is_vector()) { str << "float"sv; vstd::to_string(t->dimension(), str); }
else if (t->is_matrix()) { auto n = vstd::to_string(t->dimension()); str << "_float"sv << n << 'x' << n; }
RAII Wrapping
struct CodeWrapper {
vstd::StringBuilder *_result;
CodeWrapper(vstd::StringBuilder *r, string_view open) : _result(r) { if (_result) *_result << open; }
~CodeWrapper() { if (_result) *_result << ')'; }
};
CodeWrapper wrapper{&str, "to_float4x4("};
Trailing Comma Fix
str << '(';
for (auto &&item : items) str << item << ',';
if (!items.empty()) str[str.size() - 1] = ')';
else str << ')';
Iteration & Hash
for (char c : str) {}
str.erase(str.begin() + 5);
vstd::hash<vstd::StringBuilder> hasher; size_t h = hasher(builder);
if (builder1 == builder2) {}
if (builder1 == "literal"sv) {}
Best Practices
- Use
"text"sv literals to avoid allocations
vstd::to_string() for integers (faster than luisa::format())
builder.reserve(1024) when size known
operator<< for chaining, operator+= for single items
- Check
size() before modifying last character
HLSL Builtins
Located in src/backends/common/hlsl/builtin/. Access via:
#include <backends/common/hlsl/builtin/hlsl_builtin.hpp>
auto header = lc_hlsl::get_hlsl_builtin("hlsl_header");
std::string_view code(static_cast<const char*>(header.ptr), header.size);
Header Files (.bytes)
| Key | Description |
|---|
hlsl_header | Main HLSL header |
hlsl_header_fallback | Fallback header |
spv_alias | SPIR-V aliases |
bindless_upload.bytes / bindless_upload_vk.bytes | Bindless upload helpers |
accel_process.bytes / accel_process_vk.bytes | Acceleration-structure processing |
accel_process_vk_motion.bytes | Acceleration-structure processing with motion blur |
raytracing_motion_header | Ray tracing motion blur |
dx_linalg | Linear algebra utils (DXIL) |
vk_linalg | Linear algebra utils (SPIR-V) |
raytracing_header | Ray tracing |
tex2d_bindless / tex3d_bindless | Bindless textures |
compute_quad | Compute quad ops |
determinant / inverse | Matrix ops |
indirect | Indirect dispatch/draw |
resource_size | Resource queries |
accel_header | Acceleration structures |
copy_sign | Sign ops |
bindless_common | Bindless utils |
auto_diff | Autodiff |
reduce | Parallel reduction |
DXIL Files (.dxil)
| Key | Description |
|---|
accel_process_vk.dxil | Vulkan acceleration structures |
load_bdls.dxil / load_bdls_vk.dxil | Bindless loading |
set_accel4.dxil | Set acceleration structure |
bc6_encodeblock.dxil / bc6_trymodeg10.dxil / bc6_trymodele10.dxil | BC6 compression |
bc7_encodeblock.dxil / bc7_trymode02.dxil / bc7_trymode137.dxil / bc7_trymode456.dxil | BC7 compression |
Adding Builtins
- Add
.bytes or .dxil to src/backends/common/hlsl/builtin/
- Declare:
LC_HLSL_DECL_VARNAME(my_bytes)
- Register:
LC_HLSL_INSERT_VARNAME(my_bytes, "my_key")
Build: .hlsl → .bytes, shaders → .dxil, embedded via bin2obj.
Codegen Debug
Set env LUISA_DUMP_SOURCE=1 to dump generated HLSL to per-shader files.
Output: hlsl_output_<shader_name>.hlsl in the working directory.
Naming priority depends on the backend and shader path:
DX compute / raster / save paths:
ShaderOption::name / fileName — user-provided name
Function::name() — kernel/callable debug name
Function::hash() formatted as hex — fallback (e.g. hlsl_output_a1b2c3d4.hlsl)
VK internal builtin compute helpers only (ComputeShader::compile_builtin_hlsl_to_spirv):
file_name — user-provided / cached name
- Generated HLSL MD5 — fallback
VK raster (VkRasterExt):
ShaderOption::name is always required, so the dump file uses that name.
VK user compute shaders must use native SPIR-V codegen (LUISA_XIR_TO_SPIRV or LUISA_AST_LLVM_TO_SPIRV); the Vulkan Function compute path must not call HLSL/DXC.
Files are written with "wb" (overwrite) — no need to delete old files.
Each shader gets its own file; no more single hlsl_output.hlsl with appended content.