Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial tint transpilation support for WebGPU #8477

Merged
merged 3 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions libs/filamat/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v)

if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(${TARGET} libtint)
endif ()

# We are being naughty and accessing private headers here
# For spirv-tools, we're just following glslang's example
target_include_directories(${TARGET} PRIVATE ${spirv-tools_SOURCE_DIR}/include)
Expand Down
67 changes: 64 additions & 3 deletions libs/filamat/src/GLSLPostProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
#include <unordered_map>
#include <vector>

#ifdef FILAMENT_SUPPORTS_WEBGPU
#include <tint/tint.h>
#endif

using namespace glslang;
using namespace spirv_cross;
using namespace spvtools;
Expand Down Expand Up @@ -514,8 +518,43 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
}
}

bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl) {
#if FILAMENT_SUPPORTS_WEBGPU
//Currently no options we want to use
const tint::spirv::reader::Options readerOpts{};
tint::wgsl::writer::Options writerOpts{};

tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);

if (tintRead.Diagnostics().ContainsErrors()) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup:" << tintRead.Diagnostics().Str() << io::endl;

//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
}

tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
/// An instance of SuccessType that can be used to check a tint Result.
tint::SuccessType tintSuccess;

if (wgslOut != tintSuccess) {
slog.w << "This tint writer error is currently ignored during WebGPU bringup:" << wgslOut.Failure().reason.Str() << io::endl;
//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
}
*outWsl = wgslOut->wgsl;
return true;
#else
slog.i << "Trying to emit WGSL without including WebGPU dependencies, please set CMake arg FILAMENT_SUPPORTS_WEBGPU and FILAMENT_SUPPORTS_WEBGPU" << io::endl;
return false;
#endif

}

bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config,
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl) {
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl, std::string* outputWgsl) {
using TargetLanguage = MaterialBuilder::TargetLanguage;

if (config.targetLanguage == TargetLanguage::GLSL) {
Expand All @@ -530,6 +569,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
.glslOutput = outputGlsl,
.spirvOutput = outputSpirv,
.mslOutput = outputMsl,
.wgslOutput = outputWgsl,
};

switch (config.shaderType) {
Expand Down Expand Up @@ -610,13 +650,20 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
config.shaderType, config.shaderModel, config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
return false;
}
}
} else {
slog.e << "GLSL post-processor invoked with optimization level NONE"
<< io::endl;
}
break;
case MaterialBuilder::Optimization::PREPROCESSOR:
preprocessOptimization(tShader, config, internalConfig);
if (!preprocessOptimization(tShader, config, internalConfig)) {
return false;
}
break;
case MaterialBuilder::Optimization::SIZE:
case MaterialBuilder::Optimization::PERFORMANCE:
Expand Down Expand Up @@ -646,7 +693,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
return true;
}

void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
using TargetApi = MaterialBuilder::TargetApi;
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
Expand All @@ -662,6 +709,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,

if (!ok) {
slog.e << tShader.getInfoLog() << io::endl;
return false;
}

if (internalConfig.spirvOutput) {
Expand All @@ -683,6 +731,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
bool const linkOk = program.link(msg);
if (!ok || !linkOk) {
slog.e << spirvShader.getInfoLog() << io::endl;
return false;
} else {
SpvOptions options;
options.generateDebugInfo = mGenerateDebugInfo;
Expand All @@ -702,10 +751,17 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
config.shaderModel, config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
return false;
}
}


if (internalConfig.glslOutput) {
*internalConfig.glslOutput = glsl;
}
return true;
}

bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
Expand Down Expand Up @@ -746,6 +802,11 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(&spirv, internalConfig.wgslOutput)) {
return false;
}
}

// Transpile back to GLSL
if (internalConfig.glslOutput) {
Expand Down
8 changes: 6 additions & 2 deletions libs/filamat/src/GLSLPostProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,23 @@ class GLSLPostProcessor {
bool process(const std::string& inputShader, Config const& config,
std::string* outputGlsl,
SpirvBlob* outputSpirv,
std::string* outputMsl);
std::string* outputMsl,
std::string* outputWgsl);

// public so backend_test can also use it
static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
filament::backend::ShaderStage stage, filament::backend::ShaderModel shaderModel,
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
const ShaderMinifier* minifier);

static bool spirvToWgsl(const SpirvBlob* spirv, std::string* outWsl);

private:
struct InternalConfig {
std::string* glslOutput = nullptr;
SpirvBlob* spirvOutput = nullptr;
std::string* mslOutput = nullptr;
std::string* wgslOutput = nullptr;
EShLanguage shLang = EShLangFragment;
// use 100 for ES environment, 110 for desktop
int langVersion = 0;
Expand All @@ -103,7 +107,7 @@ class GLSLPostProcessor {
bool fullOptimization(const glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;

void preprocessOptimization(glslang::TShader& tShader,
bool preprocessOptimization(glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;

/**
Expand Down
5 changes: 4 additions & 1 deletion libs/filamat/src/MaterialBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
const bool targetApiNeedsWgsl = targetApi == TargetApi::WEBGPU;
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;

// Set when a job fails
Expand All @@ -923,9 +924,11 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
// TODO: avoid allocations when not required
std::vector<uint32_t> spirv;
std::string msl;
std::string wgsl;

std::vector<uint32_t>* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr;
std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr;
std::string* pWgsl = targetApiNeedsWgsl ? &wgsl : nullptr;

TextEntry glslEntry{};
BinaryEntry spirvEntry{};
Expand Down Expand Up @@ -1006,7 +1009,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
config.glsl.subpassInputToColorLocation.emplace_back(0, 0);
}

bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl);
bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl, pWgsl);
if (!ok) {
showErrorMessage(mMaterialName.c_str_safe(), v.variant, targetApi, v.stage,
featureLevel, shader);
Expand Down
4 changes: 3 additions & 1 deletion third_party/dawn/tnt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ set(DAWN_ABSEIL_DIR ${EXTERNAL}/abseil)
set(DAWN_SPIRV_TOOLS_DIR ${EXTERNAL}/spirv-tools/)
set(DAWN_SPIRV_HEADERS_DIR ${EXTERNAL}/spirv-headers)
set(DAWN_GLSLANG_DIR ${EXTERNAL}/glslang/)

if(IS_HOST_PLATFORM)
set(TINT_BUILD_SPV_READER ON)
endif ()
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
Loading