Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
30afa6c
base for wgsl writer
Kbz-8 Apr 14, 2025
bc546c4
removing garbage file
Kbz-8 Apr 14, 2025
bfcd013
adding sanitazation
Kbz-8 Apr 14, 2025
83e6832
working on external statements ci skip
Kbz-8 Apr 14, 2025
82e9ea5
working on external implementation
Kbz-8 Apr 14, 2025
b922d20
working on wgsl backend
Kbz-8 Apr 15, 2025
329249a
adding binding shift to groups
Kbz-8 Apr 17, 2025
49dc8c5
adding some tests
Kbz-8 Apr 24, 2025
f22dcc8
ading tests
Kbz-8 Apr 27, 2025
bb6a9d1
adding tests
Kbz-8 May 18, 2025
11577b7
working on a rebase
Kbz-8 Aug 28, 2025
9b61a03
fixing rebase, fixing unary management, bitwise operators and unit te…
Kbz-8 Aug 28, 2025
a807be0
fixing unit tests, adding type constants, early depth tests, depth wr…
Kbz-8 Aug 30, 2025
b31ac16
pushing a lot of work
Kbz-8 Sep 7, 2025
7e83fe6
adding swizzle assignment removal transformer
Kbz-8 Sep 14, 2025
b46a1f2
fixing codestyle consistency
Kbz-8 Sep 15, 2025
7a7499f
documenting failing WGSL tests, fixing some
Kbz-8 Sep 15, 2025
f0f9d1d
removing merge trashes from gitignore
Kbz-8 Sep 15, 2025
36df049
Merge branch 'main' into main
SirLynix Sep 15, 2025
b3ee2c1
fixing after-merge compilation issues
Kbz-8 Sep 15, 2025
da0e678
fixing all unit tests
Kbz-8 Sep 15, 2025
2e7a582
removing unused nazara's xmake repo
Kbz-8 Sep 15, 2025
bc91adb
removing test example
Kbz-8 Sep 16, 2025
fbb63a4
adding debug logging to CI
Kbz-8 Sep 16, 2025
bc39710
adding missing tests (woops)
Kbz-8 Sep 16, 2025
76ed51d
adding inout implementation of WgslWriter
Kbz-8 Sep 16, 2025
c18c495
adding SamplerType splitting in function calls and function parameters
Kbz-8 Sep 16, 2025
9d059de
implementing missing tests, fixing binding array texture support, fix…
Kbz-8 Sep 16, 2025
af23bc0
removing texture 1d array from wgsl, fixing texture sampling functions
Kbz-8 Sep 16, 2025
c9a3bfd
fixing arrays of textures test
Kbz-8 Sep 16, 2025
be783fd
fixing access member tests
Kbz-8 Sep 16, 2025
67e9430
adding matrix inverse helper
Kbz-8 Sep 17, 2025
61e54b6
fixing desc attributes, adding wgsl to CLI
Kbz-8 Sep 17, 2025
e68e962
adding WGSL to readme
Kbz-8 Sep 17, 2025
9f82312
removing invalid and unused statements and attributes
Kbz-8 Sep 17, 2025
df7cd8e
begenning builtin emulations
Kbz-8 Sep 18, 2025
164dd64
adding unsupported builtin emulation, fixing interpolate attribute
Kbz-8 Sep 20, 2025
e907419
fixing intrinsic test
Kbz-8 Sep 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ jobs:

# Setup compilation mode and install project dependencies
- name: Configure xmake and install dependencies
run: xmake config --plat=${{ matrix.confs.plat }} --arch=${{ matrix.confs.arch }} --kind=${{ matrix.kind }} --mode=${{ matrix.confs.mode }} ${{ env.ADDITIONAL_CONF }} --ccache=n --yes
run: xmake config -vD --plat=${{ matrix.confs.plat }} --arch=${{ matrix.confs.arch }} --kind=${{ matrix.kind }} --mode=${{ matrix.confs.mode }} ${{ env.ADDITIONAL_CONF }} --ccache=n --yes

