diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 003dd507e6..f55e8b3b19 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -8,6 +8,9 @@ A new header is inserted each time a *tag* is created. - Added `sheenColor` and `sheenRoughness` properties to materials to create cloth/fabric - gltfio: added support for `KHR_materials_sheen` - gltfio: shader optimizations are now disabled by default, unless opting in or using ubershaders +- `MaterialBuilder::build()` now expects a reference to a `JobSystem` to multi-thread shaders + generation. A `JobSystem` can be obtained with `Engine::getJobSystem()` when using Filament, + or created directly otherwise (⚠️ **API change**). ## v1.9.10 diff --git a/android/filamat-android/CMakeLists.txt b/android/filamat-android/CMakeLists.txt index cb863fe9c2..3e90a412a8 100644 --- a/android/filamat-android/CMakeLists.txt +++ b/android/filamat-android/CMakeLists.txt @@ -33,13 +33,16 @@ add_library(shaders STATIC IMPORTED) set_target_properties(shaders PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libshaders.a) +set(FILAMAT_INCLUDE_DIRS + ../../libs/utils/include + ) + include_directories(${FILAMENT_DIR}/include) set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_SOURCE_DIR}/libfilamat-jni.map") -add_library(filamat-jni SHARED - src/main/cpp/MaterialBuilder.cpp -) +add_library(filamat-jni SHARED src/main/cpp/MaterialBuilder.cpp) +target_include_directories(filamat-jni PRIVATE ${FILAMAT_INCLUDE_DIRS}) target_link_libraries(filamat-jni ${FILAMAT_FLAVOR} diff --git a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp index 43fb3bec48..2634ce2e38 100644 --- a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp +++ b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp @@ -18,6 +18,8 @@ #include +#include + using namespace filament; using namespace filamat; @@ -45,9 +47,23 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nDestroyMaterialBuilder extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_filamat_MaterialBuilder_nBuilderBuild(JNIEnv*, jclass, - jlong nativeBuilder) { + jlong nativeBuilder, jlong nativeJobSystem) { auto builder = (MaterialBuilder*) nativeBuilder; - return (jlong) new Package(builder->build()); + auto jobSystem = (utils::JobSystem*) nativeJobSystem; + + if (nativeJobSystem == 0) { + jobSystem = new utils::JobSystem; + jobSystem->adopt(); + } + + jlong result = (jlong) new Package(builder->build(*jobSystem)); + + if (nativeJobSystem == 0) { + jobSystem->emancipate(); + delete jobSystem; + } + + return result; } extern "C" JNIEXPORT jbyteArray JNICALL diff --git a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java index cecaf3a8ae..7ce1f33284 100644 --- a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java +++ b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java @@ -17,17 +17,29 @@ package com.google.android.filament.filamat; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; public class MaterialBuilder { - @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources private final BuilderFinalizer mFinalizer; private final long mNativeObject; + private static Class sEngineClass = null; + private static Method sGetNativeJobSystemMethod = null; + static { System.loadLibrary("filamat-jni"); + try { + sEngineClass = Class.forName("com.google.android.filament.Engine"); + sGetNativeJobSystemMethod = sEngineClass.getDeclaredMethod("getNativeJobSystem"); + } catch (ClassNotFoundException | NoSuchMethodException e) { + // It's okay if we don't find it, this is to avoid creating dependencies + } } public enum Shading { @@ -421,9 +433,55 @@ public class MaterialBuilder { return this; } + /** + * Validates, builds, and returns the compiled material. While this method never + * returns null, the returned {@link MaterialPackage} may be invalid. Call + * {@link MaterialPackage#isValid()} before using it. + * + * Calling this method is equivalent to calling {@link #build(Object)} and passing + * null as the job system provider. + * + * @see #build(Object) + */ @NonNull public MaterialPackage build() { - long nativePackage = nBuilderBuild(mNativeObject); + return build(null); + } + + /** + * Validates, builds, and returns the compiled material. While this method never + * returns null, the returned {@link MaterialPackage} may be invalid. Call + * {@link MaterialPackage#isValid()} before using it. + * + * You can pass a job system provider to this method, or null. When passing null + * or an invalid job system provider, a temporary job system will be created which + * is less efficient than reusing an existing job system. + * + * Currently the only valid type of job system provider is an Engine + * instance from the main Filament library (com.google.android.filament.Engine). + * + * If you are using Filament and the filamat library together you must pass an + * Engine as the job system provider, or invoke + * MaterialBuilder from a thread other than the thread used to invoke Filament + * APIs. + * + * @param jobSystemProvider An Engine instance or null + */ + @NonNull + public MaterialPackage build(@Nullable Object jobSystemProvider) { + long nativeJobSystem = 0; + if (jobSystemProvider != null && sEngineClass != null) { + if (sEngineClass.isInstance(jobSystemProvider) && sGetNativeJobSystemMethod != null) { + try { + //noinspection ConstantConditions + nativeJobSystem = (Long) sGetNativeJobSystemMethod.invoke(jobSystemProvider); + } catch (IllegalAccessException | InvocationTargetException e) { + // Ignore + } + } + } + + long nativePackage = nBuilderBuild(mNativeObject, nativeJobSystem); byte[] data = nGetPackageBytes(nativePackage); MaterialPackage result = new MaterialPackage(ByteBuffer.wrap(data), nGetPackageIsValid(nativePackage)); @@ -455,7 +513,7 @@ public class MaterialBuilder { private static native long nCreateMaterialBuilder(); private static native void nDestroyMaterialBuilder(long nativeBuilder); - private static native long nBuilderBuild(long nativeBuilder); + private static native long nBuilderBuild(long nativeBuilder, long nativeJobSystem); private static native byte[] nGetPackageBytes(long nativePackage); private static native boolean nGetPackageIsValid(long nativePackage); private static native void nDestroyPackage(long nativePackage); diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index be61470e5f..a49b78e9fe 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -306,3 +306,9 @@ Java_com_google_android_filament_Engine_nGetRenderableManager(JNIEnv*, jclass, Engine* engine = (Engine*) nativeEngine; return (jlong) &engine->getRenderableManager(); } + +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_Engine_nGetJobSystem(JNIEnv*, jclass, jlong nativeEngine) { + Engine* engine = (Engine*) nativeEngine; + return (jlong) &engine->getJobSystem(); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 799dd8b653..16476131b7 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -654,13 +654,21 @@ public class Engine { return mNativeObject; } + @UsedByReflection("MaterialBuilder.java") + public long getNativeJobSystem() { + if (mNativeObject == 0) { + throw new IllegalStateException("Calling method on destroyed Engine"); + } + return nGetJobSystem(getNativeObject()); + } + private void clearNativeObject() { mNativeObject = 0; } private static void assertDestroy(boolean success) { if (!success) { - throw new IllegalStateException("Object couldn't be destoyed (double destroy()?)"); + throw new IllegalStateException("Object couldn't be destroyed (double destroy()?)"); } } @@ -698,4 +706,5 @@ public class Engine { private static native long nGetTransformManager(long nativeEngine); private static native long nGetLightManager(long nativeEngine); private static native long nGetRenderableManager(long nativeEngine); + private static native long nGetJobSystem(long nativeEngine); } diff --git a/android/samples/sample-material-builder/src/main/java/com/google/android/filament/material_builder/MainActivity.kt b/android/samples/sample-material-builder/src/main/java/com/google/android/filament/material_builder/MainActivity.kt index aeaed65353..eec7f820b0 100644 --- a/android/samples/sample-material-builder/src/main/java/com/google/android/filament/material_builder/MainActivity.kt +++ b/android/samples/sample-material-builder/src/main/java/com/google/android/filament/material_builder/MainActivity.kt @@ -216,7 +216,9 @@ class MainActivity : Activity() { // variant of the filamat library. .optimization(MaterialBuilder.Optimization.NONE) - .build() + // When compiling more than one material variant, it is more efficient to pass an Engine + // instance to reuse the Engine's job system + .build(engine) if (matPackage.isValid) { val buffer = matPackage.buffer diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 536291b443..6eb24ad0aa 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -38,6 +38,10 @@ #include #include +namespace utils { +class JobSystem; +} + namespace filamat { struct MaterialInfo; @@ -474,9 +478,11 @@ public: MaterialBuilder& enableFramebufferFetch() noexcept; - - //! Build the material. - Package build() noexcept; + /** + * Build the material. If you are using the Filament engine with this library, you should use + * the job system provided by Engine. + */ + Package build(utils::JobSystem& jobSystem) noexcept; public: // The methods and types below are for internal use @@ -595,7 +601,9 @@ private: void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept; void writeSurfaceChunks(ChunkContainer& container) const noexcept; - bool generateShaders(const std::vector& variants, ChunkContainer& container, + bool generateShaders( + utils::JobSystem& jobSystem, + const std::vector& variants, ChunkContainer& container, const MaterialInfo& info) const noexcept; bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; } diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 66b6ef17ee..07ec121a2c 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -106,8 +106,9 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const return oss.str(); } -void GLSLPostProcessor::spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl, - const GLSLPostProcessor::Config& config) const { +void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl, + const Config &config, ShaderMinifier& minifier) const { + CompilerMSL mslCompiler(*spirv); CompilerGLSL::Options options; mslCompiler.set_common_options(options); @@ -150,7 +151,7 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl } *outMsl = mslCompiler.compile(); - *outMsl = mShaderMinifier.removeWhitespace(*outMsl); + *outMsl = minifier.removeWhitespace(*outMsl); } bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config, @@ -167,18 +168,20 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co return true; } - mGlslOutput = outputGlsl; - mSpirvOutput = outputSpirv; - mMslOutput = outputMsl; + InternalConfig internalConfig; + + internalConfig.glslOutput = outputGlsl; + internalConfig.spirvOutput = outputSpirv; + internalConfig.mslOutput = outputMsl; if (config.shaderType == filament::backend::VERTEX) { - mShLang = EShLangVertex; + internalConfig.shLang = EShLangVertex; } else { - mShLang = EShLangFragment; + internalConfig.shLang = EShLangFragment; } TProgram program; - TShader tShader(mShLang); + TShader tShader(internalConfig.shLang); // The cleaner must be declared after the TShader to prevent ASAN failures. GLSLangCleaner cleaner; @@ -186,10 +189,10 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co const char* shaderCString = inputShader.c_str(); tShader.setStrings(&shaderCString, 1); - mLangVersion = GLSLTools::glslangVersionFromShaderModel(config.shaderModel); - GLSLTools::prepareShaderParser(tShader, mShLang, mLangVersion, mOptimization); + internalConfig.langVersion = GLSLTools::glslangVersionFromShaderModel(config.shaderModel); + GLSLTools::prepareShaderParser(tShader, internalConfig.shLang, internalConfig.langVersion, mOptimization); EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi); - bool ok = tShader.parse(&DefaultTBuiltInResource, mLangVersion, false, msg); + bool ok = tShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg); if (!ok) { utils::slog.e << tShader.getInfoLog() << utils::io::endl; return false; @@ -206,12 +209,14 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co switch (mOptimization) { case MaterialBuilder::Optimization::NONE: - if (mSpirvOutput) { + if (internalConfig.spirvOutput) { SpvOptions options; options.generateDebugInfo = mGenerateDebugInfo; - GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput, &options); - if (mMslOutput) { - spirvToToMsl(mSpirvOutput, mMslOutput, config); + GlslangToSpv(*program.getIntermediate(internalConfig.shLang), + *internalConfig.spirvOutput, &options); + if (internalConfig.mslOutput) { + spirvToToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config, + internalConfig.minifier); } } else { utils::slog.e << "GLSL post-processor invoked with optimization level NONE" @@ -219,38 +224,41 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co } break; case MaterialBuilder::Optimization::PREPROCESSOR: - preprocessOptimization(tShader, config); + preprocessOptimization(tShader, config, internalConfig); break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - fullOptimization(tShader, config); + fullOptimization(tShader, config, internalConfig); break; } - if (mGlslOutput) { - *mGlslOutput = mShaderMinifier.removeWhitespace(*mGlslOutput); + if (internalConfig.glslOutput) { + *internalConfig.glslOutput = + internalConfig.minifier.removeWhitespace(*internalConfig.glslOutput); // In theory this should only be enabled for SIZE, but in practice we often use PERFORMANCE. if (mOptimization != MaterialBuilder::Optimization::NONE) { - *mGlslOutput = mShaderMinifier.renameStructFields(*mGlslOutput); + *internalConfig.glslOutput = + internalConfig.minifier.renameStructFields(*internalConfig.glslOutput); } if (mPrintShaders) { - utils::slog.i << *mGlslOutput << utils::io::endl; + utils::slog.i << *internalConfig.glslOutput << utils::io::endl; } } return true; } void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, - GLSLPostProcessor::Config const& config) const { + GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const { + using TargetApi = MaterialBuilder::TargetApi; std::string glsl; TShader::ForbidIncluder forbidIncluder; int version = GLSLTools::glslangVersionFromShaderModel(config.shaderModel); - const TargetApi targetApi = mSpirvOutput ? TargetApi::VULKAN : TargetApi::OPENGL; + const TargetApi targetApi = internalConfig.spirvOutput ? TargetApi::VULKAN : TargetApi::OPENGL; EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi); bool ok = tShader.preprocess(&DefaultTBuiltInResource, version, ENoProfile, false, false, msg, &glsl, forbidIncluder); @@ -259,9 +267,9 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, utils::slog.e << tShader.getInfoLog() << utils::io::endl; } - if (mSpirvOutput) { + if (internalConfig.spirvOutput) { TProgram program; - TShader spirvShader(mShLang); + TShader spirvShader(internalConfig.shLang); // The cleaner must be declared after the TShader/TProgram which are setting the current // pool in the tls @@ -269,8 +277,9 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, const char* shaderCString = glsl.c_str(); spirvShader.setStrings(&shaderCString, 1); - GLSLTools::prepareShaderParser(spirvShader, mShLang, mLangVersion, mOptimization); - ok = spirvShader.parse(&DefaultTBuiltInResource, mLangVersion, false, msg); + GLSLTools::prepareShaderParser(spirvShader, + internalConfig.shLang, internalConfig.langVersion, mOptimization); + ok = spirvShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg); program.addShader(&spirvShader); // Even though we only have a single shader stage, linking is still necessary to finalize // SPIR-V types @@ -280,21 +289,23 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, } else { SpvOptions options; options.generateDebugInfo = mGenerateDebugInfo; - GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput, &options); + GlslangToSpv(*program.getIntermediate(internalConfig.shLang), + *internalConfig.spirvOutput, &options); } } - if (mMslOutput) { - spirvToToMsl(mSpirvOutput, mMslOutput, config); + if (internalConfig.mslOutput) { + spirvToToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config, + internalConfig.minifier); } - if (mGlslOutput) { - *mGlslOutput = glsl; + if (internalConfig.glslOutput) { + *internalConfig.glslOutput = glsl; } } void GLSLPostProcessor::fullOptimization(const TShader& tShader, - GLSLPostProcessor::Config const& config) const { + GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const { SpirvBlob spirv; // Compile GLSL to to SPIR-V @@ -306,16 +317,16 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, OptimizerPtr optimizer = createOptimizer(mOptimization, config); optimizeSpirv(optimizer, spirv); - if (mSpirvOutput) { - *mSpirvOutput = spirv; + if (internalConfig.spirvOutput) { + *internalConfig.spirvOutput = spirv; } - if (mMslOutput) { - spirvToToMsl(&spirv, mMslOutput, config); + if (internalConfig.mslOutput) { + spirvToToMsl(&spirv, internalConfig.mslOutput, config, internalConfig.minifier); } // Transpile back to GLSL - if (mGlslOutput) { + if (internalConfig.glslOutput) { CompilerGLSL::Options glslOptions; glslOptions.es = config.shaderModel == filament::backend::ShaderModel::GL_ES_30; glslOptions.version = shaderVersionFromModel(config.shaderModel); @@ -334,7 +345,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, } } - *mGlslOutput = glslCompiler.compile(); + *internalConfig.glslOutput = glslCompiler.compile(); } } diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 962a138669..78bc529a3b 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -38,7 +38,6 @@ using SpirvBlob = std::vector; class GLSLPostProcessor { public: - enum Flags : uint32_t { PRINT_SHADERS = 1 << 0, GENERATE_DEBUG_INFO = 1 << 1, @@ -62,11 +61,19 @@ public: std::string* outputMsl); private: + struct InternalConfig { + std::string* glslOutput = nullptr; + SpirvBlob* spirvOutput = nullptr; + std::string* mslOutput = nullptr; + EShLanguage shLang = EShLangFragment; + int langVersion = 0; + ShaderMinifier minifier; + }; void fullOptimization(const glslang::TShader& tShader, - GLSLPostProcessor::Config const& config) const; + GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const; void preprocessOptimization(glslang::TShader& tShader, - GLSLPostProcessor::Config const& config) const; + GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const; /** * Retrieve an optimizer instance tuned for the given optimization level and shader configuration. @@ -80,18 +87,12 @@ private: static void registerPerformancePasses(spvtools::Optimizer& optimizer, Config const& config); void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const; - void spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl, - const GLSLPostProcessor::Config& config) const; + void spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl, const Config &config, + ShaderMinifier& minifier) const; const MaterialBuilder::Optimization mOptimization; const bool mPrintShaders; const bool mGenerateDebugInfo; - std::string* mGlslOutput = nullptr; - SpirvBlob* mSpirvOutput = nullptr; - std::string* mMslOutput = nullptr; - EShLanguage mShLang = EShLangFragment; - ShaderMinifier mShaderMinifier; - int mLangVersion = 0; }; } // namespace filamat diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index d7273e6f46..18379bc74e 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -16,10 +16,13 @@ #include "filamat/MaterialBuilder.h" +#include #include -#include +#include #include +#include +#include #include #include @@ -596,8 +599,8 @@ static void showErrorMessage(const char* materialName, uint8_t variant, << shaderCode; } -bool MaterialBuilder::generateShaders(const std::vector& variants, ChunkContainer& container, - const MaterialInfo& info) const noexcept { +bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector& variants, + ChunkContainer& container, const MaterialInfo& info) const noexcept { // Create a postprocessor to optimize / compile to Spir-V if necessary. #ifndef FILAMAT_LITE uint32_t flags = 0; @@ -606,21 +609,19 @@ bool MaterialBuilder::generateShaders(const std::vector& variants, Chun GLSLPostProcessor postProcessor(mOptimization, flags); #endif - // Generate all shaders. + // Start: must be protected by lock + Mutex entriesLock; std::vector glslEntries; std::vector spirvEntries; std::vector metalEntries; - - // Dictionary used to compress text-based shading languages (GLSL and MSL). LineDictionary textDictionary; - #ifndef FILAMAT_LITE BlobDictionary spirvDictionary; #endif - std::vector spirv; - std::string msl; + // End: must be protected by lock - ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mMaterialCode.getResolved(), + ShaderGenerator sg( + mProperties, mVariables, mOutputs, mDefines, mMaterialCode.getResolved(), mMaterialCode.getLineOffset(), mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(), mMaterialDomain); @@ -629,7 +630,14 @@ bool MaterialBuilder::generateShaders(const std::vector& variants, Chun mBlendingMode == BlendingMode::MASKED || !emptyVertexCode; container.addSimpleChild(ChunkType::MaterialHasCustomDepthShader, customDepth); + std::atomic_bool cancelJobs(false); + bool firstJob = true; + for (const auto& params : mCodeGenPermutations) { + if (cancelJobs.load()) { + return false; + } + const ShaderModel shaderModel = ShaderModel(params.shaderModel); const TargetApi targetApi = params.targetApi; const TargetLanguage targetLanguage = params.targetLanguage; @@ -641,101 +649,138 @@ bool MaterialBuilder::generateShaders(const std::vector& variants, Chun (targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL); const bool targetApiNeedsMsl = targetApi == TargetApi::METAL; const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL; - std::vector* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr; - std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr; - TextEntry glslEntry{0}; - SpirvEntry spirvEntry{0}; - TextEntry metalEntry{0}; - - glslEntry.shaderModel = static_cast(params.shaderModel); - spirvEntry.shaderModel = static_cast(params.shaderModel); - metalEntry.shaderModel = static_cast(params.shaderModel); + // Set when a job fails + JobSystem::Job* parent = jobSystem.createJob(); for (const auto& v : variants) { - glslEntry.variant = v.variant; - spirvEntry.variant = v.variant; - metalEntry.variant = v.variant; - - // Generate raw shader code. - // The quotes in Google-style line directives cause problems with certain drivers. These - // directives are optimized away when using the full filamat, so down below we - // explicitly remove them when using filamat lite. - std::string shader; - if (v.stage == filament::backend::ShaderType::VERTEX) { - shader = sg.createVertexProgram( - shaderModel, targetApi, targetLanguage, info, v.variant, - mInterpolation, mVertexDomain); -#ifdef FILAMAT_LITE - GLSLToolsLite glslTools; - glslTools.removeGoogleLineDirectives(shader); -#endif - } else if (v.stage == filament::backend::ShaderType::FRAGMENT) { - shader = sg.createFragmentProgram( - shaderModel, targetApi, targetLanguage, info, v.variant, mInterpolation); -#ifdef FILAMAT_LITE - GLSLToolsLite glslTools; - glslTools.removeGoogleLineDirectives(shader); -#endif - } - - std::string* pGlsl = nullptr; - if (targetApiNeedsGlsl) { - pGlsl = &shader; - } - -#ifndef FILAMAT_LITE - - GLSLPostProcessor::Config config{ - .shaderType = v.stage, - .shaderModel = shaderModel, - .glsl = {} - }; - - if (mEnableFramebufferFetch) { - config.glsl.subpassInputToColorLocation.emplace_back(0, 0); - } - - bool ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl); -#else - bool ok = true; -#endif - if (!ok) { - showErrorMessage(mMaterialName.c_str_safe(), v.variant, targetApi, v.stage, shader); - return false; - } - - if (targetApi == TargetApi::OPENGL) { - if (targetLanguage == TargetLanguage::SPIRV) { - sg.fixupExternalSamplers(shaderModel, shader, info); + JobSystem::Job* job = jobs::createJob(jobSystem, parent, [&]() { + if (cancelJobs.load()) { + return; } - glslEntry.stage = v.stage; - glslEntry.shader = shader; - textDictionary.addText(glslEntry.shader); - glslEntries.push_back(glslEntry); - } + // TODO: avoid allocations when not required + std::vector spirv; + std::string msl; + + std::vector* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr; + std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr; + + TextEntry glslEntry{0}; + SpirvEntry spirvEntry{0}; + TextEntry metalEntry{0}; + + glslEntry.shaderModel = static_cast(params.shaderModel); + spirvEntry.shaderModel = static_cast(params.shaderModel); + metalEntry.shaderModel = static_cast(params.shaderModel); + + glslEntry.variant = v.variant; + spirvEntry.variant = v.variant; + metalEntry.variant = v.variant; + + // Generate raw shader code. + // The quotes in Google-style line directives cause problems with certain drivers. These + // directives are optimized away when using the full filamat, so down below we + // explicitly remove them when using filamat lite. + std::string shader; + if (v.stage == filament::backend::ShaderType::VERTEX) { + shader = sg.createVertexProgram( + shaderModel, targetApi, targetLanguage, info, v.variant, + mInterpolation, mVertexDomain); +#ifdef FILAMAT_LITE + GLSLToolsLite glslTools; + glslTools.removeGoogleLineDirectives(shader); +#endif + } else if (v.stage == filament::backend::ShaderType::FRAGMENT) { + shader = sg.createFragmentProgram( + shaderModel, targetApi, targetLanguage, info, v.variant, mInterpolation); +#ifdef FILAMAT_LITE + GLSLToolsLite glslTools; + glslTools.removeGoogleLineDirectives(shader); +#endif + } + + std::string* pGlsl = nullptr; + if (targetApiNeedsGlsl) { + pGlsl = &shader; + } #ifndef FILAMAT_LITE - if (targetApi == TargetApi::VULKAN) { - assert(!spirv.empty()); - spirvEntry.stage = v.stage; - spirvEntry.dictionaryIndex = spirvDictionary.addBlob(spirv); - spirv.clear(); - spirvEntries.push_back(spirvEntry); - } - if (targetApi == TargetApi::METAL) { - assert(!spirv.empty()); - assert(msl.length() > 0); - metalEntry.stage = v.stage; - metalEntry.shader = msl; - spirv.clear(); - msl.clear(); - textDictionary.addText(metalEntry.shader); - metalEntries.push_back(metalEntry); - } + GLSLPostProcessor::Config config{ + .shaderType = v.stage, + .shaderModel = shaderModel, + .glsl = {} + }; + + if (mEnableFramebufferFetch) { + config.glsl.subpassInputToColorLocation.emplace_back(0, 0); + } + + bool ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl); +#else + bool ok = true; #endif + if (!ok) { + showErrorMessage(mMaterialName.c_str_safe(), v.variant, targetApi, v.stage, shader); + cancelJobs = true; + return; + } + + if (targetApi == TargetApi::OPENGL) { + if (targetLanguage == TargetLanguage::SPIRV) { + sg.fixupExternalSamplers(shaderModel, shader, info); + } + } + + // NOTE: Everything below touches shared structures protected by a lock + // NOTE: do not execute expensive work from here on! + std::unique_lock lock(entriesLock); + + if (targetApi == TargetApi::OPENGL) { + glslEntry.stage = v.stage; + glslEntry.shader = shader; + + textDictionary.addText(glslEntry.shader); + glslEntries.push_back(glslEntry); + } + +#ifndef FILAMAT_LITE + if (targetApi == TargetApi::VULKAN) { + assert(!spirv.empty()); + spirvEntry.stage = v.stage; + + spirvEntry.dictionaryIndex = spirvDictionary.addBlob(spirv); + spirvEntries.push_back(spirvEntry); + } + + if (targetApi == TargetApi::METAL) { + assert(!spirv.empty()); + assert(msl.length() > 0); + metalEntry.stage = v.stage; + metalEntry.shader = msl; + + textDictionary.addText(metalEntry.shader); + metalEntries.push_back(metalEntry); + } +#endif + }); + + // NOTE: We run the first job separately to work the lack of thread safety + // guarantees in glslang. This library performs unguarded global + // operations on first use. + if (firstJob) { + jobSystem.runAndWait(job); + firstJob = false; + } else { + jobSystem.run(job); + } } + + jobSystem.runAndWait(parent); + } + + if (cancelJobs.load()) { + return false; } // Emit dictionary chunk (TextDictionaryReader and DictionaryTextChunk) @@ -784,7 +829,7 @@ MaterialBuilder& MaterialBuilder::output(VariableQualifier qualifier, OutputTarg } // Unconditionally add this output, then we'll check if we've maxed on on any particular target. - auto& output = mOutputs.emplace_back(name, qualifier, target, type, location); + mOutputs.emplace_back(name, qualifier, target, type, location); uint8_t colorOutputCount = 0; uint8_t depthOutputCount = 0; @@ -814,7 +859,7 @@ MaterialBuilder& MaterialBuilder::enableFramebufferFetch() noexcept { return *this; } -Package MaterialBuilder::build() noexcept { +Package MaterialBuilder::build(JobSystem& jobSystem) noexcept { if (materialBuilderClients == 0) { utils::slog.e << "Error: MaterialBuilder::init() must be called before build()." << utils::io::endl; @@ -861,7 +906,7 @@ Package MaterialBuilder::build() noexcept { const auto variants = mMaterialDomain == MaterialDomain::SURFACE ? determineSurfaceVariants(mVariantFilter, isLit(), mShadowMultiplier) : determinePostProcessVariants(); - bool success = generateShaders(variants, container, info); + bool success = generateShaders(jobSystem, variants, container, info); if (!success) { // Return an empty package to signal a failure to build the material. diff --git a/libs/filamat/tests/test_filamat.cpp b/libs/filamat/tests/test_filamat.cpp index 8b74af14f8..421923db0e 100644 --- a/libs/filamat/tests/test_filamat.cpp +++ b/libs/filamat/tests/test_filamat.cpp @@ -23,6 +23,11 @@ #include +#include + +#include + +using namespace utils; using namespace ASTUtils; using namespace filament::backend; @@ -39,7 +44,7 @@ static ::testing::AssertionResult PropertyListsMatch(const MaterialBuilder::Prop return ::testing::AssertionSuccess(); } -std::string shaderWithAllProperties(ShaderType type, +std::string shaderWithAllProperties(JobSystem& jobSystem, ShaderType type, const std::string fragmentCode, const std::string vertexCode = "", filamat::MaterialBuilder::Shading shadingModel = filamat::MaterialBuilder::Shading::LIT, filamat::MaterialBuilder::RefractionMode refractionMode = filamat::MaterialBuilder::RefractionMode::NONE) { @@ -60,7 +65,7 @@ std::string shaderWithAllProperties(ShaderType type, std::fill_n(allProperties, MaterialBuilder::MATERIAL_PROPERTIES_COUNT, true); // We need to "build" the material to resolve any includes in user code. - builder.build(); + builder.build(jobSystem); return builder.peek(type, {1, MaterialBuilder::TargetApi::OPENGL, MaterialBuilder::TargetLanguage::GLSL}, @@ -86,8 +91,17 @@ protected: } virtual void SetUp() { + jobSystem = std::make_unique(); + jobSystem->adopt(); MaterialBuilder::init(); } + + virtual void TearDown() { + jobSystem->emancipate(); + MaterialBuilder::shutdown(); + } + + std::unique_ptr jobSystem; }; TEST_F(MaterialCompiler, StaticCodeAnalyzerNothingDetected) { @@ -97,7 +111,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNothingDetected) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -117,7 +131,8 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNothingDetectedVertex) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::VERTEX, fragmentCode, vertexCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::VERTEX, + fragmentCode, vertexCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -138,7 +153,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNotFollowingINParameters) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -155,7 +170,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssign) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -177,7 +192,8 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssignVertex) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::VERTEX, fragmentCode, vertexCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::VERTEX, + fragmentCode, vertexCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -195,7 +211,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssignWithSwizzling) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -218,7 +234,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolAsOutParameterWithAliasing) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -241,7 +257,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolAsOutParameterWithAliasingAndSw } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -269,7 +285,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolInOutInChainWithDirectIndexInto } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -297,7 +313,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolInOutInChain) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -316,7 +332,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerBaseColor) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -334,7 +350,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerRoughness) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -352,7 +368,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerMetallic) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -370,7 +386,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerReflectance) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -388,7 +404,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAmbientOcclusion) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -406,7 +422,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoat) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -426,7 +442,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerTransmission) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, "", + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "", filamat::MaterialBuilder::Shading::LIT, filamat::MaterialBuilder::RefractionMode::CUBEMAP); @@ -448,7 +464,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatRoughness) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, ""); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, ""); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -467,7 +483,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatNormal) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, ""); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, ""); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -486,7 +502,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerThickness) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, "", + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "", filamat::MaterialBuilder::Shading::SUBSURFACE); GLSLTools glslTools; @@ -505,7 +521,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSubsurfacePower) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, "", + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "", filamat::MaterialBuilder::Shading::SUBSURFACE); GLSLTools glslTools; @@ -524,7 +540,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSubsurfaceColor) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, "", + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "", filamat::MaterialBuilder::Shading::SUBSURFACE); GLSLTools glslTools; @@ -543,7 +559,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAnisotropicDirection) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -561,7 +577,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAnisotropic) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -579,7 +595,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSheenColor) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode, "", + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "", filamat::MaterialBuilder::Shading::CLOTH); GLSLTools glslTools; @@ -598,7 +614,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSheenRoughness) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -616,7 +632,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNormal) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -634,7 +650,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerBentNormal) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -652,7 +668,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -670,7 +686,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerWithinInclude) { } )"); - std::string shaderCode = shaderWithAllProperties(ShaderType::FRAGMENT, fragmentCode); + std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode); GLSLTools glslTools; MaterialBuilder::PropertyList properties {false}; @@ -691,7 +707,7 @@ TEST_F(MaterialCompiler, EmptyName) { builder.material(shaderCode.c_str()); // The material should compile successfully with an empty name builder.name(""); - filamat::Package result = builder.build(); + filamat::Package result = builder.build(*jobSystem); } TEST_F(MaterialCompiler, Uv0AndUv1) { @@ -699,7 +715,7 @@ TEST_F(MaterialCompiler, Uv0AndUv1) { // Requiring both sets of UV coordinates should not fail. builder.require(filament::VertexAttribute::UV0); builder.require(filament::VertexAttribute::UV1); - filamat::Package result = builder.build(); + filamat::Package result = builder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } @@ -709,7 +725,7 @@ TEST_F(MaterialCompiler, Arrays) { builder.parameter(UniformType::FLOAT4, 1, "f4"); builder.parameter(UniformType::FLOAT, 1, "f1"); - filamat::Package result = builder.build(); + filamat::Package result = builder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } diff --git a/libs/filamat/tests/test_includes.cpp b/libs/filamat/tests/test_includes.cpp index 30798f4d81..448f70e7ff 100644 --- a/libs/filamat/tests/test_includes.cpp +++ b/libs/filamat/tests/test_includes.cpp @@ -23,6 +23,11 @@ #include "MockIncluder.h" #include +#include + +#include + +using namespace utils; // ------------------------------------------------------------------------------------------------- @@ -468,15 +473,18 @@ TEST(IncludeResolver, MultipleIncludesSameLineLineDirective) { class MaterialBuilder : public ::testing::Test { protected: - + std::unique_ptr jobSystem; filamat::MaterialBuilder mBuilder; MaterialBuilder() { + jobSystem = std::make_unique(); + jobSystem->adopt(); filamat::MaterialBuilder::init(); mBuilder.optimization(filamat::MaterialBuilder::Optimization::NONE); } virtual ~MaterialBuilder() { + jobSystem->emancipate(); filamat::MaterialBuilder::shutdown(); } }; @@ -487,7 +495,7 @@ TEST_F(MaterialBuilder, NoIncluder) { )"); mBuilder.material(shaderCode.c_str()); - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); // Shader code with an include should fail to compile if no includer is specified. EXPECT_FALSE(result.isValid()); @@ -507,7 +515,7 @@ TEST_F(MaterialBuilder, Include) { )"); mBuilder.includeCallback(includer); - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } @@ -528,7 +536,7 @@ TEST_F(MaterialBuilder, IncludeVertex) { )"); mBuilder.includeCallback(includer); - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } @@ -548,7 +556,7 @@ TEST_F(MaterialBuilder, IncludeWithinFunction) { mBuilder.includeCallback(includer); - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } @@ -561,11 +569,11 @@ TEST_F(MaterialBuilder, IncludeFailure) { MockIncluder includer; mBuilder.includeCallback(includer); - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); EXPECT_FALSE(result.isValid()); } TEST_F(MaterialBuilder, NoShaderCode) { - filamat::Package result = mBuilder.build(); + filamat::Package result = mBuilder.build(*jobSystem); EXPECT_TRUE(result.isValid()); } diff --git a/libs/filamentapp/src/MeshAssimp.cpp b/libs/filamentapp/src/MeshAssimp.cpp index 343eb2f5fd..afceff488d 100644 --- a/libs/filamentapp/src/MeshAssimp.cpp +++ b/libs/filamentapp/src/MeshAssimp.cpp @@ -190,7 +190,7 @@ Material* createMaterialFromConfig(Engine& engine, MaterialConfig config ) { builder.shading(config.unlit ? Shading::UNLIT : Shading::LIT); - Package pkg = builder.build(); + Package pkg = builder.build(engine.getJobSystem()); return Material::Builder().package(pkg.getData(), pkg.getSize()).build(engine); } diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp index 1e2fa70325..95b1ed66d2 100644 --- a/libs/gltfio/src/MaterialGenerator.cpp +++ b/libs/gltfio/src/MaterialGenerator.cpp @@ -18,7 +18,6 @@ #include -#include #include #include @@ -469,7 +468,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const builder.shading(Shading::LIT); } - Package pkg = builder.build(); + Package pkg = builder.build(engine->getJobSystem()); return Material::Builder().package(pkg.getData(), pkg.getSize()).build(*engine); } diff --git a/samples/sample_cloth.cpp b/samples/sample_cloth.cpp index 1ca2ada47d..4f6eec4f12 100644 --- a/samples/sample_cloth.cpp +++ b/samples/sample_cloth.cpp @@ -208,7 +208,7 @@ static void setup(Engine* engine, View* view, Scene* scene) { )SHADER") .shading(Shading::CLOTH); - Package pkg = builder.build(); + Package pkg = builder.build(engine->getJobSystem()); g_material = Material::Builder().package(pkg.getData(), pkg.getSize()) .build(*engine); diff --git a/samples/sample_full_pbr.cpp b/samples/sample_full_pbr.cpp index 2629dbee59..48859e7a06 100644 --- a/samples/sample_full_pbr.cpp +++ b/samples/sample_full_pbr.cpp @@ -375,7 +375,7 @@ static void setup(Engine* engine, View* view, Scene* scene) { } } - Package pkg = builder.build(); + Package pkg = builder.build(engine->getJobSystem()); g_material = Material::Builder().package(pkg.getData(), pkg.getSize()).build(*engine); g_materialInstances["DefaultMaterial"] = g_material->createInstance(); diff --git a/samples/sample_normal_map.cpp b/samples/sample_normal_map.cpp index a6a92874fe..9a95d4d2ff 100644 --- a/samples/sample_normal_map.cpp +++ b/samples/sample_normal_map.cpp @@ -299,7 +299,7 @@ static void setup(Engine* engine, View*, Scene* scene) { .parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "baseColorMap"); } - Package pkg = builder.build(); + Package pkg = builder.build(engine->getJobSystem()); g_material = Material::Builder().package(pkg.getData(), pkg.getSize()) .build(*engine); diff --git a/tools/matc/src/matc/MaterialCompiler.cpp b/tools/matc/src/matc/MaterialCompiler.cpp index 1477d07273..995b9602b7 100644 --- a/tools/matc/src/matc/MaterialCompiler.cpp +++ b/tools/matc/src/matc/MaterialCompiler.cpp @@ -24,6 +24,8 @@ #include +#include + #include "DirIncluder.h" #include "MaterialLexeme.h" #include "MaterialLexer.h" @@ -299,9 +301,15 @@ bool MaterialCompiler::run(const Config& config) { builder.shaderDefine(define.first.c_str(), define.second.c_str()); } + JobSystem js; + js.adopt(); + // Write builder.build() to output. - Package package = builder.build(); + Package package = builder.build(js); + + js.emancipate(); MaterialBuilder::shutdown(); + if (!package.isValid()) { std::cerr << "Could not compile material " << input->getName() << std::endl; return false;