---
title: "Changelog"
url: https://recursiveai.org/oneshader/docs/changelog/
description: "What changed in each OneShader release, from the first preview to 1.2.0."
version: "1.2.0"
source: https://github.com/recursive-ai-oss/oneshader-csharp/blob/8896e63be3a4f246295427451f15e4fcfb5535cf/CHANGELOG.md
---

# Changelog

All notable changes to the packages `RecursiveAI.OneShader`, `RecursiveAI.OneShader.Build` and `RecursiveAI.OneShader.Tool`.
Versions follow [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [1.2.0]

- 16-bit and 64-bit types. With `CompileOptions.Enable16BitTypes` (DXC's `-enable-16bit-types`; MSBuild `OneShaderEnable16BitTypes`, item metadata `Enable16BitTypes`, CLI `-enable-16bit-types`) `half`, `min16float`, `min10float`, `min16int`, `min16uint` and `float16_t`/`int16_t`/`uint16_t` (vectors and matrices) are native 16-bit types, in arithmetic and in buffers with DXC's byte layout; without it the 16-bit names other than `half`/`min*` are error OSH0360, and the min-precision types are 32-bit values marked RelaxedPrecision on SPIR-V, as DXC does. `double`, `int64_t` and `uint64_t` work on SPIR-V in arithmetic, buffers, `asdouble`, `asuint(double, lo, hi)` and 64-bit `Interlocked*` on structured buffers and group-shared memory. A double argument to a float-only intrinsic (`floor`, `sqrt`, ...) is narrowed to float as DXC does, with the new warning OSH0361. Unsuffixed float literals keep double precision until they meet a concrete type. Constant folding is exact at each width.
- MSL: native `half`, `short`, `ushort`, `long` and `ulong`; `double`, 64-bit integer vectors in buffers and 64-bit atomics other than unsigned `InterlockedMax`/`InterlockedMin` without a used result (the only form Metal 2.4 has) are errors at the first use (OSH5102) that say what to change. WGSL: `half` is `f16` with `enable f16;` (16-bit values in buffers are read and written as halves of 32-bit words; stores use atomic and/or on the word so neighbours written by other invocations are not disturbed); 16-bit integers, `double` and 64-bit integers are errors at the first use (OSH5202). A refusal is an error only when that target is selected.
- Reflection: `EntryPointInfo.RequiredFeatures` (`DeviceFeatures`: `Float16`, `Int16`, `Float64`, `Int64`, `StorageBuffer16`, `UniformBuffer16`, `PushConstant16`, `InputOutput16`, `Int64Atomics`; the serialized `entry` record gets an optional `features` child). `EntryLayout.RequiredFeatures` and `PipelineLayout.RequiredFeatures`; `PipelineLayoutInfo.RequiredFeatures` (the Vulkan device features to enable); `WebGpuPipelineLayout.RequiredFeatures` lists `shader-f16`. Generated entry classes get a `RequiredFeatures` constant beside `SubgroupOperations`, and the generated `Layout` carries it.
- Default vertex formats for 16-bit inputs: `Float16x2`/`Float16x4`, `Sint16x2`/`Sint16x4`, `Uint16x2`/`Uint16x4`.
- Pipeline layout helpers (`OneShader.Layouts`), additions only. `PipelineLayout.TryCreateUnion` builds one layout for any number of entry points, several per stage; resources at one slot that agree merge with united stages, disagreements are a `LayoutError` naming both entry points and the slot, and push-constant blocks merge into one covering range when their overlapping members agree (`EntryLayout.PushConstantType`, `WithPushConstantType`). Entry points compiled from different files that disagree on a Metal index or WebGPU push slot are reported by `TryToMetal`/`TryToWebGpu`. `VulkanLayoutOptions` and `WebGpuLayoutOptions` gain stage widening (`WidenSets`/`WidenGroups`, `WidenBindings`, `WidenPushConstants`) and dynamic offsets by slot (`DynamicBindings`; WebGPU also `DynamicBuffers`); `ResourceLayout.DynamicOffset`, `DescriptorSetLayoutBindingInfo.IsDynamic` and `BindGroupLayoutEntryInfo.HasDynamicOffset` expose the flag. `EntryLayout.TryWithVertexOverrides` / `VertexLayout.TryWithOverrides` apply the host's stride, and per-location format and offset, checked against the shader's input type (`LayoutErrorKind.InvalidVertexInput`); Vulkan, Metal and WebGPU vertex descriptions follow. `TryToMetal` also reports two resources of one stage that take the same Metal index.
- Specialization constants. `[[vk::constant_id(N)]] const <bool|int|uint|float> Name = <default>;` becomes each target's pipeline-time constant, so one compiled shader serves many variants without recompiling. SPIR-V: `OpSpecConstant`/`OpSpecConstantTrue`/`False` with `SpecId N`, the ids DXC writes for the same source. MSL: `constant T Name [[function_constant(N)]]`, read through `is_function_constant_defined` with the default as the fallback. WGSL: `@id(N) override Name: T = default;`. Optimization never folds a specialization constant to its default; code derived from one (including `static const` initializers) is ordinary code that runs per pipeline. Mistakes are errors at the HLSL line: a duplicate id, a vector or struct type, `static`, a missing or non-constant default, a negative id (OSH0335); an array size, `[numthreads]` argument or `case` label that uses one says why it is refused (OSH0310), as DXC refuses the same uses.
- Reflection: `ShaderReflection.SpecConstants` (`SpecConstantInfo`: name, id, scalar type, default bits and value, a `SpecConstantSlot` per target with the SPIR-V `SpecId`, the Metal function-constant index and variable, the WGSL `@id` and override name) and `EntryPointInfo.UsedSpecConstants`. The serialized form gets `spec` and `specuse` records, written only for shaders that have constants; the command-line tool's JSON gets `specConstants` and `usedSpecConstants` likewise. `InterfaceCompatibility` reports a removed constant or a changed id or type (`InterfaceDifferenceKind.SpecConstant`); new constants and changed defaults stay compatible.
- `OneShader.Layouts`: `SpecializationValues` (from `EntryLayout.SpecConstants`; typed `Set`, plain data) converts to Vulkan `VkSpecializationInfo` contents (`Layouts.Vulkan.SpecializationInfo`: map entries and the data block), Metal function-constant values (`Layouts.Metal.FunctionConstantValue`: index, `MTLDataType` number, bits) and the WebGPU `constants` record keyed by decimal id. `EntryLayout` gets `SpecConstants`.
- Generated code: `SpecializationConstants.<Name>.Id`/`Default` constants, a blittable `Specialization` struct (one 4-byte field per constant, `Defaults`, `ToVulkan`/`ToMetal`/`ToWebGpu`) and `SpecConstantIds` on every entry-point class that reads constants; the entry's `Layout` carries them. In a variant file each variant class has its own `SpecializationConstants` and `Specialization` (a constant may exist in only some variants), and the aggregate entry class has `SpecConstantIds` for the variant it was looked up in.
- `CompileOptions.SpecializationOverrides` fixes chosen constants at compile time: they become plain constants, get folded, and leave reflection. A value that does not fit the type is error OSH0335; an unknown name is warning OSH0335.
- Metal note: a function that declares function constants cannot build a pipeline state as it is, even when every default is wanted; create it with `makeFunction(name:constantValues:)` (an empty `MTLFunctionConstantValues` selects the defaults).
- Shader variants in the build package. A `OneShader` item takes `Axes="ALPHA_TEST;LIGHTING=NONE|LAMBERT|PBR"` (flags and choices; the cartesian product is compiled), or `OneShaderVariant` items name hand-picked define sets. One compile per variant per selected target, in parallel; only variants whose define set, shader, includes or options changed are recompiled. More than 64 variants is error OSH0905 (raise it with `MaxVariants` / `OneShaderMaxVariants`); malformed declarations are OSH0904 and OSH0906. An error that happens under some variants is reported once on the HLSL line with the define set (`(variant ALPHA_TEST=1, LIGHTING=PBR)`).
- Generated code for a file with variants: `Key` (one property per axis, enums for choices), `Variants.Get(key)` returning a `Variant` with an `Entry` per entry point (`SpirV`, `Msl`, `Wgsl`, `Layout`, ...), `Bindings` and `Pipeline` per variant, and a static class per variant shaped like a single file's class. Constant-buffer, push-constant and vertex structs are generated once when all variants agree on the layout, per variant when they differ. Files without variants generate exactly the same C# as before. See `docs/generated-bindings.md`.
- A file listed explicitly as a `OneShader` item (to give it metadata) is no longer added a second time by the default `**/*.hlsl` glob.
- `ShaderCompileCache` (`RecursiveAI.OneShader`): thread-safe, bounded compile cache keyed by source, path, options and the content of the includes the compile read; concurrent calls for one key compile once; `Clear()` empties it.
- The build task logs `OneShader: N compiled, M up to date.` at low importance (`-v:d`).

## [1.1.0]

- New package `RecursiveAI.OneShader.Tool`: the `oneshader` command-line compiler (a .NET tool, and a NativeAOT single-file binary with `dotnet publish -r <rid>`). It takes DXC's core flags (`-T`, `-E`, `-D`, `-I`, `-Fo`, `-spirv`, `-O0`..`-O3`, `-Zpr`/`-Zpc`, `-HV 2021`, `-WX`) plus `--target spirv|msl|wgsl` (repeatable), `--all-entries`, `--reflect <file.json>` and `--depfile <file>`. Diagnostics print as `file(line,col): error OSHnnnn: message`; exit code 0, 1 for compile errors, 2 for command-line errors.
- Pipeline layout helpers (`OneShader.Layouts`): plain data, no graphics API behind it, that turns a shader into what a renderer creates. `EntryLayout` (from reflection with `EntryLayout.TryCreate`, or the generated `Sprite.VSMain.Layout`) and `PipelineLayout.TryCreate` merge entry points into bind groups with combined stage visibility and push-constant blocks, returning conflicts as a `LayoutError` value. `ToVulkan()` gives descriptor set layouts, push-constant ranges and vertex input with numeric `VkDescriptorType`/`VkShaderStageFlagBits`/`VkFormat` values; `TryToMetal()` gives per-stage buffer, texture and sampler indices, the `setBytes` slot and `MTLVertexDescriptor` data; `TryToWebGpu()` gives bind group layout entries with the WebGPU enum strings, push constants as a uniform buffer at the reserved group and binding, and vertex buffer layouts. Generated classes gain `Layout` on every entry point and `Pipeline` (vertex + pixel, or one compute entry point) on the file.
- Wave (subgroup) and quad intrinsics on all three targets: `WaveGetLaneCount`/`Index`, `WaveIsFirstLane`, `WaveActiveAnyTrue`/`AllTrue`/`AllEqual`/`Ballot`/`Sum`/`Product`/`Min`/`Max`/`BitAnd`/`BitOr`/`BitXor`/`CountBits`, `WavePrefixSum`/`Product`/`CountBits`, `WaveReadLaneAt`/`First` and `QuadReadAcrossX`/`Y`/`Diagonal`/`QuadReadLaneAt` on 32-bit int, uint and float scalars and vectors. SPIR-V uses `GroupNonUniform*` as DXC does, MSL `simd_*`/`quad_*`, WGSL the `subgroups` feature (`enable subgroups;`; not in vertex shaders). `WaveMatch` and `WaveMultiPrefix*` are an error (OSH0350). Reflection gains `EntryPointInfo.SubgroupOperations` (Vulkan's `VkSubgroupFeatureFlagBits` values), `EntryPointInfo.UsesSubgroups` and `ShaderReflection.UsesSubgroups`; `EntryLayout`/`PipelineLayout`/`PipelineLayoutInfo`/`MetalPipelineLayout` carry `SubgroupOperations`, `WebGpuPipelineLayout.RequiredFeatures` lists `subgroups`, and generated entry classes get a `SubgroupOperations` constant. `BuiltIn` gains `SubgroupLocalInvocationId` and `SubgroupSize`.
- Reflection: `ResourceInfo.ImageFormat` records the `[[vk::image_format]]` name of a storage texture (the serialized `resource` record gets an optional `format` attribute, written only when set).

## [1.0.0]

First stable release. The public API (`ShaderCompiler`, `CompileOptions`, `CompileResult`, reflection, `InterfaceCompatibility`) and the generated C# shape are now covered by Semantic Versioning.

- Vertex, pixel and compute shaders in modern HLSL (HLSL 2021 templates included) compile to optimized SPIR-V for Vulkan 1.2, Metal Shading Language and WGSL, each with reflection; the build package compiles `*.hlsl` during `dotnet build`, reports errors on the HLSL line, embeds the selected targets (`OneShaderTargets`) and generates C# structs and binding code.
- Since 0.4: the packages are built with deterministic paths, embedded untracked sources and a symbol package (`.snupkg`); package metadata links to the public repository; the four-platform CI (Windows x64, Linux x64 and arm64, macOS arm64) runs the compiler tests, the build-package tests and the NativeAOT smoke app, and runs the reference-tool tests (DXC, `spirv-val`, naga) wherever those tools are released.
- Tested: the five fuzz targets (preprocessor, front end, IR and optimizer, every backend) ran clean for 600 s each, and the corpus is checked with `spirv-val`, by running it on GPUs against DXC's output, with naga, with Apple's `metal` compiler and on WebGPU.

## [0.4.0-preview.1]

- Hardening from fuzzing every compile path: crashes, hangs and invalid SPIR-V or WGSL on unusual input are now diagnostics. Errors are capped at 100 per compile (OSH0004) and identical consecutive errors are dropped; array dimensions are limited to 16, local array initializers to 4096 elements, and binding, set and register numbers to 65535; the preprocessor's default output and expansion limits are 2M tokens and 20M steps. Component-wise intrinsics on matrices (`saturate`, `lerp`, `pow`, `sin` and others) work on every target.
- Very deep call chains no longer overflow the stack on threads with a small stack (seen on macOS).
- WGSL: `WgslBackend.EntryFunctionName` gives the name an entry point has in the WGSL text (reserved words such as `loop` or `fragment` get a trailing underscore). The build task uses it for `WgslEntryPoint` instead of reading the generated text, and a test checks entry points named after reserved words in MSL and WGSL against naga and `metal`.
- MSL: `switch` cases that fall through and a `default` in the middle now run correctly. Before, a case entered by falling through re-ran the switch edge's phi copies and overwrote the value it fell in with, and a `break` or `continue` inside an `if` inside a case was dropped. WGSL, which has no fall-through, repeats the following case's statements; both are covered by the new `lang_switch_flow` parity case.
- MSL honours `precise` under Metal's default fast math: functions with precise arithmetic turn off `reassociate` and `contract`, and precise divisions use `precise::divide`.
- `precise` on local variables, assignments and function return values now marks the arithmetic that feeds them in every target (the SPIR-V output gets `NoContraction` on the same operations as DXC; before, the modifier was parsed and ignored).
- WGSL: arithmetic marked `precise` compiles with warning OSH5205, because WGSL cannot forbid a fused multiply-add.

## [0.3.0-preview.1]

- Push constants: `[[vk::push_constant]]` on a struct-typed global or a `ConstantBuffer<T>` compiles on every target. SPIR-V gets a `PushConstant` block whose member offsets equal DXC's (vector-relaxed std430, not the cbuffer rules); MSL takes it as a `constant T&` at a Metal buffer index (set it with `setVertexBytes`, `setFragmentBytes` or `setBytes`); WGSL binds it as a uniform buffer at the next free binding of the highest bind group (WebGPU has no push constants). Reflection gains `ShaderReflection.PushConstants` (layout, stages, per-target slots) and `EntryPointInfo.PushConstant`; the generated class gains a `PushConstants` struct (offsets and `Size` equal to SPIR-V) and `Bindings.PushConstants` (`Size`, `Stages` for the `VkPushConstantRange`, `MslIndex`, `WgslGroup`/`WgslBinding`). An entry point that reads two blocks is OSH0333 at the second block; a block over 128 bytes (Vulkan's guaranteed `maxPushConstantsSize`) is warning OSH0334; `[[vk::push_constant]]` on a `cbuffer` is OSH0333 (DXC rejects it too).
- Build package: `OneShaderTargets` (`SpirV;Msl;Wgsl`, default `SpirV`; per-item `Targets` metadata) embeds Metal and WGSL sources next to the SPIR-V. Entry-point classes gain `Msl`, `MslFunctionName`, `MslVertexBufferIndex`, `Wgsl` and `WgslEntryPoint`; resource bindings gain `MslIndex` / `MslCounterIndex` and `ResourceBinding.MetalIndex`. A target's refusal (for example OSH5202 on WGSL) is a build error on the HLSL line only when that target is selected; unknown target names are OSH0903.
- New WGSL target (`CompileTarget.Wgsl`): vertex, fragment and compute entry points as WGSL text for WebGPU, with the same buffer byte layouts as the SPIR-V target. See docs/wgsl-backend.md for what is not available.
- New: Metal Shading Language output (`CompileTarget.Msl`, `MslBackend`): one source file per entry point with the same buffer byte layout as the SPIR-V output, Metal argument indices in reflection, and `tools/validate-msl.sh` to check the files with Apple's compiler. See `docs/msl-backend.md`.

## [0.2.0-preview.1]

- Optimizer, on by default (`CompileOptions.Optimize`): inlining, SSA promotion, constant folding, common-subexpression and dead-code elimination, control-flow simplification. SPIR-V is about 38% smaller than before and close to DXC's size.
- HLSL 2021 language features: function and struct templates, member functions and `this`, single inheritance, operator overloads, `sizeof`, static locals, resource-typed locals and parameters.
- Bounded resource arrays end to end (`Texture2D T[4]`, `SamplerState S[2]`, arrays of structured buffers and constant buffers): constant and run-time indexing, `NonUniformResourceIndex`, elements passed to functions, reflection and generated `Count`. Unbounded arrays are a clear error.
- `AppendStructuredBuffer` and `ConsumeStructuredBuffer` (`Append`, `Consume`, `GetDimensions`) with the hidden counter exposed in reflection (`counter` records) and in the generated bindings (`CounterBinding`, a `<name>.counter` entry in `All`).
- A constant buffer may share its name with one of its members, and a whole `ConstantBuffer<S>` can be copied into a struct local (`S s = cb;`).
- Static struct members, member functions defined outside their struct, template argument deduction through struct templates (`Box<T>`), and integer literal arithmetic in 64 bits (`(1 << 31) >> 3` is `0x10000000`, as in DXC).
- A resource array occupies one binding number (it used to take one per element when numbering unannotated resources).
- Stage inputs and outputs may be arrays and matrices (one location per element or row, like DXC), and `SV_ClipDistance` / `SV_CullDistance` work in vertex outputs and pixel inputs, as scalars, vectors or arrays.
- `StructuredBuffer<float3>` and other elements whose size is not a multiple of their alignment use the aligned stride (16 bytes for `float3`), which Vulkan validation requires; `bool` members of constant and structured buffers are stored as 32-bit integers.
- New: `Texture2D.CalculateLevelOfDetail` that validates, `GatherCmp`/`GatherCmpRed` on 2D, array and cube textures, `f16tof32`, `f32tof16`, `faceforward`, `log10`.
- Overlapping `packoffset` members are an error (OSH0332) instead of invalid SPIR-V.
- `ShaderCompiler.Compile` turns an unexpected internal exception into error OSH7000 on line 1 of the file instead of throwing; the build task reports internal errors at line 1 as well.

- Hot reload support: `CompileResult.Files` lists the files a compile read (for file watchers), `ShaderCompiler.CompileFile` compiles from disk and reports an unreadable file as diagnostic OSH0003, and `InterfaceCompatibility.Compare` tells whether a recompiled shader still matches the generated C# (constant-buffer offsets and types, vertex inputs, bindings, entry points, thread-group sizes), listing each difference. Generated shader classes gain a `Reflection` member with the reflection they were generated from. Concurrent `Compile` calls are tested to give the same bytes as serial ones. `samples/HotReload` shows the loop.

## [0.1.0-preview.1]

First preview.

- HLSL compiler for vertex, pixel and compute shaders (modern HLSL, HLSL 2021): preprocessor, constant buffers, textures and samplers, structured and byte-address buffers, storage images, groupshared memory, barriers and atomics.
- SPIR-V output for Vulkan 1.2, with reflection; buffer layouts and descriptor bindings follow DXC's Vulkan conventions.
- MSBuild integration: `*.hlsl` compiled during `dotnet build`, errors on the HLSL line, embedded SPIR-V and generated C# structs, vertex inputs and binding constants.
- Known limits: SPIR-V only; single resources only (no resource arrays); no templates, member functions, operator overloads or wave intrinsics yet; `dotnet build` only.

[1.2.0]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v1.2.0
[1.1.0]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v1.1.0
[1.0.0]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v1.0.0
[0.4.0-preview.1]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v0.4.0-preview.1
[0.3.0-preview.1]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v0.3.0-preview.1
[0.2.0-preview.1]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v0.2.0-preview.1
[0.1.0-preview.1]: https://github.com/recursive-ai-oss/oneshader-csharp/releases/tag/v0.1.0-preview.1