# Save dependencies
- name: Save cached xmake dependencies
Expand Down
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Nazara Shading Language (NZSL)

NZSL is a shader language inspired by Rust and C++ which compiles to GLSL or SPIR-V (without depending on SPIRV-Cross).
NZSL is a shader language inspired by Rust and C++ which compiles to GLSL, WGSL or SPIR-V (without depending on SPIRV-Cross).

### Why a new shader language?

Expand Down Expand Up @@ -48,25 +48,26 @@ fn main(input: VertOut) -> FragOut

You can find precompiled binaries in the [releases](https://github.com/NazaraEngine/ShaderLang/releases).

NZSL is designed to be embedded in a game engine / game / graphics application that uses GLSL / SPIR-V for its shaders.
NZSL is designed to be embedded in a game engine / game / graphics application that uses GLSL / WGSL / SPIR-V for its shaders.

You can use it to generate GLSL, GLSL ES and SPIR-V in two non-exclusive ways:
You can use it to generate GLSL, GLSL ES, WGSL and SPIR-V in two non-exclusive ways:

1) Using the offline NZSL compiler (nzslc) ahead of time, in a way similar to glslang or glslc today.
2) Use NZSL as a library in your application to compile shaders in a dynamic way, just as they're needed (which can be used to benefit from supported extensions to improve generation).

### Offline compilation

There are two binary tools you can use:
- **nzslc**: shader compiler, for compiling nzsl files to binary nzsl or directly to GLSL/SPIR-V.
- **nzslc**: shader compiler, for compiling nzsl files to binary nzsl or directly to GLSL/WGSL/SPIR-V.
- **nzsla**: shader archiver, store and compress all your compiled shaders in a single file.

**nzslc example usage:**

- Validating shader: `nzslc file.nzsl`
- Compile a shader to GLSL: `nzsl --compile=glsl file.nzsl`
- Compile a shader to WGSL: `nzsl --compile=wgsl file.nzsl`
- Compile a shader to SPIR-V: `nzsl --compile=spv file.nzsl`
- Compile a shader using modules to both GLSL and SPIR-V header includable version: `nzsl --module module_file.nzsl --module module_folder/ --compile=glsl-header,spv-header file.nzsl`
- Compile a shader using modules to GLSL, WGSL and SPIR-V header includable version: `nzsl --module module_file.nzsl --module module_folder/ --compile=glsl-header,wgsl-header,spv-header file.nzsl`

Run `nzslc -h` to see all supported options.

Expand All @@ -86,6 +87,7 @@ Run `nzsla -h` to see all supported options.
#include <NZSL/Parser.hpp>
#include <NZSL/GlslWriter.hpp>
#include <NZSL/SpirvWriter.hpp>
#include <NZSL/WgslWriter.hpp>

