All notable changes to the packages RecursiveAI.OneShader, RecursiveAI.OneShader.Build and RecursiveAI.OneShader.Tool.
Versions follow Semantic Versioning.
[Unreleased]
1.2.0
- 16-bit and 64-bit types. With
CompileOptions.Enable16BitTypes(DXC's-enable-16bit-types; MSBuildOneShaderEnable16BitTypes, item metadataEnable16BitTypes, CLI-enable-16bit-types)half,min16float,min10float,min16int,min16uintandfloat16_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 thanhalf/min*are error OSH0360, and the min-precision types are 32-bit values marked RelaxedPrecision on SPIR-V, as DXC does.double,int64_tanduint64_twork on SPIR-V in arithmetic, buffers,asdouble,asuint(double, lo, hi)and 64-bitInterlocked*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,longandulong;double, 64-bit integer vectors in buffers and 64-bit atomics other than unsignedInterlockedMax/InterlockedMinwithout a used result (the only form Metal 2.4 has) are errors at the first use (OSH5102) that say what to change. WGSL:halfisf16withenable 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,doubleand 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 serializedentryrecord gets an optionalfeatureschild).EntryLayout.RequiredFeaturesandPipelineLayout.RequiredFeatures;PipelineLayoutInfo.RequiredFeatures(the Vulkan device features to enable);WebGpuPipelineLayout.RequiredFeatureslistsshader-f16. Generated entry classes get aRequiredFeaturesconstant besideSubgroupOperations, and the generatedLayoutcarries it. - Default vertex formats for 16-bit inputs:
Float16x2/Float16x4,Sint16x2/Sint16x4,Uint16x2/Uint16x4. - Pipeline layout helpers (
OneShader.Layouts), additions only.PipelineLayout.TryCreateUnionbuilds one layout for any number of entry points, several per stage; resources at one slot that agree merge with united stages, disagreements are aLayoutErrornaming 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 byTryToMetal/TryToWebGpu.VulkanLayoutOptionsandWebGpuLayoutOptionsgain stage widening (WidenSets/WidenGroups,WidenBindings,WidenPushConstants) and dynamic offsets by slot (DynamicBindings; WebGPU alsoDynamicBuffers);ResourceLayout.DynamicOffset,DescriptorSetLayoutBindingInfo.IsDynamicandBindGroupLayoutEntryInfo.HasDynamicOffsetexpose the flag.EntryLayout.TryWithVertexOverrides/VertexLayout.TryWithOverridesapply 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.TryToMetalalso 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/FalsewithSpecId N, the ids DXC writes for the same source. MSL:constant T Name [[function_constant(N)]], read throughis_function_constant_definedwith 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 (includingstatic constinitializers) 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 orcaselabel 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, aSpecConstantSlotper target with the SPIR-VSpecId, the Metal function-constant index and variable, the WGSL@idand override name) andEntryPointInfo.UsedSpecConstants. The serialized form getsspecandspecuserecords, written only for shaders that have constants; the command-line tool's JSON getsspecConstantsandusedSpecConstantslikewise.InterfaceCompatibilityreports a removed constant or a changed id or type (InterfaceDifferenceKind.SpecConstant); new constants and changed defaults stay compatible. OneShader.Layouts:SpecializationValues(fromEntryLayout.SpecConstants; typedSet, plain data) converts to VulkanVkSpecializationInfocontents (Layouts.Vulkan.SpecializationInfo: map entries and the data block), Metal function-constant values (Layouts.Metal.FunctionConstantValue: index,MTLDataTypenumber, bits) and the WebGPUconstantsrecord keyed by decimal id.EntryLayoutgetsSpecConstants.- Generated code:
SpecializationConstants.<Name>.Id/Defaultconstants, a blittableSpecializationstruct (one 4-byte field per constant,Defaults,ToVulkan/ToMetal/ToWebGpu) andSpecConstantIdson every entry-point class that reads constants; the entry'sLayoutcarries them. In a variant file each variant class has its ownSpecializationConstantsandSpecialization(a constant may exist in only some variants), and the aggregate entry class hasSpecConstantIdsfor the variant it was looked up in. CompileOptions.SpecializationOverridesfixes 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 emptyMTLFunctionConstantValuesselects the defaults). - Shader variants in the build package. A
OneShaderitem takesAxes="ALPHA_TEST;LIGHTING=NONE|LAMBERT|PBR"(flags and choices; the cartesian product is compiled), orOneShaderVariantitems 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 withMaxVariants/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 aVariantwith anEntryper entry point (SpirV,Msl,Wgsl,Layout, ...),BindingsandPipelineper 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. Seedocs/generated-bindings.md. - A file listed explicitly as a
OneShaderitem (to give it metadata) is no longer added a second time by the default**/*.hlslglob. 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: theoneshadercommand-line compiler (a .NET tool, and a NativeAOT single-file binary withdotnet 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 asfile(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 withEntryLayout.TryCreate, or the generatedSprite.VSMain.Layout) andPipelineLayout.TryCreatemerge entry points into bind groups with combined stage visibility and push-constant blocks, returning conflicts as aLayoutErrorvalue.ToVulkan()gives descriptor set layouts, push-constant ranges and vertex input with numericVkDescriptorType/VkShaderStageFlagBits/VkFormatvalues;TryToMetal()gives per-stage buffer, texture and sampler indices, thesetBytesslot andMTLVertexDescriptordata;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 gainLayouton every entry point andPipeline(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/FirstandQuadReadAcrossX/Y/Diagonal/QuadReadLaneAton 32-bit int, uint and float scalars and vectors. SPIR-V usesGroupNonUniform*as DXC does, MSLsimd_*/quad_*, WGSL thesubgroupsfeature (enable subgroups;; not in vertex shaders).WaveMatchandWaveMultiPrefix*are an error (OSH0350). Reflection gainsEntryPointInfo.SubgroupOperations(Vulkan'sVkSubgroupFeatureFlagBitsvalues),EntryPointInfo.UsesSubgroupsandShaderReflection.UsesSubgroups;EntryLayout/PipelineLayout/PipelineLayoutInfo/MetalPipelineLayoutcarrySubgroupOperations,WebGpuPipelineLayout.RequiredFeatureslistssubgroups, and generated entry classes get aSubgroupOperationsconstant.BuiltIngainsSubgroupLocalInvocationIdandSubgroupSize. - Reflection:
ResourceInfo.ImageFormatrecords the[[vk::image_format]]name of a storage texture (the serializedresourcerecord gets an optionalformatattribute, 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
*.hlslduringdotnet 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'smetalcompiler 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,sinand 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.EntryFunctionNamegives the name an entry point has in the WGSL text (reserved words such aslooporfragmentget a trailing underscore). The build task uses it forWgslEntryPointinstead of reading the generated text, and a test checks entry points named after reserved words in MSL and WGSL against naga andmetal. - MSL:
switchcases that fall through and adefaultin 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 abreakorcontinueinside anifinside a case was dropped. WGSL, which has no fall-through, repeats the following case's statements; both are covered by the newlang_switch_flowparity case. - MSL honours
preciseunder Metal's default fast math: functions with precise arithmetic turn offreassociateandcontract, and precise divisions useprecise::divide. preciseon local variables, assignments and function return values now marks the arithmetic that feeds them in every target (the SPIR-V output getsNoContractionon the same operations as DXC; before, the modifier was parsed and ignored).- WGSL: arithmetic marked
precisecompiles 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 aConstantBuffer<T>compiles on every target. SPIR-V gets aPushConstantblock whose member offsets equal DXC's (vector-relaxed std430, not the cbuffer rules); MSL takes it as aconstant T&at a Metal buffer index (set it withsetVertexBytes,setFragmentBytesorsetBytes); WGSL binds it as a uniform buffer at the next free binding of the highest bind group (WebGPU has no push constants). Reflection gainsShaderReflection.PushConstants(layout, stages, per-target slots) andEntryPointInfo.PushConstant; the generated class gains aPushConstantsstruct (offsets andSizeequal to SPIR-V) andBindings.PushConstants(Size,Stagesfor theVkPushConstantRange,MslIndex,WgslGroup/WgslBinding). An entry point that reads two blocks is OSH0333 at the second block; a block over 128 bytes (Vulkan's guaranteedmaxPushConstantsSize) is warning OSH0334;[[vk::push_constant]]on acbufferis OSH0333 (DXC rejects it too). - Build package:
OneShaderTargets(SpirV;Msl;Wgsl, defaultSpirV; per-itemTargetsmetadata) embeds Metal and WGSL sources next to the SPIR-V. Entry-point classes gainMsl,MslFunctionName,MslVertexBufferIndex,WgslandWgslEntryPoint; resource bindings gainMslIndex/MslCounterIndexandResourceBinding.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, andtools/validate-msl.shto check the files with Apple's compiler. Seedocs/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 generatedCount. Unbounded arrays are a clear error.AppendStructuredBufferandConsumeStructuredBuffer(Append,Consume,GetDimensions) with the hidden counter exposed in reflection (counterrecords) and in the generated bindings (CounterBinding, a<name>.counterentry inAll).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) >> 3is0x10000000, 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_CullDistancework 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 forfloat3), which Vulkan validation requires;boolmembers of constant and structured buffers are stored as 32-bit integers.New:
Texture2D.CalculateLevelOfDetailthat validates,GatherCmp/GatherCmpRedon 2D, array and cube textures,f16tof32,f32tof16,faceforward,log10.Overlapping
packoffsetmembers are an error (OSH0332) instead of invalid SPIR-V.ShaderCompiler.Compileturns 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.Fileslists the files a compile read (for file watchers),ShaderCompiler.CompileFilecompiles from disk and reports an unreadable file as diagnostic OSH0003, andInterfaceCompatibility.Comparetells 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 aReflectionmember with the reflection they were generated from. ConcurrentCompilecalls are tested to give the same bytes as serial ones.samples/HotReloadshows 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:
*.hlslcompiled duringdotnet 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 buildonly.