From 7d31a7f7ea8162becd238ded3a50690a034c1c01 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 15 Nov 2023 10:51:58 -0800 Subject: [PATCH 01/16] Move stereoscopic debug options to Settings framework (#7347) --- .../filament-android/src/main/cpp/Engine.cpp | 10 +++- .../filament-android/src/main/cpp/View.cpp | 11 ++++ .../com/google/android/filament/Engine.java | 55 +++++++++++++++++-- .../com/google/android/filament/View.java | 47 ++++++++++++++++ filament/include/filament/Engine.h | 26 ++++++++- filament/include/filament/View.h | 5 +- filament/src/Engine.cpp | 8 +++ filament/src/View.cpp | 2 +- filament/src/details/Engine.h | 4 ++ filament/src/details/View.cpp | 4 +- filament/src/details/View.h | 6 +- libs/viewer/include/viewer/Settings.h | 3 + libs/viewer/include/viewer/ViewerGui.h | 5 -- libs/viewer/src/Settings.cpp | 21 +++++++ libs/viewer/src/ViewerGui.cpp | 10 +--- samples/gltf_viewer.cpp | 19 ------- web/filament-js/extensions.js | 6 ++ web/filament-js/filament.d.ts | 3 + web/filament-js/jsbindings.cpp | 3 + 19 files changed, 203 insertions(+), 45 deletions(-) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index b5fd507aa18..51499a14bc9 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -435,6 +435,13 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j return (jboolean)engine->isAutomaticInstancingEnabled(); } +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) { + Engine* engine = (Engine*) nativeEngine; + return (jlong) engine->getMaxStereoscopicEyes(); +} + + extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jclass, jlong nativeEngine) { @@ -477,7 +484,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*, jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB, jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB, - jlong jobSystemThreadCount) { + jlong jobSystemThreadCount, jlong stereoscopicEyeCount) { Engine::Builder* builder = (Engine::Builder*) nativeBuilder; Engine::Config config = { .commandBufferSizeMB = (uint32_t) commandBufferSizeMB, @@ -486,6 +493,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu .minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB, .perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB, .jobSystemThreadCount = (uint32_t) jobSystemThreadCount, + .stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount, }; builder->config(&config); } diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index fc4de145e90..e2d8efacb3a 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -480,6 +480,17 @@ Java_com_google_android_filament_View_nIsStencilBufferEnabled(JNIEnv *, jclass, return view->isStencilBufferEnabled(); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_View_nSetStereoscopicOptions(JNIEnv *, jclass, jlong nativeView, + jboolean enabled) { + View* view = (View*) nativeView; + View::StereoscopicOptions options { + .enabled = (bool) enabled + }; + view->setStereoscopicOptions(options); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass, 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 26fd9e81c3a..014cc33ceec 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 @@ -111,6 +111,8 @@ public class Engine { private long mNativeObject; + private Config mConfig; + @NonNull private final TransformManager mTransformManager; @NonNull private final LightManager mLightManager; @NonNull private final RenderableManager mRenderableManager; @@ -163,6 +165,7 @@ public static class Builder { @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private final BuilderFinalizer mFinalizer; private final long mNativeBuilder; + private Config mConfig; public Builder() { mNativeBuilder = nCreateBuilder(); @@ -204,10 +207,11 @@ public Builder sharedContext(Object sharedContext) { * @return A reference to this Builder for chaining calls. */ public Builder config(Config config) { + mConfig = config; nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB, config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB, config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB, - config.jobSystemThreadCount); + config.jobSystemThreadCount, config.stereoscopicEyeCount); return this; } @@ -235,7 +239,7 @@ public Builder featureLevel(FeatureLevel featureLevel) { public Engine build() { long nativeEngine = nBuilderBuild(mNativeBuilder); if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); - return new Engine(nativeEngine); + return new Engine(nativeEngine, mConfig); } private static class BuilderFinalizer { @@ -343,14 +347,24 @@ public static class Config { * the number of threads to use. */ public long jobSystemThreadCount = 0; + + /** + * The number of eyes to render when stereoscopic rendering is enabled. Supported values are + * between 1 and Engine#getMaxStereoscopicEyes() (inclusive). + * + * @see View#setStereoscopicOptions + * @see Engine#getMaxStereoscopicEyes + */ + public long stereoscopicEyeCount = 2; } - private Engine(long nativeEngine) { + private Engine(long nativeEngine, Config config) { mNativeObject = nativeEngine; mTransformManager = new TransformManager(nGetTransformManager(nativeEngine)); mLightManager = new LightManager(nGetLightManager(nativeEngine)); mRenderableManager = new RenderableManager(nGetRenderableManager(nativeEngine)); mEntityManager = new EntityManager(nGetEntityManager(nativeEngine)); + mConfig = config; } /** @@ -543,6 +557,37 @@ public boolean isAutomaticInstancingEnabled() { return nIsAutomaticInstancingEnabled(getNativeObject()); } + /** + * Retrieves the configuration settings of this {@link Engine}. + * + * This method returns the configuration object that was supplied to the Engine's {@link + * Builder#config} method during the creation of this Engine. If the {@link Builder::config} + * method was not explicitly called (or called with null), this method returns the default + * configuration settings. + * + * @return a {@link Config} object with this Engine's configuration + * @see Builder#config + */ + @NonNull + public Config getConfig() { + if (mConfig == null) { + mConfig = new Config(); + } + return mConfig; + } + + /** + * Returns the maximum number of stereoscopic eyes supported by Filament. The actual number of + * eyes rendered is set at Engine creation time with the {@link + * Engine#Config#stereoscopicEyeCount} setting. + * + * @return the max number of stereoscopic eyes supported + * @see Engine#Config#stereoscopicEyeCount + */ + public long getMaxStereoscopicEyes() { + return nGetMaxStereoscopicEyes(getNativeObject()); + } + // SwapChain @@ -1171,6 +1216,7 @@ private static void assertDestroy(boolean success) { private static native long nGetEntityManager(long nativeEngine); private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable); private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine); + private static native long nGetMaxStereoscopicEyes(long nativeEngine); private static native int nGetSupportedFeatureLevel(long nativeEngine); private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal); private static native int nGetActiveFeatureLevel(long nativeEngine); @@ -1180,7 +1226,8 @@ private static void assertDestroy(boolean success) { private static native void nSetBuilderBackend(long nativeBuilder, long backend); private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB, long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB, - long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount); + long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount, + long stereoscopicEyeCount); private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal); private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext); private static native long nBuilderBuild(long nativeBuilder); diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java index 91622d26c41..8dfe948023d 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/View.java +++ b/android/filament-android/src/main/java/com/google/android/filament/View.java @@ -75,6 +75,7 @@ public class View { private AmbientOcclusionOptions mAmbientOcclusionOptions; private BloomOptions mBloomOptions; private FogOptions mFogOptions; + private StereoscopicOptions mStereoscopicOptions; private RenderTarget mRenderTarget; private BlendMode mBlendMode; private DepthOfFieldOptions mDepthOfFieldOptions; @@ -1055,6 +1056,51 @@ public boolean isStencilBufferEnabled() { return nIsStencilBufferEnabled(getNativeObject()); } + /** + * Sets the stereoscopic rendering options for this view. + * + *

+ * Currently, only one type of stereoscopic rendering is supported: side-by-side. + * Side-by-side stereo rendering splits the viewport into two halves: a left and right half. + * Eye 0 will render to the left half, while Eye 1 will render into the right half. + *

+ * + *

+ * Currently, the following features are not supported with stereoscopic rendering: + * - post-processing + * - shadowing + * - punctual lights + *

+ * + *

+ * Stereo rendering depends on device and platform support. To check if stereo rendering is + * supported, use {@link Engine#isStereoSupported()}. If stereo rendering is not supported, then + * the stereoscopic options have no effect. + *

+ * + * @param options The stereoscopic options to use on this view + * @see #getStereoscopicOptions + */ + public void setStereoscopicOptions(@NonNull StereoscopicOptions options) { + mStereoscopicOptions = options; + nSetStereoscopicOptions(getNativeObject(), options.enabled); + } + + /** + * Gets the stereoscopic options. + * + * @return options Stereoscopic options currently set. + * @see #setStereoscopicOptions + */ + @NonNull + public StereoscopicOptions getStereoscoopicOptions() { + if (mStereoscopicOptions == null) { + mStereoscopicOptions = new StereoscopicOptions(); + } + return mStereoscopicOptions; + } + + /** * A class containing the result of a picking query */ @@ -1220,6 +1266,7 @@ void clearNativeObject() { private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, int levels, int blendMode, boolean threshold, boolean enabled, float highlight, boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold); private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float cutOffDistance, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, long skyColorNativeObject, boolean enabled); + private static native void nSetStereoscopicOptions(long nativeView, boolean enabled); private static native void nSetBlendMode(long nativeView, int blendMode); private static native void nSetDepthOfFieldOptions(long nativeView, float cocScale, float maxApertureDiameter, boolean enabled, int filter, boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 2442e6c2554..53b08215d99 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -294,9 +294,10 @@ class UTILS_PUBLIC Engine { /* * The number of eyes to render when stereoscopic rendering is enabled. Supported values are - * between 1 and CONFIG_MAX_STEREOSCOPIC_EYES (inclusive). + * between 1 and Engine::getMaxStereoscopicEyes() (inclusive). * * @see View::setStereoscopicOptions + * @see Engine::getMaxStereoscopicEyes */ uint8_t stereoscopicEyeCount = 2; }; @@ -562,6 +563,29 @@ class UTILS_PUBLIC Engine { */ bool isStereoSupported() const noexcept; + /** + * Retrieves the configuration settings of this Engine. + * + * This method returns the configuration object that was supplied to the Engine's + * Builder::config method during the creation of this Engine. If the Builder::config method was + * not explicitly called (or called with nullptr), this method returns the default configuration + * settings. + * + * @return a Config object with this Engine's configuration + * @see Builder::config + */ + const Config& getConfig() const noexcept; + + /** + * Returns the maximum number of stereoscopic eyes supported by Filament. The actual number of + * eyes rendered is set at Engine creation time with the Engine::Config::stereoscopicEyeCount + * setting. + * + * @return the max number of stereoscopic eyes supported + * @see Engine::Config::stereoscopicEyeCount + */ + static size_t getMaxStereoscopicEyes() noexcept; + /** * @return EntityManager used by filament */ diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index a58587e9ede..8832e054cf2 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -691,11 +691,12 @@ class UTILS_PUBLIC View : public FilamentAPI { * - punctual lights * * Stereo rendering depends on device and platform support. To check if stereo rendering is - * supported, use Engine::isStereoSupported(). + * supported, use Engine::isStereoSupported(). If stereo rendering is not supported, then the + * stereoscopic options have no effect. * * @param options The stereoscopic options to use on this view */ - void setStereoscopicOptions(StereoscopicOptions const& options); + void setStereoscopicOptions(StereoscopicOptions const& options) noexcept; /** * Returns the stereoscopic options associated with this View. diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 01b956484c4..661f183425d 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -327,10 +327,18 @@ size_t Engine::getMaxAutomaticInstances() const noexcept { return downcast(this)->getMaxAutomaticInstances(); } +const Engine::Config& Engine::getConfig() const noexcept { + return downcast(this)->getConfig(); +} + bool Engine::isStereoSupported() const noexcept { return downcast(this)->isStereoSupported(); } +size_t Engine::getMaxStereoscopicEyes() noexcept { + return FEngine::getMaxStereoscopicEyes(); +} + #if defined(__EMSCRIPTEN__) void Engine::resetBackendState() noexcept { downcast(this)->resetBackendState(); diff --git a/filament/src/View.cpp b/filament/src/View.cpp index dd8e9380a75..bc5da818290 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -283,7 +283,7 @@ bool View::isStencilBufferEnabled() const noexcept { return downcast(this)->isStencilBufferEnabled(); } -void View::setStereoscopicOptions(const StereoscopicOptions& options) { +void View::setStereoscopicOptions(const StereoscopicOptions& options) noexcept { return downcast(this)->setStereoscopicOptions(options); } diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index bb3a283ef8d..bbe9c0fea98 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -184,6 +184,10 @@ class FEngine : public Engine { bool isStereoSupported() const noexcept { return getDriver().isStereoSupported(); } + static size_t getMaxStereoscopicEyes() noexcept { + return CONFIG_MAX_STEREOSCOPIC_EYES; + } + PostProcessManager const& getPostProcessManager() const noexcept { return mPostProcessManager; } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 829c3ecc5d9..fe66b844ca9 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -1126,9 +1126,7 @@ View::PickingQuery& FView::pick(uint32_t x, uint32_t y, backend::CallbackHandler return *pQuery; } -void FView::setStereoscopicOptions(const StereoscopicOptions& options) { - ASSERT_PRECONDITION(!options.enabled || mIsStereoSupported, - "Stereo rendering is not supported."); +void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept { mStereoscopicOptions = options; } diff --git a/filament/src/details/View.h b/filament/src/details/View.h index c2152a52f55..0f974754d3e 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -168,7 +168,9 @@ class FView : public View { bool hasDPCF() const noexcept { return mShadowType == ShadowType::DPCF; } bool hasPCSS() const noexcept { return mShadowType == ShadowType::PCSS; } bool hasPicking() const noexcept { return mActivePickingQueriesList != nullptr; } - bool hasInstancedStereo() const noexcept { return mStereoscopicOptions.enabled; } + bool hasInstancedStereo() const noexcept { + return mIsStereoSupported && mStereoscopicOptions.enabled; + } FrameGraphId renderShadowMaps(FEngine& engine, FrameGraph& fg, CameraInfo const& cameraInfo, math::float4 const& userTime, @@ -192,7 +194,7 @@ class FView : public View { bool isStencilBufferEnabled() const noexcept { return mStencilBufferEnabled; } - void setStereoscopicOptions(StereoscopicOptions const& options); + void setStereoscopicOptions(StereoscopicOptions const& options) noexcept; FCamera const* getDirectionalLightCamera() const noexcept { return mShadowMapManager.getDirectionalLightCamera(); diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index fc46d2887ed..926894a9154 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -77,6 +77,7 @@ using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions; using VignetteOptions = filament::View::VignetteOptions; using VsmShadowOptions = filament::View::VsmShadowOptions; using GuardBandOptions = filament::View::GuardBandOptions; +using StereoscopicOptions = filament::View::StereoscopicOptions; using LightManager = filament::LightManager; // These functions push all editable property values to their respective Filament objects. @@ -192,6 +193,7 @@ struct ViewSettings { VignetteOptions vignette; VsmShadowOptions vsmShadowOptions; GuardBandOptions guardBand; + StereoscopicOptions stereoscopicOptions; // Custom View Options ColorGradingSettings colorGrading; @@ -231,6 +233,7 @@ struct ViewerOptions { float cameraISO = 100.0f; float cameraNear = 0.1f; float cameraFar = 100.0f; + float cameraEyeOcularDistance = 0.0f; float groundShadowStrength = 0.75f; bool groundPlaneEnabled = false; bool skyboxEnabled = true; diff --git a/libs/viewer/include/viewer/ViewerGui.h b/libs/viewer/include/viewer/ViewerGui.h index 6d6926a9aab..8f84335747c 100644 --- a/libs/viewer/include/viewer/ViewerGui.h +++ b/libs/viewer/include/viewer/ViewerGui.h @@ -231,8 +231,6 @@ class UTILS_PUBLIC ViewerGui { int getCurrentCamera() const { return mCurrentCamera; } - float getOcularDistance() const { return mOcularDistance; } - private: using SceneMask = gltfio::NodeManager::SceneMask; @@ -268,9 +266,6 @@ class UTILS_PUBLIC ViewerGui { SceneMask mVisibleScenes; bool mShowingRestPose = false; - // Stereoscopic debugging - float mOcularDistance = 0.0f; - // 0 is the default "free camera". Additional cameras come from the gltf file (1-based index). int mCurrentCamera = 0; diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index febeb321fd5..6d1408e7e56 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -302,6 +302,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewSett i = parse(tokens, i + 1, jsonChunk, &out->vsmShadowOptions); } else if (compare(tok, jsonChunk, "postProcessingEnabled") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->postProcessingEnabled); + } else if (compare(tok, jsonChunk, "stereoscopicOptions") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->stereoscopicOptions); } else { slog.w << "Invalid view setting key: '" << STR(tok, jsonChunk) << "'" << io::endl; i = parse(tokens, i + 1); @@ -500,6 +502,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewerOp i = parse(tokens, i + 1, jsonChunk, &out->cameraNear); } else if (compare(tok, jsonChunk, "cameraFar") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->cameraFar); + } else if (compare(tok, jsonChunk, "cameraEyeOcularDistance") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->cameraEyeOcularDistance); } else if (compare(tok, jsonChunk, "groundShadowStrength") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->groundShadowStrength); } else if (compare(tok, jsonChunk, "groundPlaneEnabled") == 0) { @@ -572,6 +576,7 @@ void applySettings(Engine* engine, const ViewSettings& settings, View* dest) { dest->setShadowType(settings.shadowType); dest->setVsmShadowOptions(settings.vsmShadowOptions); dest->setGuardBandOptions(settings.guardBand); + dest->setStereoscopicOptions(settings.stereoscopicOptions); dest->setPostProcessingEnabled(settings.postProcessingEnabled); } @@ -646,6 +651,20 @@ void applySettings(Engine* engine, const ViewerOptions& settings, Camera* camera camera->setFocusDistance(settings.cameraFocusDistance); } engine->setAutomaticInstancingEnabled(settings.autoInstancingEnabled); + + // Eyes are rendered from left-to-right, i.e., eye 0 is rendered to the left side of the + // window. + // For testing, we want to render a side-by-side layout so users can view with + // "cross-eyed" stereo. + // For cross-eyed stereo, Eye 0 is really the RIGHT eye, while Eye 1 is the LEFT eye. + const auto od = settings.cameraEyeOcularDistance; + const auto eyeCount = engine->getConfig().stereoscopicEyeCount; + const mat4 rightEye = mat4::translation(double3{ od, 0.0, 0.0}); // right eye + const mat4 leftEye = mat4::translation(double3{-od, 0.0, 0.0}); // left eye + const mat4 modelMatrices[2] = { rightEye, leftEye }; + for (int i = 0; i < eyeCount; i++) { + camera->setEyeModelMatrix(i, modelMatrices[i % 2]); + } } constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noexcept { @@ -857,6 +876,7 @@ static std::ostream& operator<<(std::ostream& out, const ViewerOptions& in) { << "\"cameraISO\": " << (in.cameraISO) << ",\n" << "\"cameraNear\": " << (in.cameraNear) << ",\n" << "\"cameraFar\": " << (in.cameraFar) << ",\n" + << "\"cameraEyeOcularDistance\": " << (in.cameraEyeOcularDistance) << ",\n" << "\"groundShadowStrength\": " << (in.groundShadowStrength) << ",\n" << "\"groundPlaneEnabled\": " << to_string(in.groundPlaneEnabled) << ",\n" << "\"skyboxEnabled\": " << to_string(in.skyboxEnabled) << ",\n" @@ -894,6 +914,7 @@ static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) { << "\"shadowType\": " << (in.shadowType) << ",\n" << "\"vsmShadowOptions\": " << (in.vsmShadowOptions) << ",\n" << "\"guardBand\": " << (in.guardBand) << ",\n" + << "\"stereoscopicOptions\": " << (in.stereoscopicOptions) << ",\n" << "\"postProcessingEnabled\": " << to_string(in.postProcessingEnabled) << "\n" << "}"; } diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 37d6420491c..531fca1f34d 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -1067,13 +1067,9 @@ void ViewerGui::updateUserInterface() { ImGui::ListBox("Cameras", &mCurrentCamera, cstrings.data(), cstrings.size()); } - StereoscopicOptions stereoOptions = mView->getStereoscopicOptions(); - ImGui::Checkbox("Instanced stereo", &stereoOptions.enabled); - if (stereoOptions.enabled) { - ImGui::SliderFloat("Ocular distance", &mOcularDistance, 0.0f, 10.0f); - - } - mView->setStereoscopicOptions(stereoOptions); + ImGui::Checkbox("Instanced stereo", &mSettings.view.stereoscopicOptions.enabled); + ImGui::SliderFloat( + "Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f, 1.0f); ImGui::Unindent(); } diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 1a09e79f8d9..dab3a2914fc 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -1039,25 +1039,6 @@ int main(int argc, char** argv) { camera->setScaling({1.0 / aspectRatio, 1.0}); } - if (view->getStereoscopicOptions().enabled) { - Camera& c = view->getCamera(); - auto od = app.viewer->getOcularDistance(); - // Eyes are rendered from left-to-right, i.e., eye 0 is rendered to the left side of the - // window. - // For testing, we want to render a side-by-side layout so users can view with - // "cross-eyed" stereo. - // For cross-eyed stereo, Eye 0 is really the RIGHT eye, while Eye 1 is the LEFT eye. - const mat4 rightEye = mat4::translation(double3{ od, 0.0, 0.0}); // right eye - const mat4 leftEye = mat4::translation(double3{-od, 0.0, 0.0}); // left eye - const mat4 modelMatrices[4] = { rightEye, leftEye, rightEye, leftEye }; - for (int i = 0; i < std::min(app.config.stereoscopicEyeCount, 4); i++) { - c.setEyeModelMatrix(i, modelMatrices[i]); - } - } else { - for (int i = 0; i < app.config.stereoscopicEyeCount; i++) { - view->getCamera().setEyeModelMatrix(i, {}); - } - } static bool stereoscopicEnabled = false; if (stereoscopicEnabled != view->getStereoscopicOptions().enabled) { // Stereo was turned on/off. diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js index 3040bafcd1f..36d0bf988be 100644 --- a/web/filament-js/extensions.js +++ b/web/filament-js/extensions.js @@ -339,6 +339,12 @@ Filament.loadClassExtensions = function() { this._setGuardBandOptions(options); }; + /// setStereoscopicOptions ::method:: + Filament.View.prototype.setStereoscopicOptions = function(overrides) { + const options = this.setStereoscopicOptionsDefaults(overrides); + this._setStereoscopicOptions(options); + } + /// BufferObject ::core class:: /// setBuffer ::method:: diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 197a5a7acc5..6d5122e5b7b 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -505,6 +505,7 @@ export class View { public setFogOptions(options: View$FogOptions): void; public setVignetteOptions(options: View$VignetteOptions): void; public setGuardBandOptions(options: View$GuardBandOptions): void; + public setStereoscopicOptions(options: View$StereoscopicOptions): void; public setAmbientOcclusion(ambientOcclusion: View$AmbientOcclusion): void; public getAmbientOcclusion(): View$AmbientOcclusion; public setBlendMode(mode: View$BlendMode): void; @@ -546,6 +547,8 @@ export class Engine { public createTextureFromJpeg(urlOrBuffer: BufferReference, options?: object): Texture; public createTextureFromPng(urlOrBuffer: BufferReference, options?: object): Texture; + public static getMaxStereoscopicEyes(): number; + public createIblFromKtx1(urlOrBuffer: BufferReference): IndirectLight; public createSkyFromKtx1(urlOrBuffer: BufferReference): Skybox; public createTextureFromKtx1(urlOrBuffer: BufferReference, options?: object): Texture; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 90e4171b7a5..bf8e860b5b6 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -400,6 +400,8 @@ class_("Engine") .function("getActiveFeatureLevel", &Engine::getActiveFeatureLevel) + .class_function("getMaxStereoscopicEyes", &Engine::getMaxStereoscopicEyes) + .function("_execute", EMBIND_LAMBDA(void, (Engine* engine), { EM_ASM_INT({ const handle = window.filament_contextHandle; @@ -654,6 +656,7 @@ class_("View") .function("_setFogOptions", &View::setFogOptions) .function("_setVignetteOptions", &View::setVignetteOptions) .function("_setGuardBandOptions", &View::setGuardBandOptions) + .function("_setStereoscopicOptions", &View::setStereoscopicOptions) .function("setAmbientOcclusion", &View::setAmbientOcclusion) .function("getAmbientOcclusion", &View::getAmbientOcclusion) .function("setAntiAliasing", &View::setAntiAliasing) From 5d0c06e3f22215b4bd22f8d8ccfc1eb95abfa6bc Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 15 Nov 2023 13:50:59 -0800 Subject: [PATCH 02/16] Remove debug code in MetalShaderCompiler (#7368) --- filament/backend/src/metal/MetalShaderCompiler.mm | 3 --- 1 file changed, 3 deletions(-) diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index e798d1b5ae3..7f4280979ab 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -170,9 +170,6 @@ bool isReady() const noexcept { CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); mCompilerThreadPool.queue(priorityQueue, token, [this, name, device = mDevice, program = std::move(program), token]() { - int sleepTime = atoi(name.c_str()); - sleep(sleepTime); - MetalFunctionBundle compiledProgram = compileProgram(program, device); token->set(compiledProgram); From 8f6c4841cb86e4052fe0ee0c73d2b48501971d9c Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Wed, 15 Nov 2023 14:42:25 -0800 Subject: [PATCH 03/16] Fix LoadImage backend tests --- filament/backend/test/test_LoadImage.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index 9b9dbfdf12d..15c2fc19657 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -231,9 +231,10 @@ TEST_F(BackendTest, UpdateImage2D) { // Test integer format uploads. // TODO: These cases fail on OpenGL and Vulkan. - testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI); - testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI); - testCases.emplace_back("RGB_INTEGER, INT -> RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I); + // TODO: These cases now also fail on Metal, but at some point previously worked. + // testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI); + // testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI); + // testCases.emplace_back("RGB_INTEGER, INT -> RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I); // Test uploads with buffer padding. // TODO: Vulkan crashes with "Assertion failed: (offset + size <= allocationSize)" @@ -440,7 +441,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) { SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); std::string fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentUpdateImageMip); @@ -459,7 +460,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) { // Create image data. PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512); - api.update3DImage(texture, 1, 0, 0, 0, 512, 512, 1, std::move(descriptor)); + api.update3DImage(texture, /* level*/ 1, 0, 0, 0, 512, 512, 1, std::move(descriptor)); api.beginFrame(0, 0); @@ -517,7 +518,7 @@ TEST_F(BackendTest, UpdateImage3D) { SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); std::string fragment = stringReplace("{samplerType}", getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate); From caae42fdf6ab48b9bf361f4a49f49e9b44bf4dda Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 15:00:58 -0800 Subject: [PATCH 04/16] Enable preprocessor optimization of ESSL 1.0 Since #7358 is blocked by an upstream spirv-cross issue, we can at least do a bit of preprocessor optimization for ESSL 1.0 code in the meantime and introduce the FILAMENT_EFFECTIVE_VERSION preprocessor definitions. --- filament/src/materials/blitLow.mat | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 7 ++++- libs/filamat/src/shaders/CodeGenerator.cpp | 30 ++++++++++++++++++++++ shaders/src/depth_main.fs | 2 +- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/filament/src/materials/blitLow.mat b/filament/src/materials/blitLow.mat index 4bebc8612e6..09e6c79920c 100644 --- a/filament/src/materials/blitLow.mat +++ b/filament/src/materials/blitLow.mat @@ -35,7 +35,7 @@ vertex { fragment { void postProcess(inout PostProcessInputs postProcess) { -#if __VERSION__ == 100 +#if FILAMENT_EFFECTIVE_VERSION == 100 postProcess.color = texture2D(materialParams_color, variable_vertex.xy); #else postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 871bc298002..817429bb665 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -23,6 +23,7 @@ #include #include +#include "backend/DriverEnums.h" #include "sca/builtinResource.h" #include "sca/GLSLTools.h" @@ -395,7 +396,11 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - fullOptimization(tShader, config, internalConfig); + if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { + preprocessOptimization(tShader, config, internalConfig); + } else { + fullOptimization(tShader, config, internalConfig); + } break; } diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index b4818a50fde..a5b48ff7c8b 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -151,6 +151,36 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << "#define FILAMENT_HAS_FEATURE_INSTANCING\n"; } + // During compilation and optimization, __VERSION__ reflects the shader language version of the + // intermediate code, not the version of the final code. spirv-cross automatically adapts + // certain language features (e.g. fragment output) but leaves others untouched (e.g. sampler + // functions, bit shift operations). Client code may have to make decisions based on this + // information, so define a FILAMENT_EFFECTIVE_VERSION constant. + const char *effective_version; + if (mTargetLanguage == TargetLanguage::GLSL) { + effective_version = "__VERSION__"; + } else { + switch (mShaderModel) { + case ShaderModel::MOBILE: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + effective_version = "300"; + } else { + effective_version = "100"; + } + break; + case ShaderModel::DESKTOP: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) { + effective_version = "450"; + } else { + effective_version = "410"; + } + break; + default: + assert(false); + } + } + generateDefine(out, "FILAMENT_EFFECTIVE_VERSION", effective_version); + if (stage == ShaderStage::VERTEX) { CodeGenerator::generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV); CodeGenerator::generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing); diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 77608f3e15c..1632509f4aa 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -57,7 +57,7 @@ void main() { fragColor.xy = computeDepthMomentsVSM(depth); fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) -#if MATERIAL_FEATURE_LEVEL == 0 +#if FILAMENT_EFFECTIVE_VERSION == 0 outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; From 494934f34eacacf4138469c659d2644e81e9b9ca Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 15:56:38 -0800 Subject: [PATCH 05/16] Add option to matc to disable ESSL 1.0 codegen --- CMakeLists.txt | 5 +++ filament/CMakeLists.txt | 32 +-------------- filament/src/materials/blitLow1.mat | 39 ------------------- libs/filagui/CMakeLists.txt | 8 ---- libs/filagui/src/materials/uiBlit1.mat | 28 ------------- .../filamat/include/filamat/MaterialBuilder.h | 4 ++ libs/filamat/src/MaterialBuilder.cpp | 10 ++++- tools/matc/src/matc/CommandlineConfig.cpp | 9 ++++- tools/matc/src/matc/Config.h | 5 +++ tools/matc/src/matc/MaterialCompiler.cpp | 1 + 10 files changed, 32 insertions(+), 109 deletions(-) delete mode 100644 filament/src/materials/blitLow1.mat delete mode 100644 libs/filagui/src/materials/uiBlit1.mat diff --git a/CMakeLists.txt b/CMakeLists.txt index dd5bfaaf8d6..4bb1422da64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,6 +525,11 @@ if (FILAMENT_SUPPORTS_METAL) set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal) endif() +# Disable ESSL 1.0 code generation. +if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0) + set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) +endif() + # Enable debug info (preserves names in SPIR-V) if (FILAMENT_ENABLE_MATDBG) set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d) diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index e6685c7d5ef..f9bba52968f 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -242,6 +242,7 @@ set(MATERIAL_SRCS src/materials/antiAliasing/fxaa.mat src/materials/antiAliasing/taa.mat src/materials/vsmMipmap.mat + src/materials/blitLow.mat ) set(MATERIAL_FL0_SRCS @@ -249,13 +250,6 @@ set(MATERIAL_FL0_SRCS src/materials/skybox0.mat ) -# TODO(exv): Replace the below duplicated materials with a command-line option -# to matc to force disable including ESSL 1.0 code in FL0 materials. - -set(MATERIAL_SWITCH_FL0_SRCS_HACK - src/materials/blitLow.mat -) - # Embed the binary resource blob for materials. get_resgen_vars(${RESOURCE_DIR} materials) list(APPEND PRIVATE_HDRS ${RESGEN_HEADER}) @@ -326,30 +320,6 @@ foreach (mat_src ${MATERIAL_SRCS}) list(APPEND MATERIAL_BINS ${output_path}) endforeach() - -# HACK: Pick between the normal and FL0-exclusionary variants of the materials -# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. -foreach (mat_src ${MATERIAL_SWITCH_FL0_SRCS_HACK}) - get_filename_component(localname "${mat_src}" NAME_WE) - get_filename_component(fullname "${mat_src}" ABSOLUTE) - set(output_path "${MATERIAL_DIR}/${localname}.filamat") - - if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) - # Pick the non-FL0 variant instead. - string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") - string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") - endif() - - add_custom_command( - OUTPUT ${output_path} - COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} - MAIN_DEPENDENCY ${fullname} - DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" - ) - list(APPEND MATERIAL_BINS ${output_path}) -endforeach() - if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL) foreach (mat_src ${MATERIAL_FL0_SRCS}) get_filename_component(localname "${mat_src}" NAME_WE) diff --git a/filament/src/materials/blitLow1.mat b/filament/src/materials/blitLow1.mat deleted file mode 100644 index efe17433c53..00000000000 --- a/filament/src/materials/blitLow1.mat +++ /dev/null @@ -1,39 +0,0 @@ -material { - name : blitLow, - parameters : [ - { - type : sampler2d, - name : color, - precision: medium - }, - { - type : float4, - name : resolution, - precision: high - }, - { - type : float4, - name : viewport, - precision: high - } - ], - variables : [ - vertex - ], - depthWrite : false, - depthCulling : false, - domain: postprocess, -} - -vertex { - void postProcessVertex(inout PostProcessVertexInputs postProcess) { - postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw; - postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy); - } -} - -fragment { - void postProcess(inout PostProcessInputs postProcess) { - postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); - } -} diff --git a/libs/filagui/CMakeLists.txt b/libs/filagui/CMakeLists.txt index cb1024005e9..66058c4103c 100644 --- a/libs/filagui/CMakeLists.txt +++ b/libs/filagui/CMakeLists.txt @@ -35,19 +35,11 @@ set(MATERIAL_SRCS file(MAKE_DIRECTORY ${MATERIAL_DIR}) -# HACK: Pick between the normal and FL0-exclusionary variants of the materials -# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. foreach (mat_src ${MATERIAL_SRCS}) get_filename_component(localname "${mat_src}" NAME_WE) get_filename_component(fullname "${mat_src}" ABSOLUTE) set(output_path "${MATERIAL_DIR}/${localname}.filamat") - if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) - # Pick the non-FL0 variant instead. - string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") - string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") - endif() - add_custom_command( OUTPUT ${output_path} COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} diff --git a/libs/filagui/src/materials/uiBlit1.mat b/libs/filagui/src/materials/uiBlit1.mat deleted file mode 100644 index d0edc109d23..00000000000 --- a/libs/filagui/src/materials/uiBlit1.mat +++ /dev/null @@ -1,28 +0,0 @@ -material { - name : uiBlit, - parameters : [ - { - type : sampler2d, - name : albedo - } - ], - requires : [ - uv0, - color - ], - shadingModel : unlit, - culling : none, - depthCulling: false, - blending : transparent, -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - vec2 uv = getUV0(); - uv.y = 1.0 - uv.y; - vec4 albedo = texture(materialParams_albedo, uv); - material.baseColor = getColor() * albedo; - material.baseColor.rgb *= material.baseColor.a; - } -} diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 36c24c4161c..6e7c70a0e21 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -135,6 +135,7 @@ class UTILS_PUBLIC MaterialBuilderBase { Optimization mOptimization = Optimization::PERFORMANCE; bool mPrintShaders = false; bool mGenerateDebugInfo = false; + bool mIncludeEssl1 = true; utils::bitset32 mShaderModels; struct CodeGenParams { ShaderModel shaderModel; @@ -271,6 +272,9 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { MaterialBuilder& noSamplerValidation(bool enabled) noexcept; + //! Enable generation of ESSL 1.0 code in FL0 materials. + MaterialBuilder& includeEssl1(bool enabled) noexcept; + //! Set the name of this material. MaterialBuilder& name(const char* name) noexcept; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index d175c4e1c63..52cba77d6df 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -139,8 +139,9 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics, glTargetLanguage, effectiveFeatureLevel, }); - if (featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 - && shaderModel == ShaderModel::MOBILE) { + if (mIncludeEssl1 + && featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 + && shaderModel == ShaderModel::MOBILE) { // ESSL1 code may never be compiled to SPIR-V. mCodeGenPermutations.push_back({ shaderModel, @@ -1565,4 +1566,9 @@ MaterialBuilder& MaterialBuilder::noSamplerValidation(bool enabled) noexcept { return *this; } +MaterialBuilder& MaterialBuilder::includeEssl1(bool enabled) noexcept { + mIncludeEssl1 = enabled; + return *this; +} + } // namespace filamat diff --git a/tools/matc/src/matc/CommandlineConfig.cpp b/tools/matc/src/matc/CommandlineConfig.cpp index 5323fb87419..30d6114e99d 100644 --- a/tools/matc/src/matc/CommandlineConfig.cpp +++ b/tools/matc/src/matc/CommandlineConfig.cpp @@ -62,6 +62,9 @@ static void usage(char* name) { " MATC --api opengl --api metal ...\n\n" " --feature-level, -l\n" " Specify the maximum feature level allowed (default is 3).\n\n" + " --no-essl1, -1\n" + " Don't generate ESSL 1.0 code even for Feature Level 0 mobile shaders.\n" + " Shaders are still validated against ESSL 1.0.\n\n" " --define, -D\n" " Add a preprocessor define macro via =. defaults to 1 if omitted.\n" " Can be repeated to specify multiple definitions:\n" @@ -171,7 +174,7 @@ static void parseDefine(std::string defineString, Config::StringReplacementMap& } bool CommandlineConfig::parse() { - static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:OSEr:vV:gtwF"; + static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:OSEr:vV:gtwF1"; static const struct option OPTIONS[] = { { "help", no_argument, nullptr, 'h' }, { "license", no_argument, nullptr, 'L' }, @@ -187,6 +190,7 @@ bool CommandlineConfig::parse() { { "preprocessor-only", no_argument, nullptr, 'E' }, { "api", required_argument, nullptr, 'a' }, { "feature-level", required_argument, nullptr, 'l' }, + { "no-essl1", no_argument, nullptr, '1' }, { "define", required_argument, nullptr, 'D' }, { "template", required_argument, nullptr, 'T' }, { "reflect", required_argument, nullptr, 'r' }, @@ -270,6 +274,9 @@ bool CommandlineConfig::parse() { } break; } + case '1': + mIncludeEssl1 = false; + break; case 'D': parseDefine(arg, mDefines); break; diff --git a/tools/matc/src/matc/Config.h b/tools/matc/src/matc/Config.h index c5403d693d3..79c6786541a 100644 --- a/tools/matc/src/matc/Config.h +++ b/tools/matc/src/matc/Config.h @@ -119,6 +119,10 @@ class Config { return mNoSamplerValidation; } + bool includeEssl1() const noexcept { + return mIncludeEssl1; + } + filament::UserVariantFilterMask getVariantFilter() const noexcept { return mVariantFilter; } @@ -150,6 +154,7 @@ class Config { StringReplacementMap mDefines; StringReplacementMap mTemplateMap; filament::UserVariantFilterMask mVariantFilter = 0; + bool mIncludeEssl1 = true; }; } diff --git a/tools/matc/src/matc/MaterialCompiler.cpp b/tools/matc/src/matc/MaterialCompiler.cpp index d9920d4c059..b21f75f84c5 100644 --- a/tools/matc/src/matc/MaterialCompiler.cpp +++ b/tools/matc/src/matc/MaterialCompiler.cpp @@ -399,6 +399,7 @@ bool MaterialCompiler::run(const Config& config) { builder .noSamplerValidation(config.noSamplerValidation()) + .includeEssl1(config.includeEssl1()) .includeCallback(includer) .fileName(materialFilePath.getName().c_str()) .platform(config.getPlatform()) From f9def09d1758f3ef2cd531748922887a6402156e Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 16:02:41 -0800 Subject: [PATCH 06/16] Update NEW_RELEASE_NOTES.md --- NEW_RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index c3161a7700b..19887bf7a8b 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials +- matc: Enable preprocessor optimization of ESSL 1.0 shader code [⚠️ **Recompile materials**] From 7797f5af38f989cd888d5cf6ab20aedcfe746d90 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Thu, 16 Nov 2023 12:47:07 -0800 Subject: [PATCH 07/16] Incorporate feedback --- CMakeLists.txt | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bb1422da64..00e03a598fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -527,7 +527,7 @@ endif() # Disable ESSL 1.0 code generation. if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0) - set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) + set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) endif() # Enable debug info (preserves names in SPIR-V) diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 817429bb665..b4aaf54e4a2 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -397,6 +397,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { + // Full optimization blocked by upstream issue: + // https://github.com/KhronosGroup/SPIRV-Cross/issues/2223 preprocessOptimization(tShader, config, internalConfig); } else { fullOptimization(tShader, config, internalConfig); From 63db4f0bf09b64bcf9119a2bece98e4e9373e3db Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Thu, 16 Nov 2023 12:51:53 -0800 Subject: [PATCH 08/16] Fix typo in depth_main.fs --- shaders/src/depth_main.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 1632509f4aa..e50d7620f17 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -57,7 +57,7 @@ void main() { fragColor.xy = computeDepthMomentsVSM(depth); fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) -#if FILAMENT_EFFECTIVE_VERSION == 0 +#if FILAMENT_EFFECTIVE_VERSION == 100 outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; From 5aa15f061b43157d5f281217da669ab053aa5c87 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 Nov 2023 12:16:39 -0800 Subject: [PATCH 09/16] fix scissor/viewport state tracking --- filament/backend/src/opengl/OpenGLDriver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index cfe4cacde61..1c49dcc0ecf 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1969,6 +1969,13 @@ void OpenGLDriver::makeCurrent(Handle schDraw, Handle GLSwapChain* scRead = handle_cast(schRead); mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain); mCurrentDrawSwapChain = scDraw; + + // From the GL spec for glViewport and glScissor: + // When a GL context is first attached to a window, width and height are set to the + // dimensions of that window. + // So basically, our viewport/scissor can be reset to "something" here. + mContext.state.window.viewport = {}; + mContext.state.window.scissor = {}; } // ------------------------------------------------------------------------------------------------ From f2f4f556b5ae826dc2e38b6dc4487e610c4e0dd0 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 Nov 2023 09:44:03 -0800 Subject: [PATCH 10/16] fix a lot of issues with the backend tests With these fixes failing tests are: OpenGL: BackendTest.FeedbackLoops Metal: BackendTest.ColorResolve BackendTest.BufferObjectUpdateWithOffset BasicStencilBufferTest.StencilBufferMSAA Vulkan: Many failures still --- filament/backend/test/BackendTest.cpp | 4 +- filament/backend/test/test_Blit.cpp | 152 +++++------------- filament/backend/test/test_FeedbackLoops.cpp | 14 +- filament/backend/test/test_LoadImage.cpp | 146 +++++++++-------- filament/backend/test/test_ReadPixels.cpp | 18 +-- .../backend/test/test_RenderExternalImage.cpp | 12 +- 6 files changed, 144 insertions(+), 202 deletions(-) diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 70261e8bf83..2cb5f75b95e 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -86,9 +86,9 @@ void BackendTest::executeCommands() { } void BackendTest::flushAndWait() { - auto& api = getDriverApi(); - api.finish(); + getDriverApi().finish(); executeCommands(); + getDriver().purge(); } Handle BackendTest::createSwapChain() { diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index de4f5a66b34..3144071910f 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -193,60 +193,6 @@ static void createFaces(DriverApi& dapi, Handle texture, int baseWidt dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb)); } -TEST_F(BackendTest, CubemapMinify) { - auto& api = getDriverApi(); - - Handle texture = api.createTexture( - SamplerType::SAMPLER_CUBEMAP, 4, TextureFormat::RGBA8, 1, 256, 256, 1, - TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT); - - createFaces(api, texture, 256, 256, 0, float3(0.5, 0, 0)); - - const int srcLevel = 0; - const int dstLevel = 1; - - const TextureCubemapFace srcFace = TextureCubemapFace::NEGATIVE_Y; - const TextureCubemapFace dstFace = TextureCubemapFace::NEGATIVE_Y; - - const TargetBufferInfo srcInfo = { texture, srcLevel, +srcFace }; - const TargetBufferInfo dstInfo = { texture, dstLevel, +dstFace }; - - Handle srcRenderTarget; - srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR, - 256 >> srcLevel, 256 >> srcLevel, 1, { srcInfo }, {}, {}); - - Handle dstRenderTarget; - dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR, - 256 >> dstLevel, 256 >> dstLevel, 1, { dstInfo }, {}, {}); - - api.blit(TargetBufferFlags::COLOR0, dstRenderTarget, - {0, 0, 256 >> dstLevel, 256 >> dstLevel}, srcRenderTarget, - {0, 0, 256 >> srcLevel, 256 >> srcLevel}, SamplerMagFilter::LINEAR); - - // Push through an empty frame. Note that this test does not do - // makeCurrent / commit and is therefore similar to renderStandaloneView. - api.beginFrame(0, 0); - api.endFrame(0); - - // Grab a screenshot. - ScreenshotParams params { 256 >> dstLevel, 256 >> dstLevel, "CubemapMinify.png" }; - api.beginFrame(0, 0); - dumpScreenshot(api, dstRenderTarget, ¶ms); - api.endFrame(0); - - // Wait for the ReadPixels result to come back. - api.finish(); - executeCommands(); - getDriver().purge(); - - // Cleanup. - api.destroyTexture(texture); - api.destroyRenderTarget(srcRenderTarget); - api.destroyRenderTarget(dstRenderTarget); - executeCommands(); -} - - TEST_F(BackendTest, ColorMagnify) { auto& api = getDriverApi(); @@ -263,7 +209,7 @@ TEST_F(BackendTest, ColorMagnify) { api.makeCurrent(swapChain, swapChain); // Create a source texture. - Handle srcTexture = api.createTexture( + Handle const srcTexture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT); const bool flipY = sBackend == Backend::OPENGL; @@ -271,7 +217,7 @@ TEST_F(BackendTest, ColorMagnify) { createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY); // Create a destination texture. - Handle dstTexture = api.createTexture( + Handle const dstTexture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT); @@ -338,7 +284,7 @@ TEST_F(BackendTest, ColorMinify) { api.makeCurrent(swapChain, swapChain); // Create a source texture. - Handle srcTexture = api.createTexture( + Handle const srcTexture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT); const bool flipY = sBackend == Backend::OPENGL; @@ -346,7 +292,7 @@ TEST_F(BackendTest, ColorMinify) { createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY); // Create a destination texture. - Handle dstTexture = api.createTexture( + Handle const dstTexture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT); @@ -362,29 +308,20 @@ TEST_F(BackendTest, ColorMinify) { // Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT. const int srcLevel = 1; - api.blit(TargetBufferFlags::COLOR0, dstRenderTargets[0], - {0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTargets[srcLevel], - {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR); - - // Push through an empty frame to allow the texture to upload and the blit to execute. - api.beginFrame(0, 0); - api.commit(swapChain); - api.endFrame(0); + api.blit(TargetBufferFlags::COLOR0, + dstRenderTargets[0], {0, 0, kDstTexWidth, kDstTexHeight}, + srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, + SamplerMagFilter::LINEAR); // Grab a screenshot. ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" }; - api.beginFrame(0, 0); dumpScreenshot(api, dstRenderTargets[0], ¶ms); - api.commit(swapChain); - api.endFrame(0); // Wait for the ReadPixels result to come back. - api.finish(); - executeCommands(); - getDriver().purge(); + flushAndWait(); // Check if the image matches perfectly to our golden run. - const uint32_t expected = 0x7739bef5; + const uint32_t expected = 0xf3d9c53f; printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected); EXPECT_TRUE(params.pixelHashResult == expected); @@ -417,8 +354,6 @@ TEST_F(BackendTest, DepthMinify) { { ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); prog.uniformBlockBindings({{"params", 1}}); program = api.createProgram(std::move(prog)); } @@ -550,85 +485,82 @@ TEST_F(BackendTest, ColorResolve) { constexpr auto kColorTexFormat = TextureFormat::RGBA8; constexpr int kSampleCount = 4; - // Create a SwapChain and make it current. We don't really use it so the res doesn't matter. - auto swapChain = api.createSwapChainHeadless(256, 256, 0); - api.makeCurrent(swapChain, swapChain); - // Create a program. ProgramHandle program; { ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); prog.uniformBlockBindings({{"params", 1}}); program = api.createProgram(std::move(prog)); } // Create a VertexBuffer, IndexBuffer, and RenderPrimitive. - TrianglePrimitive* triangle = new TrianglePrimitive(api); + TrianglePrimitive const triangle(api); // Create 4-sample texture. - Handle srcColorTexture = api.createTexture( + Handle const srcColorTexture = api.createTexture( SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1, TextureUsage::COLOR_ATTACHMENT); // Create 1-sample texture. - Handle dstColorTexture = api.createTexture( + Handle const dstColorTexture = api.createTexture( SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT); // Create a 4-sample render target with the 4-sample texture. - Handle srcRenderTarget = api.createRenderTarget( - TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount,{{ srcColorTexture }}, {}, {}); + Handle const srcRenderTarget = api.createRenderTarget( + TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, + {{ srcColorTexture }}, {}, {}); // Create a 1-sample render target with the 1-sample texture. - Handle dstRenderTarget = api.createRenderTarget( - TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, {{ dstColorTexture }}, {}, {}); + Handle const dstRenderTarget = api.createRenderTarget( + TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, + {{ dstColorTexture }}, {}, {}); // Prep for rendering. RenderPassParams params = {}; params.flags.clear = TargetBufferFlags::COLOR; + params.flags.discardStart = TargetBufferFlags::ALL; + params.flags.discardEnd = TargetBufferFlags::NONE; params.clearColor = float4(1, 1, 0, 1); params.viewport.width = kSrcTexWidth; params.viewport.height = kSrcTexHeight; PipelineState state = {}; - state.rasterState.colorWrite = true; - state.rasterState.culling = RasterState::CullingMode::NONE; state.program = program; + state.rasterState.colorWrite = true; + state.rasterState.depthWrite = false; + state.rasterState.depthFunc = RasterState::DepthFunc::A; + state.rasterState.culling = CullingMode::NONE; + auto ubuffer = api.createBufferObject(sizeof(MaterialParams), BufferObjectBinding::UNIFORM, BufferUsage::STATIC); - api.makeCurrent(swapChain, swapChain); - api.beginFrame(0, 0); - api.bindUniformBuffer(0, ubuffer); - // Draw red triangle into srcRenderTarget. uploadUniforms(api, ubuffer, { .color = float4(1, 0, 0, 1), .scale = float4(1, 1, 0.5, 0), }); - api.beginRenderPass(srcRenderTarget, params); - api.draw(state, triangle->getRenderPrimitive(), 1); - api.endRenderPass(); + + // FIXME: on Metal this triangle is not drawn. Can't understand why. + api.beginFrame(0, 0); + api.beginRenderPass(srcRenderTarget, params); + api.bindUniformBuffer(0, ubuffer); + api.draw(state, triangle.getRenderPrimitive(), 1); + api.endRenderPass(); api.endFrame(0); // Resolve the MSAA render target into the single-sample render target. - api.blit(TargetBufferFlags::COLOR, dstRenderTarget, - {0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget, - {0, 0, kSrcTexWidth, kSrcTexHeight}, SamplerMagFilter::LINEAR); + api.blit(TargetBufferFlags::COLOR, + dstRenderTarget, {0, 0, kDstTexWidth, kDstTexHeight}, + srcRenderTarget, {0, 0, kSrcTexWidth, kSrcTexHeight}, + SamplerMagFilter::NEAREST); // Grab a screenshot. - ScreenshotParams sparams { kDstTexWidth, kDstTexHeight, "ColorResolve.png" }; - api.beginFrame(0, 0); + ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" }; dumpScreenshot(api, dstRenderTarget, &sparams); - api.commit(swapChain); - api.endFrame(0); // Wait for the ReadPixels result to come back. - api.finish(); - executeCommands(); - getDriver().purge(); + flushAndWait(); // Check if the image matches perfectly to our golden run. const uint32_t expected = 0xebfac2ef; @@ -640,11 +572,9 @@ TEST_F(BackendTest, ColorResolve) { api.destroyProgram(program); api.destroyTexture(srcColorTexture); api.destroyTexture(dstColorTexture); - api.destroySwapChain(swapChain); api.destroyRenderTarget(srcRenderTarget); api.destroyRenderTarget(dstRenderTarget); - delete triangle; - executeCommands(); + flushAndWait(); } TEST_F(BackendTest, DepthResolve) { @@ -667,8 +597,6 @@ TEST_F(BackendTest, DepthResolve) { { ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); prog.uniformBlockBindings({{"params", 1}}); program = api.createProgram(std::move(prog)); } diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index 24dd7b202a3..871aad05f42 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -50,7 +50,7 @@ layout(location = 0) out vec4 fragColor; // Filament's Vulkan backend requires a descriptor set index of 1 for all samplers. // This parameter is ignored for other backends. -layout(location = 0, set = 1) uniform sampler2D tex; +layout(location = 0, set = 1) uniform sampler2D test_tex; uniform Params { highp float fbWidth; @@ -61,7 +61,7 @@ uniform Params { void main() { vec2 fbsize = vec2(params.fbWidth, params.fbHeight); vec2 uv = (gl_FragCoord.xy + 0.5) / fbsize; - fragColor = textureLod(tex, uv, params.sourceLevel); + fragColor = textureLod(test_tex, uv, params.sourceLevel); })"; static uint32_t sPixelHashResult = 0; @@ -133,24 +133,24 @@ TEST_F(BackendTest, FeedbackLoops) { // Create a program. ProgramHandle program; { - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("backend_test_sib") + SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() + .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; + Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); prog.uniformBlockBindings({{"params", 1}}); program = api.createProgram(std::move(prog)); } - TrianglePrimitive triangle(getDriverApi()); + TrianglePrimitive const triangle(getDriverApi()); // Create a texture. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; - Handle texture = api.createTexture( + Handle const texture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage); // Create a RenderTarget for each miplevel. diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index 15c2fc19657..8e1d8f3b562 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -174,6 +174,41 @@ static const char* getSamplerTypeName(TextureFormat textureFormat) { default: return "sampler2D"; } + +} +static SamplerFormat getSamplerFormat(TextureFormat textureFormat) { + switch (textureFormat) { + case TextureFormat::R8UI: + case TextureFormat::R16UI: + case TextureFormat::RG8UI: + case TextureFormat::RGB8UI: + case TextureFormat::R32UI: + case TextureFormat::RG16UI: + case TextureFormat::RGBA8UI: + case TextureFormat::RGB16UI: + case TextureFormat::RG32UI: + case TextureFormat::RGBA16UI: + case TextureFormat::RGB32UI: + case TextureFormat::RGBA32UI: + return SamplerFormat::UINT; + + case TextureFormat::R8I: + case TextureFormat::R16I: + case TextureFormat::RG8I: + case TextureFormat::RGB8I: + case TextureFormat::R32I: + case TextureFormat::RG16I: + case TextureFormat::RGBA8I: + case TextureFormat::RGB16I: + case TextureFormat::RG32I: + case TextureFormat::RGBA16I: + case TextureFormat::RGB32I: + case TextureFormat::RGBA32I: + return SamplerFormat::INT; + + default: + return SamplerFormat::FLOAT; + } } TEST_F(BackendTest, UpdateImage2D) { @@ -232,9 +267,9 @@ TEST_F(BackendTest, UpdateImage2D) { // Test integer format uploads. // TODO: These cases fail on OpenGL and Vulkan. // TODO: These cases now also fail on Metal, but at some point previously worked. - // testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI); - // testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI); - // testCases.emplace_back("RGB_INTEGER, INT -> RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I); + testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI); + testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI); + testCases.emplace_back("RGB_INTEGER, INT -> RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I); // Test uploads with buffer padding. // TODO: Vulkan crashes with "Assertion failed: (offset + size <= allocationSize)" @@ -262,48 +297,45 @@ TEST_F(BackendTest, UpdateImage2D) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() + SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH }} ) .build(); - ProgramHandle program; - std::string fragment = stringReplace("{samplerType}", + + std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(t.textureFormat), fragmentTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; + Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); - program = api.createProgram(std::move(prog)); + ProgramHandle const program = api.createProgram(std::move(prog)); // Create a Texture. - auto usage = TextureUsage::SAMPLEABLE; - Handle texture = api.createTexture(SamplerType::SAMPLER_2D, 1, + auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE; + Handle const texture = api.createTexture(SamplerType::SAMPLER_2D, 1, t.textureFormat, 1, 512, 512, 1u, usage); // Upload some pixel data. if (t.uploadSubregions) { - const auto& pf = t.pixelFormat; - const auto& pt = t.pixelType; - PixelBufferDescriptor subregion1 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding); - PixelBufferDescriptor subregion2 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding); - PixelBufferDescriptor subregion3 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding); - PixelBufferDescriptor subregion4 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding); - api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1, std::move(subregion1)); - api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1, std::move(subregion2)); - api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1, std::move(subregion3)); - api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1, std::move(subregion4)); + api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1, + checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding)); + api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1, + checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding)); + api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1, + checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding)); + api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1, + checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding)); } else { - PixelBufferDescriptor descriptor - = checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding); - api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor)); + api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, + checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding)); } SamplerGroup samplers(1); - SamplerParams sparams = {}; - sparams.filterMag = SamplerMagFilter::LINEAR; - sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, sparams }); + samplers.setSampler(0, { texture, { + .filterMag = SamplerMagFilter::NEAREST, + .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST } }); + auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); @@ -313,7 +345,6 @@ TEST_F(BackendTest, UpdateImage2D) { readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash); - api.flush(); api.commit(swapChain); api.endFrame(0); @@ -321,25 +352,21 @@ TEST_F(BackendTest, UpdateImage2D) { api.destroySwapChain(swapChain); api.destroyRenderTarget(defaultRenderTarget); api.destroyTexture(texture); - - // This ensures all driver commands have finished before exiting the test. - api.finish(); } + api.finish(); api.stopCapture(); - executeCommands(); - - getDriver().purge(); + flushAndWait(); } TEST_F(BackendTest, UpdateImageSRGB) { auto& api = getDriverApi(); api.startCapture(); - PixelDataFormat pixelFormat = PixelDataFormat::RGB; - PixelDataType pixelType = PixelDataType::UBYTE; - TextureFormat textureFormat = TextureFormat::SRGB8_A8; + PixelDataFormat const pixelFormat = PixelDataFormat::RGBA; + PixelDataType const pixelType = PixelDataType::UBYTE; + TextureFormat const textureFormat = TextureFormat::SRGB8_A8; // Create a platform-specific SwapChain and make it current. auto swapChain = createSwapChain(); @@ -347,22 +374,22 @@ TEST_F(BackendTest, UpdateImageSRGB) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() + SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); - std::string fragment = stringReplace("{samplerType}", + std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; + Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); - ProgramHandle program = api.createProgram(std::move(prog)); + ProgramHandle const program = api.createProgram(std::move(prog)); // Create a texture. - Handle texture = api.createTexture(SamplerType::SAMPLER_2D, 1, - textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE); + Handle const texture = api.createTexture(SamplerType::SAMPLER_2D, 1, + textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE); // Create image data. size_t components; int bpp; @@ -402,7 +429,7 @@ TEST_F(BackendTest, UpdateImageSRGB) { renderTriangle(defaultRenderTarget, swapChain, program); - static const uint32_t expectedHash = 519370995; + static const uint32_t expectedHash = 359858623; readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash); api.flush(); @@ -416,12 +443,9 @@ TEST_F(BackendTest, UpdateImageSRGB) { // This ensures all driver commands have finished before exiting the test. api.finish(); - api.stopCapture(); - executeCommands(); - - getDriver().purge(); + flushAndWait(); } TEST_F(BackendTest, UpdateImageMipLevel) { @@ -443,20 +467,20 @@ TEST_F(BackendTest, UpdateImageMipLevel) { .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); - std::string fragment = stringReplace("{samplerType}", + std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentUpdateImageMip); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; + Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); - ProgramHandle program = api.createProgram(std::move(prog)); + ProgramHandle const program = api.createProgram(std::move(prog)); // Create a texture with 3 mip levels. // Base level: 1024 // Level 1: 512 <-- upload data and sample from this level // Level 2: 256 Handle texture = api.createTexture(SamplerType::SAMPLER_2D, 3, - textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE); + textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE); // Create image data. PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512); @@ -491,12 +515,9 @@ TEST_F(BackendTest, UpdateImageMipLevel) { // This ensures all driver commands have finished before exiting the test. api.finish(); - api.stopCapture(); - executeCommands(); - - getDriver().purge(); + flushAndWait(); } TEST_F(BackendTest, UpdateImage3D) { @@ -507,7 +528,7 @@ TEST_F(BackendTest, UpdateImage3D) { PixelDataType pixelType = PixelDataType::FLOAT; TextureFormat textureFormat = TextureFormat::RGBA16F; SamplerType samplerType = SamplerType::SAMPLER_2D_ARRAY; - TextureUsage usage = TextureUsage::SAMPLEABLE; + TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE; // Create a platform-specific SwapChain and make it current. auto swapChain = createSwapChain(); @@ -524,9 +545,9 @@ TEST_F(BackendTest, UpdateImage3D) { getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("tex"), 0 }; + Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); - ProgramHandle program = api.createProgram(std::move(prog)); + ProgramHandle const program = api.createProgram(std::move(prog)); // Create a texture. Handle texture = api.createTexture(samplerType, 1, @@ -578,12 +599,9 @@ TEST_F(BackendTest, UpdateImage3D) { // This ensures all driver commands have finished before exiting the test. api.finish(); - api.stopCapture(); - executeCommands(); - - getDriver().purge(); + flushAndWait(); } } // namespace test diff --git a/filament/backend/test/test_ReadPixels.cpp b/filament/backend/test/test_ReadPixels.cpp index 34b2a12337e..1f000c6e6aa 100644 --- a/filament/backend/test/test_ReadPixels.cpp +++ b/filament/backend/test/test_ReadPixels.cpp @@ -23,6 +23,7 @@ #include #include +#include using namespace filament; using namespace filament::backend; @@ -107,7 +108,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { // The size of the actual render target, taking mip level into account; size_t getRenderTargetSize () const { - return renderTargetBaseSize >> mipLevel; + return std::max(size_t(1), renderTargetBaseSize >> mipLevel); } // The rect that read pixels will read from. @@ -169,7 +170,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { // The normative read pixels test case. Render a white triangle over a blue background and read // the full viewport into a pixel buffer. - TestCase t; + TestCase const t0; // Check that a subregion of the render target can be read into a pixel buffer. TestCase t2; @@ -233,7 +234,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { t8.testName = "readPixels_swapchain"; t8.useDefaultRT = true; - TestCase testCases[] = { t, t2, t3, t4, t5, t6, t7, t8 }; + TestCase const testCases[] = { t0, t2, t3, t4, t5, t6, t7, t8 }; // Create programs. Handle programFloat, programUint; @@ -264,7 +265,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { // Create a Texture and RenderTarget to render into. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; - Handle texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, + Handle const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1, usage); @@ -282,7 +283,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { {}); } - TrianglePrimitive triangle(getDriverApi()); + TrianglePrimitive const triangle(getDriverApi()); RenderPassParams params = {}; fullViewport(params); @@ -356,7 +357,6 @@ TEST_F(ReadPixelsTest, ReadPixels) { getDriverApi().beginRenderPass(renderTarget, params); getDriverApi().endRenderPass(); - getDriverApi().flush(); getDriverApi().commit(swapChain); getDriverApi().endFrame(0); @@ -369,11 +369,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { getDriverApi().destroyProgram(programUint); // This ensures all driver commands have finished before exiting the test. - getDriverApi().finish(); - - executeCommands(); - - getDriver().purge(); + flushAndWait(); } TEST_F(ReadPixelsTest, ReadPixelsPerformance) { diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 27519a0a255..2039bb2aae7 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -45,10 +45,10 @@ std::string fragment (R"(#version 450 core layout(location = 0) out vec4 fragColor; layout(location = 0) in vec2 uv; -layout(location = 0, set = 1) uniform sampler2D tex; +layout(location = 0, set = 1) uniform sampler2D test_tex; void main() { - fragColor = texture(tex, uv); + fragColor = texture(test_tex, uv); } )"); @@ -66,7 +66,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { auto swapChain = createSwapChain(); SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("backend_test_sib") + .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) .add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); @@ -74,7 +74,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { // Create a program that samples a texture. Program p = shaderGen.getProgram(getDriverApi()); - Program::Sampler sampler { utils::CString("tex"), 0 }; + Program::Sampler sampler { utils::CString("test_tex"), 0 }; p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1); backend::Handle program = getDriverApi().createProgram(std::move(p)); @@ -146,7 +146,7 @@ TEST_F(BackendTest, RenderExternalImage) { auto swapChain = createSwapChain(); SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("backend_test_sib") + .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) .add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); @@ -154,7 +154,7 @@ TEST_F(BackendTest, RenderExternalImage) { // Create a program that samples a texture. Program p = shaderGen.getProgram(getDriverApi()); - Program::Sampler sampler { utils::CString("tex"), 0 }; + Program::Sampler sampler { utils::CString("test_tex"), 0 }; p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1); auto program = getDriverApi().createProgram(std::move(p)); From 2bd48f58ff57c7757cf0c778708af7ce40f77de2 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Tue, 7 Nov 2023 13:03:24 -0800 Subject: [PATCH 11/16] Enable optimizations for ESSL 1.0 code The CL introducing the ESSL 1.0 chunk in materials inadvertently disabled optimizations for said code. This commit reintroduces those optimizations and fixes associated bugs which manifested. In particular, spirv-cross was generating uints for bools; this has been fixed with a hack. Additionally, spirv-cross is now compiled with exceptions enabled so that matc can gracefully fail and show the code which failed to compile rather than abruptly aborting. --- CMakeLists.txt | 6 ++++++ NEW_RELEASE_NOTES.md | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 21 ++++++++++++++------- libs/filamat/src/GLSLPostProcessor.h | 2 +- libs/filamat/src/MaterialBuilder.cpp | 9 ++++----- libs/filamat/src/shaders/CodeGenerator.cpp | 16 ++++++++++------ third_party/spirv-cross/spirv_glsl.cpp | 13 +++++++++++-- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00e03a598fe..33c2bb29685 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,9 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING "Size of the Metal handle arena, default 8." ) +# Enable exceptions by default in spirv-cross. +set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF) + # ================================================================================================== # CMake policies # ================================================================================================== @@ -339,6 +342,7 @@ endif() if (CYGWIN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() if (MSVC) @@ -375,6 +379,7 @@ endif() # saved by -fno-exception and 10 KiB saved by -fno-rtti). if (ANDROID OR IOS OR WEBGL) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) if (ANDROID OR WEBGL) # Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS @@ -386,6 +391,7 @@ endif() # std::visit, which is not supported on iOS 11.0 when exceptions are enabled. if (IOS) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() # With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 19887bf7a8b..d000a526635 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -9,4 +9,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials -- matc: Enable preprocessor optimization of ESSL 1.0 shader code [⚠️ **Recompile materials**] +- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**] diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index b4aaf54e4a2..7c1c4138fd7 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -33,6 +33,7 @@ #include "MetalArgumentBuffer.h" #include "SpirvFixup.h" +#include "utils/ostream.h" #include @@ -396,12 +397,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { - // Full optimization blocked by upstream issue: - // https://github.com/KhronosGroup/SPIRV-Cross/issues/2223 - preprocessOptimization(tShader, config, internalConfig); - } else { - fullOptimization(tShader, config, internalConfig); + if (!fullOptimization(tShader, config, internalConfig)) { + return false; } break; } @@ -485,7 +482,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, } } -void GLSLPostProcessor::fullOptimization(const TShader& tShader, +bool GLSLPostProcessor::fullOptimization(const TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const { SpirvBlob spirv; @@ -553,7 +550,16 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, } } +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS *internalConfig.glslOutput = glslCompiler.compile(); +#else + try { + *internalConfig.glslOutput = glslCompiler.compile(); + } catch (spirv_cross::CompilerError e) { + slog.e << "ERROR: " << e.what() << io::endl; + return false; + } +#endif // spirv-cross automatically redeclares gl_ClipDistance if it's used. Some drivers don't // like this, so we simply remove it. @@ -566,6 +572,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, str.replace(found, clipDistanceDefinition.length(), ""); } } + return true; } std::shared_ptr GLSLPostProcessor::createOptimizer( diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 8e3c2e1e4ac..c13dece6369 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -93,7 +93,7 @@ class GLSLPostProcessor { ShaderMinifier minifier; }; - void fullOptimization(const glslang::TShader& tShader, + bool fullOptimization(const glslang::TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const; void preprocessOptimization(glslang::TShader& tShader, diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 52cba77d6df..a95bb077819 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -142,11 +142,10 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics, if (mIncludeEssl1 && featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 && shaderModel == ShaderModel::MOBILE) { - // ESSL1 code may never be compiled to SPIR-V. mCodeGenPermutations.push_back({ shaderModel, TargetApi::OPENGL, - TargetLanguage::GLSL, + glTargetLanguage, filament::backend::FeatureLevel::FEATURE_LEVEL_0 }); } @@ -1422,15 +1421,15 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& uniforms.push_back({ "objectUniforms.data[0].morphTargetCount", offsetof(PerRenderableUib, data[0].morphTargetCount), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].flagsChannels", offsetof(PerRenderableUib, data[0].flagsChannels), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].objectId", offsetof(PerRenderableUib, data[0].objectId), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].userData", offsetof(PerRenderableUib, data[0].userData), 1, diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index a5b48ff7c8b..2c20307b7f4 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -301,8 +301,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << '\n'; out << SHADERS_COMMON_DEFINES_GLSL_DATA; - if (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 && - material.featureLevel == FeatureLevel::FEATURE_LEVEL_0) { + if (material.featureLevel == FeatureLevel::FEATURE_LEVEL_0 && + (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 + || mTargetLanguage == TargetLanguage::SPIRV)) { // Insert compatibility definitions for ESSL 1.0 functions which were removed in ESSL 3.0. // This is the minimum required value according to the OpenGL ES Shading Language Version @@ -490,11 +491,14 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, const char* materialTypeString = getOutputTypeName(materialOutputType); const char* typeString = getOutputTypeName(outputType); + bool generate_essl3_code = mTargetLanguage == TargetLanguage::SPIRV + || mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1; + out << "\n#define FRAG_OUTPUT" << index << " " << name.c_str(); - if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) { - out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; - } else { + if (generate_essl3_code) { out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str(); + } else { + out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; } out << "\n#define FRAG_OUTPUT_MATERIAL_TYPE" << index << " " << materialTypeString; out << "\n#define FRAG_OUTPUT_PRECISION" << index << " " << precisionString; @@ -502,7 +506,7 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, out << "\n#define FRAG_OUTPUT_SWIZZLE" << index << " " << swizzleString; out << "\n"; - if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + if (generate_essl3_code) { out << "\nlayout(location=" << index << ") out " << precisionString << " " << typeString << " output_" << name.c_str() << ";\n"; } diff --git a/third_party/spirv-cross/spirv_glsl.cpp b/third_party/spirv-cross/spirv_glsl.cpp index 0d63d35f8f2..e0b39f95bb7 100644 --- a/third_party/spirv-cross/spirv_glsl.cpp +++ b/third_party/spirv-cross/spirv_glsl.cpp @@ -14977,7 +14977,12 @@ string CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset { auto &execution = get_entry_point(); - if (flags.get(DecorationRelaxedPrecision)) + if (type.basetype == SPIRType::UInt && is_legacy_es()) + { + // HACK: This is a bool. See comment in type_to_glsl(). + qual += "lowp "; + } + else if (flags.get(DecorationRelaxedPrecision)) { bool implied_fmediump = type.basetype == SPIRType::Float && options.fragment.default_float_precision == Options::Mediump && @@ -15503,7 +15508,11 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) if (type.basetype == SPIRType::UInt && is_legacy()) { if (options.es) - SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy ESSL."); + // HACK: spirv-cross changes bools into uints and generates code which compares them to + // zero. Input code will have already been validated as not to have contained any uints, + // so any remaining uints must in fact be bools. However, simply returning "bool" here + // will result in invalid code. Instead, return an int. + return backend.basic_int_type; else require_extension_internal("GL_EXT_gpu_shader4"); } From 47d45a64fafb7101fd9d766221484bb358d08f32 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 27 Nov 2023 11:40:00 -0800 Subject: [PATCH 12/16] Update MATERIAL_VERSION to 47 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index b6d677a4c94..67cdd8620f9 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 46; +static constexpr size_t MATERIAL_VERSION = 47; /** * Supported shading models From 000bff5ce1d69263da5d8ee68f4442bccce107b7 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 27 Nov 2023 14:18:35 -0800 Subject: [PATCH 13/16] Release Filament 1.47.0 --- NEW_RELEASE_NOTES.md | 3 --- README.md | 4 ++-- RELEASE_NOTES.md | 6 ++++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index d000a526635..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,6 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials -- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**] diff --git a/README.md b/README.md index 3090b1fd8a1..b0278db43f3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.46.0' + implementation 'com.google.android.filament:filament-android:1.47.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.46.0' +pod 'Filament', '~> 1.47.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a87c44268c7..3459c5001f8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,7 +7,13 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.48.0 + +- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials +- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**] + ## v1.47.0 + - engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] diff --git a/android/gradle.properties b/android/gradle.properties index d4b8dc0c94f..6dda5b45e97 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.46.0 +VERSION_NAME=1.47.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 19007b27293..24db7659fb0 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.46.0" + spec.version = "1.47.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.46.0/filament-v1.46.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.47.0/filament-v1.47.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index d91ac748924..ef4cd7e79d9 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.46.0", + "version": "1.47.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 3c3296a11464b8cb97a962f1494b95069e7f60d0 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 27 Nov 2023 14:22:16 -0800 Subject: [PATCH 14/16] Bump version to 1.48.0 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b0278db43f3..50bcf1ecec1 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.47.0' + implementation 'com.google.android.filament:filament-android:1.48.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.47.0' +pod 'Filament', '~> 1.48.0' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 6dda5b45e97..db2639526bc 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.47.0 +VERSION_NAME=1.48.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 24db7659fb0..3d4af657f04 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.47.0" + spec.version = "1.48.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.47.0/filament-v1.47.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.48.0/filament-v1.48.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index ef4cd7e79d9..b97c7364479 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.47.0", + "version": "1.48.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From a01d282f14a50514e88930c031d9ac60d7f15cb9 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 28 Nov 2023 10:23:31 -0800 Subject: [PATCH 15/16] Add intermediate.h include to builtinResource.h (#7388) --- libs/filamat/src/sca/builtinResource.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/filamat/src/sca/builtinResource.h b/libs/filamat/src/sca/builtinResource.h index 53ef69ccc4c..d58f2a9d59a 100644 --- a/libs/filamat/src/sca/builtinResource.h +++ b/libs/filamat/src/sca/builtinResource.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "glslang/Include/intermediate.h" + const TBuiltInResource DefaultTBuiltInResource = { /* .MaxLights = */ 32, /* .MaxClipPlanes = */ 6, From 349bf7be387e7475ac05fead7af3935e865249c3 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 1 Dec 2023 11:00:53 -0800 Subject: [PATCH 16/16] Update MATERIAL_VERSION to 48 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 67cdd8620f9..6a74ac7e3a9 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 47; +static constexpr size_t MATERIAL_VERSION = 48; /** * Supported shading models