int main()
{
Expand All @@ -98,10 +100,14 @@ int main()
nzsl::GlslWriter glslWriter;
nzsl::GlslWriter::Output output = glslWriter.Generate(shaderAst);
// output.code contains GLSL that can directly be used by OpenGL

nzsl::WgslWriter wgslWriter;
nzsl::WgslWriter::Output output = wgslWriter.Generate(shaderAst);
// output.code contains WGSL that can directly be used by WebGPU (or any native implementation)
}
```

The library contains a lot of options to customize the generation process (target SPIR-V/GLSL version, GLSL ES, gl_Position.y flipping, gl_Position.z remapping to match Vulkan semantics, supported OpenGL extensions, etc.).
The library contains a lot of options to customize the generation process (target SPIR-V/GLSL version, GLSL ES, gl_Position.y flipping, gl_Position.z remapping to match Vulkan semantics, supported OpenGL extensions, supported WebGPU features, etc.).

## Integration

Expand Down Expand Up @@ -136,13 +142,11 @@ At one of my previous working place we were using huge HLSL-derived shaders with

NZSL is designed to be small, fast and easy to debug, for example NZSL to GLSL retains a lot of the source code information which could be lost during SSA (SPIR-V) translation, even with debug symbols enabled.

## Is there a DXIL/WGSL backend?

Not yet, as I don't target Direct3D or WebGPU yet.

DXIL is not very different from SPIR-V and WGSL looks a lot like NZSL so it should be quite easy to add, though.
## Is there a DXIL backend?

See [this issue](https://github.com/NazaraEngine/ShaderLang/issues/13) for WGSL.
Not yet, as I don't target Direct3D yet.\
DXIL is not very different from SPIR-V so it should be quite easy to add, though.\
Note that [Shader Model 7 will accept SPIR-V](https://devblogs.microsoft.com/directx/directx-adopting-spir-v/) so NZSL will be usable with Direct3D.

## Are there limitations?

Expand Down
7 changes: 7 additions & 0 deletions include/NZSL/Ast/Transformations/SwizzleTransformer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@ namespace nzsl::Ast
struct Options
{
bool removeScalarSwizzling = false;
bool removeSwizzleAssigment = false;
};

private:
using Transformer::Transform;

ExpressionTransformation Transform(SwizzleExpression&& swizzle) override;
ExpressionTransformation Transform(AssignExpression&& assign) override;

void PushAssignment(AssignExpression* assign) noexcept;
void PopAssignment() noexcept;

std::vector<AssignExpression*> m_assignmentStack;
const Options* m_options;
bool m_inAssignmentLhs = false;
};
}

Expand Down
205 changes: 205 additions & 0 deletions include/NZSL/WgslWriter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright (C) 2025 kbz_8 ([email protected])
// This file is part of the "Nazara Shading Language" project
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef NZSL_WGSLWRITER_HPP
#define NZSL_WGSLWRITER_HPP

#include <NZSL/BackendParameters.hpp>
#include <NZSL/Config.hpp>
#include <NZSL/Ast/ExpressionVisitorExcept.hpp>
#include <NZSL/Ast/Module.hpp>
#include <NZSL/Ast/StatementVisitorExcept.hpp>
#include <NZSL/Ast/TransformerExecutor.hpp>
#include <string>

namespace nzsl
{
class NZSL_API WgslWriter : Ast::ExpressionVisitorExcept, Ast::StatementVisitorExcept
{
public:
using FeaturesSupportCallback = std::function<bool(std::string_view name)>;
struct Environment;
struct Output;

inline WgslWriter();
WgslWriter(const WgslWriter&) = delete;
WgslWriter(WgslWriter&&) = delete;
~WgslWriter() = default;

Output Generate(Ast::Module& module, const BackendParameters& parameters = {});

void SetEnv(Environment environment);

struct Environment
{
FeaturesSupportCallback featuresCallback;
};

struct Output
{
std::string code;
std::unordered_map<std::uint64_t /* set | binding */, unsigned int /*new binding*/> bindingRemap;
bool usesDrawParameterBaseInstanceUniform;
bool usesDrawParameterBaseVertexUniform;
bool usesDrawParameterDrawIndexUniform;
};

static void RegisterPasses(Ast::TransformerExecutor& executor);

private:
struct PreVisitor;
friend PreVisitor;

enum class IntrinsicHelper
{
Infinity,
MatrixInverse,
NaN,
};

// Attributes
struct AutoBindingAttribute;
struct AuthorAttribute;
struct BindingAttribute;
struct BuiltinAttribute;
struct CondAttribute;
struct DepthWriteAttribute;
struct DescriptionAttribute;
struct EarlyFragmentTestsAttribute;
struct EntryAttribute;
struct FeatureAttribute;
struct InterpAttribute;
struct LayoutAttribute;
struct LicenseAttribute;
struct LocationAttribute;
struct SetAttribute;
struct TagAttribute;
struct UnrollAttribute;
struct WorkgroupAttribute;

void Append(const Ast::AliasType& type);
void Append(const Ast::ArrayType& type);
void Append(const Ast::DynArrayType& type);
void Append(const Ast::ExpressionType& type);
void Append(const Ast::ExpressionValue<Ast::ExpressionType>& type);
void Append(const Ast::FunctionType& functionType);
void Append(const Ast::ImplicitArrayType& type);
void Append(const Ast::ImplicitMatrixType& type);
void Append(const Ast::ImplicitVectorType& type);
void Append(const Ast::IntrinsicFunctionType& intrinsicFunctionType);
void Append(const Ast::MatrixType& matrixType);
void Append(const Ast::MethodType& methodType);
void Append(const Ast::ModuleType& moduleType);
void Append(const Ast::NamedExternalBlockType& namedExternalBlockType);
void Append(Ast::NoType);
void Append(Ast::PrimitiveType type);
void Append(const Ast::PushConstantType& pushConstantType);
void Append(const Ast::SamplerType& samplerType);
void Append(const Ast::StorageType& storageType);
void Append(const Ast::StructType& structType);
void Append(const Ast::TextureType& samplerType);
void Append(const Ast::Type& type);
void Append(const Ast::UniformType& uniformType);
void Append(const Ast::VectorType& vecType);
template<typename T> void Append(const T& param);
template<typename T1, typename T2, typename... Args> void Append(const T1& firstParam, const T2& secondParam, Args&&... params);
template<typename... Args> void AppendAttributes(bool appendLine, Args&&... params);
template<typename T> void AppendAttributesInternal(bool& first, const T& param);
template<typename T1, typename T2, typename... Rest> void AppendAttributesInternal(bool& first, const T1& firstParam, const T2& secondParam, Rest&&... params);
void AppendAttribute(bool first, AutoBindingAttribute attribute);
void AppendAttribute(bool first, AuthorAttribute attribute);
void AppendAttribute(bool first, BindingAttribute attribute);
void AppendAttribute(bool first, BuiltinAttribute attribute);
void AppendAttribute(bool first, CondAttribute attribute);
void AppendAttribute(bool first, DepthWriteAttribute attribute);
void AppendAttribute(bool first, DescriptionAttribute attribute);
void AppendAttribute(bool first, EarlyFragmentTestsAttribute attribute);
void AppendAttribute(bool first, EntryAttribute attribute);
void AppendAttribute(bool first, FeatureAttribute attribute);
void AppendAttribute(bool first, InterpAttribute attribute);
void AppendAttribute(bool first, LayoutAttribute attribute);
void AppendAttribute(bool first, LicenseAttribute attribute);
void AppendAttribute(bool first, LocationAttribute attribute);
void AppendAttribute(bool first, SetAttribute attribute);
void AppendAttribute(bool first, TagAttribute attribute);
void AppendAttribute(bool first, UnrollAttribute attribute);
void AppendAttribute(bool first, WorkgroupAttribute attribute);
void AppendComment(std::string_view section);
void AppendCommentSection(std::string_view section);
void AppendIntrinsicHelpers(IntrinsicHelper helper, const Ast::ExpressionType& type);
void AppendHeader(const Ast::Module::Metadata& metadata);
template<typename T> void AppendIdentifier(const T& map, std::size_t id, bool append_module_prefix = false);
void AppendLine(std::string_view txt = {});
template<typename... Args> void AppendLine(Args&&... params);
void AppendModuleAttributes(const Ast::Module::Metadata& metadata);
void AppendStatementList(std::vector<Ast::StatementPtr>& statements);
template<typename T> void AppendValue(const T& value);

void EnterScope();
void LeaveScope(bool skipLine = true);

void RegisterAlias(std::size_t aliasIndex, std::string aliasName);
void RegisterConstant(std::size_t constantIndex, std::string constantName);
void RegisterFunction(std::size_t funcIndex, std::string functionName);
void RegisterModule(std::size_t moduleIndex, std::string moduleName);
void RegisterStruct(std::size_t structIndex, const Ast::StructDescription& structDescription);
void RegisterVariable(std::size_t varIndex, std::string varName, bool isInout = false);

void ScopeVisit(Ast::Statement& node);

void Visit(Ast::ExpressionPtr& expr, bool encloseIfRequired = false);

using ExpressionVisitorExcept::Visit;
void Visit(Ast::AccessFieldExpression& node) override;
void Visit(Ast::AccessIdentifierExpression& node) override;
void Visit(Ast::AccessIndexExpression& node) override;
void Visit(Ast::IdentifierValueExpression& node) override;
void Visit(Ast::AssignExpression& node) override;
void Visit(Ast::BinaryExpression& node) override;
void Visit(Ast::CallFunctionExpression& node) override;
void Visit(Ast::CastExpression& node) override;
void Visit(Ast::ConditionalExpression& node) override;
void Visit(Ast::ConstantArrayValueExpression& node) override;
void Visit(Ast::ConstantValueExpression& node) override;
void Visit(Ast::IdentifierExpression& node) override;
void Visit(Ast::IntrinsicExpression& node) override;
void Visit(Ast::SwizzleExpression& node) override;
void Visit(Ast::TypeConstantExpression& node) override;
void Visit(Ast::UnaryExpression& node) override;

using StatementVisitorExcept::Visit;
void Visit(Ast::BranchStatement& node) override;
void Visit(Ast::BreakStatement& node) override;
void Visit(Ast::ConditionalStatement& node) override;
void Visit(Ast::ContinueStatement& node) override;
void Visit(Ast::DeclareAliasStatement& node) override;
void Visit(Ast::DeclareConstStatement& node) override;
void Visit(Ast::DeclareExternalStatement& node) override;
void Visit(Ast::DeclareFunctionStatement& node) override;
void Visit(Ast::DeclareOptionStatement& node) override;
void Visit(Ast::DeclareStructStatement& node) override;
void Visit(Ast::DeclareVariableStatement& node) override;
void Visit(Ast::DiscardStatement& node) override;
void Visit(Ast::ExpressionStatement& node) override;
void Visit(Ast::ForStatement& node) override;
void Visit(Ast::ForEachStatement& node) override;
void Visit(Ast::ImportStatement& node) override;
void Visit(Ast::MultiStatement& node) override;
void Visit(Ast::NoOpStatement& node) override;
void Visit(Ast::ReturnStatement& node) override;
void Visit(Ast::ScopedStatement& node) override;
void Visit(Ast::WhileStatement& node) override;

struct State;

Environment m_environment;
State* m_currentState;
};
}

#include <NZSL/WgslWriter.inl>

#endif // NZSL_LANGWRITER_HPP
13 changes: 13 additions & 0 deletions include/NZSL/WgslWriter.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (C) 2025 kbz_8 ([email protected])
// This file is part of the "Nazara Shading Language" project
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <NZSL/WgslWriter.hpp>

namespace nzsl
{
inline WgslWriter::WgslWriter() :
m_currentState(nullptr)
{
}
}
6 changes: 4 additions & 2 deletions src/NZSL/Ast/RecursiveVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ namespace nzsl::Ast

void RecursiveVisitor::Visit(CallFunctionExpression& node)
{
node.targetFunction->Visit(*this);

for (auto& param : node.parameters)
param.expr->Visit(*this);

node.targetFunction->Visit(*this);
}

void RecursiveVisitor::Visit(CallMethodExpression& node)
Expand All @@ -59,6 +59,7 @@ namespace nzsl::Ast

void RecursiveVisitor::Visit(ConditionalExpression& node)
{
node.condition->Visit(*this);
node.truePath->Visit(*this);
node.falsePath->Visit(*this);
}
Expand Down Expand Up @@ -124,6 +125,7 @@ namespace nzsl::Ast

void RecursiveVisitor::Visit(ConditionalStatement& node)
{
node.condition->Visit(*this);
node.statement->Visit(*this);
}

Expand Down
Loading
Loading