Principal Differences between Blender BSL and C++¶
This document is an overview of how Blender Shading Language (BSL) differs from standard C++ (C++14), focusing only on the most important semantic and syntactic deviations.
Refer to the BSL specification for more details.
1. Purpose and Compilation Model¶
BSL is not a general-purpose language.
- BSL code is transpiled, not compiled to machine code.
- It targets GPU shading languages (GLSL 4.3, MSL 2.3).
- A single source must be valid C++ and valid BSL.
- Shader variants are expressed through language constructs, not preprocessor macros.
2. Unsupported or Removed C++ Features¶
BSL removes many core C++ features that are incompatible with GPU execution:
- Pointers of any kind
- Dynamic memory (
new,delete) - Virtual functions, inheritance, polymorphism
- Exceptions, RTTI (
typeid,dynamic_cast) - Lambdas
- Operator overloading
- Iterators
- Constructors and destructors
gotonoexceptextern,thread_local,register
Classes are closer to plain data containers than C++ objects.
3. Restricted Control Flow¶
- All control-flow statements must use braces (
{}), even for single statements. forloops may be annotated with[[unroll]]or[[unroll_n(N)]].breakandcontinueare forbidden inside unrolled loops.- Additionally, GPU-specific uniform vs non-uniform control flow rules apply, similar to GLSL.
4. Strong Reliance on Attributes¶
BSL heavily uses C++-style attributes ([[...]]) for semantics that do not exist in C++:
- Shader stages:
[[vertex]],[[fragment]],[[compute]] - Resource bindings:
[[uniform]],[[storage]],[[sampler]],[[image]] - Pipeline specialization:
[[compilation_constant]],[[specialization_constant]] - Control flow:
[[static_branch]],[[unroll]]
Attributes are semantically mandatory, not just annotations.
5. Entry Points and main¶
- The global function
mainis reserved and forbidden. - Shader entry points are ordinary functions annotated with stage attributes.
-
Entry point functions:
- Must return
void - Cannot be overloaded
- Require all parameters to be explicitly annotated (
[[in]],[[out]], built-ins, or resource tables)
- Must return
6. Types: GPU-Oriented, Not C++-Oriented¶
Scalars¶
int,uint,floatare always 32-bit.doubleexists only as a literal, not as a type.boolmay be 1-bit;bool32_tis required for buffer layouts.
Vectors and Matrices¶
- Built-in vector types (
float3,int4, etc.) and matrix types (float4x4). - Column-major matrices only.
- No user-defined operator overloading.
Strings¶
string_tis not a runtime string.- Stored as a 32-bit hash.
- Immutable, comparable only via built-in functions.
7. References Are Not Real References¶
- References may behave like copies in function arguments.
- Aliasing the same variable through multiple references in a function call is undefined behavior.
- Local references are syntactic sugar and are expanded by the compiler. Definitions cannot contain side effects.
This is very different from C++ reference semantics.
8. Templates: Heavily Constrained¶
- No default template arguments.
- All instantiations must be explicit and in the same file.
- Template argument deduction is forced if all arguments are present in the function parameters (implemented as overload).
- Explicit template arguments are needed if function overload is not possible.
Templates exist mainly for code generation, not abstraction.
9. Namespaces: Limited Visibility Rules¶
using namespace X;is forbidden.- Namespace elision only works in the immediate definition scope.
- Symbols must be imported explicitly with
using X::Y. - Only symbols from the same namespace can be imported at namespace scope.
10. Classes and Structs¶
- No inheritance or nesting.
- No forward declarations.
- No constructors or operators.
- Methods must be defined inline in the class.
Host-Shared Classes¶
[[host_shared]]enforces strict CPU/GPU layout compatibility.- Layout follows
std140/std430-like rules. - Members must be padded and 16-byte aligned.
This is far stricter than standard C++ layout rules.
11. Resource Management via Shader Resource Tables (SRT)¶
Instead of pointers, references, or descriptors:
- Resources are grouped into Shader Resource Tables.
- Resources are accessed structurally, not dynamically.
- Conditional presence of resources is supported via compile-time conditions.
This has no direct C++ equivalent.
12. Preprocessor Differences¶
- Excepted
#includeand#pragma once, all directives are left untouched during compilation and will be copied as-is into the compatible-language source. This is to allow runtime evaluation of theses directive (e.g. switch implementation based on system config). - Likewise, macro calls will be considered as regular tokens and are not expanded during the compilation step.
- For these two reasons, usage of preprocessor directives is strongly discouraged.
#pragma onceis mandatory for headers.#includeuses prepend semantics, not textual replacement.#includewill not respect conditionals (e.g.#if)