Skip to content

Releases: recursive-ai-oss/oneshader-csharp

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 05:46
  • Bindless resources. Unbounded resource arrays (Texture2D Tex[] : register(t0, space1); also samplers, structured, byte-address, constant buffers and read-write textures) and the Shader Model 6.6 heaps ResourceDescriptorHeap[i] / SamplerDescriptorHeap[i] compile on SPIR-V and Metal; before, unbounded arrays were error OSH0350. SPIR-V: runtime descriptor arrays with RuntimeDescriptorArray and SPV_EXT_descriptor_indexing, NonUniform decorations on the index, the access chain, the loaded descriptor and the data loaded through it, and DXC's default heap mapping (each resource type read from a heap is an unbounded array; the resource heap takes the first unused binding of set 0, the sampler heap the next; CompileOptions.ResourceHeapBinding/SamplerHeapBinding and the CLI's -fvk-bind-resource-heap/-fvk-bind-sampler-heap choose them). A heap element becomes a resource where it is assigned to a resource variable, cast, or passed to a function; misuse is OSH0336. Metal: each unbounded array or heap is one argument buffer of 8-byte descriptors (a texture's or sampler's gpuResourceID, a buffer's gpuAddress) at one buffer index, read through constant T_entry*; needs argument buffers tier 2 and Metal 3. WGSL: an error at the declaration or first use (OSH5202), only when WGSL is a selected target. Non-bindless output on every target is unchanged.
  • Reflection: ResourceInfo.IsUnbounded (Count 0), Heap (DescriptorHeaps) and HeapKinds; EntryPointInfo.DescriptorHeaps; DeviceFeatures.RuntimeDescriptorArray, NonUniformIndexing and MutableDescriptorType (also in EntryLayout/PipelineLayout/PipelineLayoutInfo.RequiredFeatures). The serialized form gets optional heap/heapkinds attributes and a heaps child; the tool's JSON gets unbounded, heap, heapKinds, descriptorHeaps and requiredFeatures.
  • OneShader.Layouts: ResourceLayout.IsUnbounded, Heap, HeapKinds. ToVulkan gives an unbounded binding a maximum descriptor count (VulkanLayoutOptions.UnboundedMaxCount, default 4096, per binding UnboundedCounts), PartiallyBound and, on the highest binding of a set, VariableDescriptorCount (options PartiallyBound, VariableDescriptorCount, UpdateAfterBind; DescriptorSetLayoutBindingInfo.BindingFlags, DescriptorSetLayoutInfo.CreateFlags), and a heap read as several kinds a Mutable descriptor with MutableTypes. DescriptorSetLayoutInfo.HasVariableCount tells which sets need the variable-count allocation info. TryToMetal reports MetalArgument.IsArgumentBuffer, DescriptorStride 8, ElementKinds, Encoding. TryToWebGpu returns a LayoutError.
  • Generated bindings: Bindings.<Name>.IsUnbounded (and Heap for a heap), ResourceBinding.IsUnbounded; the generated Layout carries the heap data; variants work.
  • The corpus gains bindless_textures, bindless_buffers and bindless_heap; the parity harness creates partially bound, update-after-bind, variable-count and mutable descriptor bindings, and the Metal runner executes argument-buffer cases.
  • PipelineLayout.ToBindings describes unbounded arrays and heaps: BindingDescription.Count is 0 with IsUnbounded, Heap and HeapKinds for a descriptor heap, PartiallyBound (true for every unbounded binding) and HasVariableCount (the highest binding of its set). Union layouts merge equal unbounded slots and refuse an unbounded slot against a sized one.
  • A scalar texel offset on a 2D or 3D texture (Sample(s, uv, 1)) applies to every axis, as in DXC; before, it produced invalid SPIR-V.

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 05:20
  • Pipeline layout helpers, additions only. UnionOptions for PipelineLayout.TryCreateUnion(entries, options, ...): IgnoreTextureDimension lets a 2D and a cube texture share a slot, MergeStorageAccess merges read-only and read-write buffers of one family to read-write. WebGpuLayoutOptions.TextureDimensions picks the viewDimension of such a slot (without a pick TryToWebGpu reports it as unsupported, and it refuses a merged read-only/read-write buffer unless ReadOnlyStorageBuffers names it). PipelineLayout.ToBindings(BindingOptions?) returns target-neutral BindingDescriptions (BindingType, TextureDimension, TextureSampleKind, comparison flag, count, stages, dynamic flag, Metal index). VertexLayout.TrySplit(streams, ..., overrides) spreads a vertex input over several buffers with per-vertex or per-instance step (VertexStream, VertexStreamLayout.ToVulkan/ToMetal/ToWebGpu); overlapping attributes, a too-small stride and a location in no stream are LayoutErrors.
  • Source-level debug information. CompileOptions.DebugInfo (DebugInfoLevel.None by default; Lines, Full) and DebugSourceRoot. SPIR-V: Lines writes OpSource (with the text of the main and each included file) and OpLine/OpNoLine, as DXC's -Zi; Full writes NonSemantic.Shader.DebugInfo.100 (compilation unit, one DebugSource per file, functions, lexical blocks, inlined-at records, local variables with DebugDeclare/DebugValue, global variables, types, lines, entry point), as DXC's -fspv-debug=vulkan-with-source, for RenderDoc and Nsight. MSL: #line directives for Metal's -gline-tables-only -frecord-sources and Xcode. WGSL is unchanged. Paths in the output are relative to DebugSourceRoot and never absolute. Off by default, and then every blob is byte-identical to before. The optimizer keeps running with debug information on and keeps positions through every pass; Optimize = false gives one step per statement with every local visible. Command line: -Zi, -fspv-debug=vulkan-with-source (also vulkan, file, source, line, tool), --debug-root. Build package: OneShaderDebugInfo (true/full/lines/false) and OneShaderOptimize, item metadata DebugInfo and Optimize. See docs/debugging.md.

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 03:04
  • 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).

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 00:43
  • 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).

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 15:37

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.