Add shader compiler for WebGPU
Use existing Vulkan compiler to generate SPIR-V and convert it into WGSL with tint compiler https://github.com/google/dawn/releases/tag/v20260219.200501
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// WebGPU shaders compiler module.
|
||||
/// </summary>
|
||||
public class ShaderCompilerWebGPU : ShaderCompiler
|
||||
{
|
||||
public static bool Use(BuildOptions options)
|
||||
{
|
||||
// Requires prebuilt tint executable in platform ThirdParty binaries folder
|
||||
// https://github.com/google/dawn/releases
|
||||
// License: Source/ThirdParty/tint-license.txt (BSD 3-Clause)
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
return options.Architecture == TargetArchitecture.x64;
|
||||
case TargetPlatform.Linux: // TODO: add Linux binary with tint
|
||||
case TargetPlatform.Mac:
|
||||
return options.Architecture == TargetArchitecture.ARM64;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_WEBGPU_SHADER_COMPILER");
|
||||
options.PublicDependencies.Add("ShaderCompilerVulkan");
|
||||
|
||||
// Deploy tint executable as a dependency for the shader compilation from SPIR-V into WGSL
|
||||
// Tint compiler from: https://github.com/google/dawn/releases
|
||||
// License: Source/ThirdParty/tint-license.txt (BSD 3-Clause)
|
||||
var depsRoot = options.DepsFolder;
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
if (options.Architecture == TargetArchitecture.x64)
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "tint.exe"));
|
||||
break;
|
||||
case TargetPlatform.Linux:
|
||||
if (options.Architecture == TargetArchitecture.x64)
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "tint"));
|
||||
break;
|
||||
case TargetPlatform.Mac:
|
||||
if (options.Architecture == TargetArchitecture.ARM64)
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "tint"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_WEBGPU_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilerWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/GraphicsDevice/Vulkan/Types.h"
|
||||
#include "Engine/Platform/CreateProcessSettings.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/Windows/WindowsFileSystem.h"
|
||||
#include <ThirdParty/glslang/SPIRV/SpvTools.h>
|
||||
#include <ThirdParty/spirv-tools/libspirv.hpp>
|
||||
|
||||
ShaderCompilerWebGPU::ShaderCompilerWebGPU(ShaderProfile profile)
|
||||
: ShaderCompilerVulkan(profile)
|
||||
{
|
||||
}
|
||||
|
||||
bool ShaderCompilerWebGPU::OnCompileBegin()
|
||||
{
|
||||
_globalMacros.Add({ "WGSL", "1" });
|
||||
return ShaderCompilerVulkan::OnCompileBegin();
|
||||
}
|
||||
|
||||
void ShaderCompilerWebGPU::InitParsing(ShaderCompilationContext* context, glslang::TShader& shader)
|
||||
{
|
||||
ShaderCompilerVulkan::InitParsing(context, shader);
|
||||
|
||||
// Use newer SPIR-V
|
||||
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2);
|
||||
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
|
||||
}
|
||||
|
||||
void ShaderCompilerWebGPU::InitCodegen(ShaderCompilationContext* context, glslang::SpvOptions& spvOptions)
|
||||
{
|
||||
ShaderCompilerVulkan::InitCodegen(context, spvOptions);
|
||||
|
||||
// Always optimize SPIR-V
|
||||
spvOptions.disableOptimizer = false;
|
||||
spvOptions.optimizeSize = true;
|
||||
}
|
||||
|
||||
bool ShaderCompilerWebGPU::Write(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, struct SpirvShaderHeader& header, std::vector<unsigned int>& spirv)
|
||||
{
|
||||
auto id = Guid::New().ToString(Guid::FormatType::N);
|
||||
auto folder = Globals::ProjectCacheFolder / TEXT("Shaders");
|
||||
auto inputFile = folder / id + TEXT(".spvasm");
|
||||
auto outputFile = folder / id + TEXT(".wgsl");
|
||||
|
||||
// Convert SPIR-V to WGSL
|
||||
// Tint compiler from: https://github.com/google/dawn/releases
|
||||
// License: Source/ThirdParty/tint-license.txt (BSD 3-Clause)
|
||||
File::WriteAllBytes(inputFile, &spirv[0], (int32)spirv.size() * sizeof(unsigned int));
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.Arguments = String::Format(TEXT("\"{}\" -if spirv -o \"{}\""), inputFile, outputFile);
|
||||
if (!context->Options->NoOptimize)
|
||||
procSettings.Arguments += TEXT(" --minify");
|
||||
procSettings.Arguments += TEXT(" --allow-non-uniform-derivatives"); // Fix sampling texture within non-uniform control flow
|
||||
#if PLATFORM_WINDOWS
|
||||
procSettings.FileName = Globals::BinariesFolder / TEXT("tint.exe");
|
||||
#else
|
||||
procSettings.FileName = Globals::BinariesFolder / TEXT("tint");
|
||||
#endif
|
||||
int32 result = Platform::CreateProcess(procSettings);
|
||||
StringAnsi wgsl;
|
||||
File::ReadAllText(outputFile, wgsl);
|
||||
if (result != 0 || wgsl.IsEmpty())
|
||||
{
|
||||
LOG(Error, "Failed to compile shader '{}' function '{}' (permutation {}) from SPIR-V into WGSL with result {}", context->Options->TargetName, String(meta.Name), permutationIndex, result);
|
||||
#if 1
|
||||
// Convert SPIR-V bytecode to text with assembly
|
||||
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3);
|
||||
std::string spirvText;
|
||||
tools.Disassemble(spirv, &spirvText);
|
||||
File::WriteAllBytes(folder / id + TEXT(".txt"), &spirvText[0], (int32)spirvText.size());
|
||||
#endif
|
||||
#if 1
|
||||
// Dump source code
|
||||
File::WriteAllBytes(folder / id + TEXT(".hlsl"), context->Options->Source, context->Options->SourceLength);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cleanup files
|
||||
FileSystem::DeleteFile(inputFile);
|
||||
FileSystem::DeleteFile(outputFile);
|
||||
|
||||
header.Type = SpirvShaderHeader::Types::WGSL;
|
||||
return WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), wgsl.Get(), wgsl.Length() + 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_WEBGPU_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.h"
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for Web GPU rendering backend.
|
||||
/// </summary>
|
||||
class ShaderCompilerWebGPU : public ShaderCompilerVulkan
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompilerWebGPU"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompilerWebGPU(ShaderProfile profile);
|
||||
|
||||
protected:
|
||||
bool OnCompileBegin() override;
|
||||
void InitParsing(ShaderCompilationContext* context, glslang::TShader& shader) override;
|
||||
void InitCodegen(ShaderCompilationContext* context, glslang::SpvOptions& spvOptions) override;
|
||||
bool Write(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, struct SpirvShaderHeader& header, std::vector<unsigned>& spirv) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user