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:
@@ -43,7 +43,7 @@ DotNetAOTModes WebPlatformTools::UseAOT() const
|
||||
|
||||
PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
||||
{
|
||||
// TODO: texture compression for Web (eg. ASTC for mobile and BC for others?)
|
||||
// Bundle raw textures
|
||||
return PixelFormatExtensions::FindUncompressedFormat(format);
|
||||
}
|
||||
|
||||
|
||||
@@ -577,7 +577,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
case BuildPlatform::Web:
|
||||
{
|
||||
const char* platformDefineName = "PLATFORM_WEB";
|
||||
// TODO: compile shaders for WebGPU
|
||||
COMPILE_PROFILE(WebGPU, SHADER_FILE_CHUNK_INTERNAL_GENERIC_CACHE);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -166,8 +166,14 @@ WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
|
||||
for (int32 i = 0; i < _fragmentDesc.targetCount; i++)
|
||||
_colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i];
|
||||
WGPUVertexBufferLayout buffers[GPU_MAX_VB_BINDED];
|
||||
PipelineDesc.vertex.bufferCount = key.VertexBufferCount;
|
||||
int32 shaderLocation = 0;
|
||||
for (int32 i = 0; i < PipelineDesc.vertex.bufferCount; i++)
|
||||
{
|
||||
buffers[i] = *key.VertexBuffers[i];
|
||||
for (int32 j = 0; j < buffers[i].attributeCount; j++)
|
||||
((WGPUVertexAttribute&)buffers[i].attributes[j]).shaderLocation = shaderLocation++;
|
||||
}
|
||||
PipelineDesc.vertex.buffers = buffers;
|
||||
|
||||
// Create object
|
||||
@@ -196,13 +202,10 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
|
||||
if (IsValid())
|
||||
OnReleaseGPU();
|
||||
|
||||
// Cache shaders
|
||||
VS = (GPUShaderProgramVSWebGPU*)desc.VS;
|
||||
PS = (GPUShaderProgramPSWebGPU*)desc.PS;
|
||||
|
||||
// Initialize description (without dynamic state from context such as render targets, vertex buffers, etc.)
|
||||
PipelineDesc = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
DebugDesc = desc;
|
||||
GetDebugName(_debugName);
|
||||
PipelineDesc.label = { _debugName.Get(), (size_t)_debugName.Count() - 1 };
|
||||
#endif
|
||||
@@ -257,17 +260,24 @@ bool GPUPipelineStateWebGPU::Init(const Description& desc)
|
||||
writeMask |= WGPUColorWriteMask_Blue;
|
||||
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
|
||||
writeMask |= WGPUColorWriteMask_Alpha;
|
||||
}
|
||||
for (auto& e : _colorTargets)
|
||||
} for (auto& e : _colorTargets)
|
||||
{
|
||||
e = WGPU_COLOR_TARGET_STATE_INIT;
|
||||
e.blend = &_blendState;
|
||||
if (desc.BlendMode.BlendEnable)
|
||||
e.blend = &_blendState;
|
||||
e.writeMask = writeMask;
|
||||
}
|
||||
|
||||
// Cache shaders
|
||||
VS = (GPUShaderProgramVSWebGPU*)desc.VS;
|
||||
PipelineDesc.vertex.module = VS->ShaderModule;
|
||||
PS = (GPUShaderProgramPSWebGPU*)desc.PS;
|
||||
if (PS)
|
||||
{
|
||||
_fragmentDesc.module = PS->ShaderModule;
|
||||
}
|
||||
|
||||
// TODO: set resources binding into PipelineDesc.layout
|
||||
// TODO: set vertex shader into PipelineDesc.vertex
|
||||
// TODO: set pixel shader into PipelineDesc.fragment
|
||||
|
||||
_memoryUsage = 1;
|
||||
return GPUPipelineState::Init(desc);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "GPUShaderProgramWebGPU.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
|
||||
#include "Engine/GraphicsDevice/Vulkan/Types.h"
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
/// <summary>
|
||||
/// Shaders base class for Web GPU backend.
|
||||
/// </summary>
|
||||
@@ -15,15 +15,22 @@ template<typename BaseType>
|
||||
class GPUShaderProgramWebGPU : public BaseType
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramWebGPU(const GPUShaderProgramInitializer& initializer)
|
||||
GPUShaderProgramWebGPU(const GPUShaderProgramInitializer& initializer, const SpirvShaderDescriptorInfo& descriptorInfo, WGPUShaderModule shaderModule)
|
||||
: DescriptorInfo(descriptorInfo)
|
||||
, ShaderModule(shaderModule)
|
||||
{
|
||||
BaseType::Init(initializer);
|
||||
}
|
||||
|
||||
~GPUShaderProgramWebGPU()
|
||||
{
|
||||
wgpuShaderModuleRelease(ShaderModule);
|
||||
}
|
||||
|
||||
public:
|
||||
SpirvShaderDescriptorInfo DescriptorInfo;
|
||||
WGPUShaderModule ShaderModule;
|
||||
|
||||
public:
|
||||
// [BaseType]
|
||||
uint32 GetBufferSize() const override
|
||||
@@ -32,7 +39,7 @@ public:
|
||||
}
|
||||
void* GetBufferHandle() const override
|
||||
{
|
||||
return nullptr;
|
||||
return ShaderModule;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,8 +49,8 @@ public:
|
||||
class GPUShaderProgramVSWebGPU : public GPUShaderProgramWebGPU<GPUShaderProgramVS>
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramVSWebGPU(const GPUShaderProgramInitializer& initializer, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout, Span<byte> bytecode)
|
||||
: GPUShaderProgramWebGPU(initializer)
|
||||
GPUShaderProgramVSWebGPU(const GPUShaderProgramInitializer& initializer, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout, const SpirvShaderDescriptorInfo& descriptorInfo, WGPUShaderModule shaderModule)
|
||||
: GPUShaderProgramWebGPU(initializer, descriptorInfo, shaderModule)
|
||||
{
|
||||
InputLayout = inputLayout;
|
||||
Layout = vertexLayout;
|
||||
@@ -56,8 +63,8 @@ public:
|
||||
class GPUShaderProgramPSWebGPU : public GPUShaderProgramWebGPU<GPUShaderProgramPS>
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramPSWebGPU(const GPUShaderProgramInitializer& initializer)
|
||||
: GPUShaderProgramWebGPU(initializer)
|
||||
GPUShaderProgramPSWebGPU(const GPUShaderProgramInitializer& initializer, const SpirvShaderDescriptorInfo& descriptorInfo, WGPUShaderModule shaderModule)
|
||||
: GPUShaderProgramWebGPU(initializer, descriptorInfo, shaderModule)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "GPUShaderWebGPU.h"
|
||||
#include "GPUShaderProgramWebGPU.h"
|
||||
#include "GPUVertexLayoutWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/GraphicsDevice/Vulkan/Types.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
|
||||
GPUConstantBufferWebGPU::GPUConstantBufferWebGPU(GPUDeviceWebGPU* device, uint32 size, WGPUBuffer buffer, const StringView& name) noexcept
|
||||
@@ -31,6 +34,34 @@ void GPUConstantBufferWebGPU::OnReleaseGPU()
|
||||
|
||||
GPUShaderProgram* GPUShaderWebGPU::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, Span<byte> bytecode, MemoryReadStream& stream)
|
||||
{
|
||||
// Extract the SPIR-V shader header from the cache
|
||||
SpirvShaderHeader* header = (SpirvShaderHeader*)bytecode.Get();
|
||||
bytecode = bytecode.Slice(sizeof(SpirvShaderHeader));
|
||||
|
||||
// Extract the WGSL shader
|
||||
BytesContainer wgsl;
|
||||
ASSERT(header->Type == SpirvShaderHeader::Types::WGSL);
|
||||
wgsl.Link(bytecode);
|
||||
|
||||
// Create a shader module
|
||||
WGPUShaderSourceWGSL shaderCodeDesc = WGPU_SHADER_SOURCE_WGSL_INIT;
|
||||
shaderCodeDesc.code = { (const char*)wgsl.Get(), (size_t)wgsl.Length() - 1 };
|
||||
WGPUShaderModuleDescriptor shaderDesc = WGPU_SHADER_MODULE_DESCRIPTOR_INIT;
|
||||
shaderDesc.nextInChain = &shaderCodeDesc.chain;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
shaderDesc.label = { initializer.Name.Get(), (size_t)initializer.Name.Length() };
|
||||
#endif
|
||||
WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(_device->Device, &shaderDesc);
|
||||
if (!shaderModule)
|
||||
{
|
||||
LOG(Error, "Failed to create a shader module");
|
||||
#if GPU_ENABLE_DIAGNOSTICS
|
||||
LOG_STR(Warning, String((char*)wgsl.Get(), wgsl.Length()));
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a shader program
|
||||
GPUShaderProgram* shader = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
@@ -38,14 +69,12 @@ GPUShaderProgram* GPUShaderWebGPU::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
{
|
||||
GPUVertexLayout* inputLayout, *vertexLayout;
|
||||
ReadVertexLayout(stream, inputLayout, vertexLayout);
|
||||
MISSING_CODE("create vertex shader");
|
||||
shader = New<GPUShaderProgramVSWebGPU>(initializer, inputLayout, vertexLayout, bytecode);
|
||||
shader = New<GPUShaderProgramVSWebGPU>(initializer, inputLayout, vertexLayout, header->DescriptorInfo, shaderModule);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Pixel:
|
||||
{
|
||||
MISSING_CODE("create pixel shader");
|
||||
shader = New<GPUShaderProgramPSWebGPU>(initializer);
|
||||
shader = New<GPUShaderProgramPSWebGPU>(initializer, header->DescriptorInfo, shaderModule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ public class ShadersCompilation : EngineModule
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (ShaderCompilerWebGPU.Use(options))
|
||||
options.PrivateDependencies.Add("ShaderCompilerWebGPU");
|
||||
if (Sdk.HasValid("PS4Sdk"))
|
||||
options.PrivateDependencies.Add("ShaderCompilerPS4");
|
||||
if (Sdk.HasValid("PS5Sdk"))
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
#include "Vulkan/ShaderCompilerVulkan.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_WEBGPU_SHADER_COMPILER
|
||||
#include "WebGPU/ShaderCompilerWebGPU.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_PS4_SHADER_COMPILER
|
||||
#include "Platforms/PS4/Engine/ShaderCompilerPS4/ShaderCompilerPS4.h"
|
||||
#endif
|
||||
@@ -159,7 +162,7 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options)
|
||||
return true;
|
||||
}
|
||||
const int32 shadersCount = meta.GetShadersCount();
|
||||
if (shadersCount == 0)
|
||||
if (shadersCount == 0 && featureLevel > FeatureLevel::ES2)
|
||||
{
|
||||
LOG(Warning, "Shader has no valid functions.");
|
||||
}
|
||||
@@ -255,6 +258,11 @@ ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile, Platf
|
||||
compiler = New<ShaderCompilerVulkan>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_WEBGPU_SHADER_COMPILER
|
||||
case ShaderProfile::WebGPU:
|
||||
compiler = New<ShaderCompilerWebGPU>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_PS4_SHADER_COMPILER
|
||||
case ShaderProfile::PS4:
|
||||
compiler = New<ShaderCompilerPS4>();
|
||||
|
||||
@@ -96,7 +96,7 @@ ShaderCompilerVulkan::~ShaderCompilerVulkan()
|
||||
// @formatter:off
|
||||
const TBuiltInResource DefaultTBuiltInResource =
|
||||
{
|
||||
/* .MaxLights = */ 32,
|
||||
/* .MaxLights = */ 0,
|
||||
/* .MaxClipPlanes = */ 6,
|
||||
/* .MaxTextureUnits = */ 32,
|
||||
/* .MaxTextureCoords = */ 32,
|
||||
@@ -589,7 +589,6 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
||||
// Prepare
|
||||
if (WriteShaderFunctionBegin(_context, meta))
|
||||
return true;
|
||||
auto options = _context->Options;
|
||||
auto type = meta.GetStage();
|
||||
|
||||
// Prepare
|
||||
@@ -669,9 +668,9 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
||||
glslang::TProgram program;
|
||||
shader.setEntryPoint(meta.Name.Get());
|
||||
shader.setSourceEntryPoint(meta.Name.Get());
|
||||
int lengths = options->SourceLength - 1;
|
||||
int lengths = _context->Options->SourceLength - 1;
|
||||
const char* names = _context->TargetNameAnsi;
|
||||
shader.setStringsWithLengthsAndNames(&options->Source, &lengths, &names, 1);
|
||||
shader.setStringsWithLengthsAndNames(&_context->Options->Source, &lengths, &names, 1);
|
||||
const int defaultVersion = 450;
|
||||
std::string preamble;
|
||||
for (int32 i = 0; i < _macros.Count() - 1; i++)
|
||||
@@ -687,14 +686,8 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
||||
preamble.append("\n");
|
||||
}
|
||||
shader.setPreamble(preamble.c_str());
|
||||
shader.setInvertY(true);
|
||||
//shader.setAutoMapLocations(true);
|
||||
//shader.setAutoMapBindings(true);
|
||||
//shader.setShiftBinding(glslang::TResourceType::EResUav, 500);
|
||||
shader.setHlslIoMapping(true);
|
||||
shader.setEnvInput(glslang::EShSourceHlsl, lang, glslang::EShClientVulkan, defaultVersion);
|
||||
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
|
||||
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
|
||||
InitParsing(_context, shader);
|
||||
if (!shader.parse(&DefaultTBuiltInResource, defaultVersion, false, messages, includer))
|
||||
{
|
||||
const auto msg = shader.getInfoLog();
|
||||
@@ -899,16 +892,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
||||
std::vector<unsigned> spirv;
|
||||
spv::SpvBuildLogger logger;
|
||||
glslang::SpvOptions spvOptions;
|
||||
spvOptions.generateDebugInfo = false;
|
||||
spvOptions.disassemble = false;
|
||||
spvOptions.disableOptimizer = options->NoOptimize;
|
||||
spvOptions.optimizeSize = !options->NoOptimize;
|
||||
spvOptions.stripDebugInfo = !options->GenerateDebugData;
|
||||
#if BUILD_DEBUG
|
||||
spvOptions.validate = true;
|
||||
#else
|
||||
spvOptions.validate = false;
|
||||
#endif
|
||||
InitCodegen(_context, spvOptions);
|
||||
glslang::GlslangToSpv(*program.getIntermediate(lang), spirv, &logger, &spvOptions);
|
||||
const std::string spirvLogOutput = logger.getAllMessages();
|
||||
if (!spirvLogOutput.empty())
|
||||
@@ -931,10 +915,8 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
||||
}
|
||||
#endif
|
||||
|
||||
int32 spirvBytesCount = (int32)spirv.size() * sizeof(unsigned);
|
||||
header.Type = SpirvShaderHeader::Types::Raw;
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), &spirv[0], spirvBytesCount))
|
||||
if (Write(_context, meta, permutationIndex, bindings, header, spirv))
|
||||
return true;
|
||||
|
||||
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros, additionalData))
|
||||
@@ -956,4 +938,32 @@ bool ShaderCompilerVulkan::OnCompileBegin()
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderCompilerVulkan::InitParsing(ShaderCompilationContext* context, glslang::TShader& shader)
|
||||
{
|
||||
shader.setInvertY(true);
|
||||
//shader.setAutoMapLocations(true);
|
||||
//shader.setAutoMapBindings(true);
|
||||
//shader.setShiftBinding(glslang::TResourceType::EResUav, 500);
|
||||
shader.setHlslIoMapping(true);
|
||||
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
|
||||
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
|
||||
}
|
||||
|
||||
void ShaderCompilerVulkan::InitCodegen(ShaderCompilationContext* context, glslang::SpvOptions& spvOptions)
|
||||
{
|
||||
spvOptions.generateDebugInfo = false;
|
||||
spvOptions.disassemble = false;
|
||||
spvOptions.disableOptimizer = context->Options->NoOptimize;
|
||||
spvOptions.optimizeSize = !context->Options->NoOptimize;
|
||||
spvOptions.stripDebugInfo = !context->Options->GenerateDebugData;
|
||||
spvOptions.validate = BUILD_DEBUG;
|
||||
}
|
||||
|
||||
bool ShaderCompilerVulkan::Write(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, struct SpirvShaderHeader& header, std::vector<unsigned int>& spirv)
|
||||
{
|
||||
int32 spirvBytesCount = (int32)spirv.size() * sizeof(unsigned);
|
||||
header.Type = SpirvShaderHeader::Types::SPIRV;
|
||||
return WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), &spirv[0], spirvBytesCount);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/ShaderCompiler.h"
|
||||
#include <vector>
|
||||
|
||||
namespace glslang
|
||||
{
|
||||
class TShader;
|
||||
struct SpvOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for Vulkan rendering backend.
|
||||
@@ -30,6 +37,11 @@ protected:
|
||||
// [ShaderCompiler]
|
||||
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
|
||||
bool OnCompileBegin() override;
|
||||
|
||||
protected:
|
||||
virtual void InitParsing(ShaderCompilationContext* context, glslang::TShader& shader);
|
||||
virtual void InitCodegen(ShaderCompilationContext* context, glslang::SpvOptions& spvOptions);
|
||||
virtual bool Write(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, struct SpirvShaderHeader& header, std::vector<unsigned>& spirv);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
BIN
Source/Platforms/Linux/Binaries/ThirdParty/x64/tint
vendored
Normal file
BIN
Source/Platforms/Linux/Binaries/ThirdParty/x64/tint
vendored
Normal file
Binary file not shown.
BIN
Source/Platforms/Mac/Binaries/ThirdParty/ARM64/tint
vendored
Normal file
BIN
Source/Platforms/Mac/Binaries/ThirdParty/ARM64/tint
vendored
Normal file
Binary file not shown.
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe
LFS
vendored
Normal file
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/tint.exe
LFS
vendored
Normal file
Binary file not shown.
26
Source/ThirdParty/tint-license.txt
vendored
Normal file
26
Source/ThirdParty/tint-license.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2023 The Dawn & Tint Authors
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Reference in New Issue
Block a user