Compare commits

..
Author SHA1 Message Date
Eliza Velasquez ab552d3619 wip: color space stuff 2023-11-13 14:32:31 -08:00
242 changed files with 1160 additions and 2300 deletions
-15
View File
@@ -41,8 +41,6 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
@@ -67,9 +65,6 @@ 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
# ==================================================================================================
@@ -342,7 +337,6 @@ endif()
if (CYGWIN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
endif()
if (MSVC)
@@ -379,7 +373,6 @@ 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
@@ -391,7 +384,6 @@ 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
@@ -531,11 +523,6 @@ 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)
@@ -743,8 +730,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
add_subdirectory(${LIBRARIES}/bluevk)
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
add_subdirectory(${EXTERNAL}/spirv-headers)
endif()
set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
+8
View File
@@ -8,3 +8,11 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
- filagui: Add support for feature level 0
- matc: Add support for post-process materials in feature level 0
- engine: Add `Material::getFeatureLevel()`
- engine: Add missing `Material::getReflectionMode()` method in Java
- engine: Support basic usage of post-processing materials on feature level 0
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.49.0'
implementation 'com.google.android.filament:filament-android:1.45.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.49.0'
pod 'Filament', '~> 1.45.0'
```
### Snapshots
-29
View File
@@ -7,35 +7,6 @@ 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.49.0
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
- matc: Generate skinning variants for FL0 materials [⚠️ **Recompile materials**]
## 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**]
## v1.46.0
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
- filagui: Add support for feature level 0
- matc: Add support for post-process materials in feature level 0
- engine: Add `Material::getFeatureLevel()`
- engine: Add missing `Material::getReflectionMode()` method in Java
- engine: Support basic usage of post-processing materials on feature level 0
- engine: Fix critical GLES 2.0 bugs
- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament
without FL0 support.
## v1.45.1
- engine: Added parameter for configuring JobSystem thread count
@@ -384,13 +384,6 @@ Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass,
engine->flushAndWait();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
engine->flush();
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -435,13 +428,6 @@ 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) {
@@ -484,7 +470,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 stereoscopicEyeCount) {
jlong jobSystemThreadCount) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -493,7 +479,6 @@ 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);
}
@@ -38,7 +38,7 @@ Java_com_google_android_filament_EntityManager_nCreateArray(JNIEnv* env, jclass,
// (which it is), but still.
em->create((size_t) n, reinterpret_cast<Entity *>(entities));
env->ReleaseIntArrayElements(entities_, entities, JNI_ABORT);
env->ReleaseIntArrayElements(entities_, entities, 0);
}
extern "C" JNIEXPORT jint JNICALL
@@ -19,7 +19,6 @@
#include <filament/Material.h>
#include "common/NioUtils.h"
#include "common/CallbackUtils.h"
using namespace filament;
@@ -272,17 +271,3 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
env->ReleaseStringUTFChars(name_, name);
return (jboolean) hasParameter;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
jlong nativeMaterial, jint priority, jint variants, jobject handler, jobject runnable) {
Material* material = (Material*) nativeMaterial;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
material->compile(
(Material::CompilerPriorityQueue) priority,
(UserVariantFilterBit) variants,
jniCallback->getHandler(), [jniCallback](Material*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
}
@@ -205,7 +205,7 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
break;
}
env->ReleaseIntArrayElements(v_, v, JNI_ABORT);
env->ReleaseIntArrayElements(v_, v, 0);
env->ReleaseStringUTFChars(name_, name);
}
@@ -98,22 +98,3 @@ Java_com_google_android_filament_Scene_nHasEntity(JNIEnv *env, jclass type, jlon
Entity entity = Entity::import(entityId);
return (jboolean) scene->hasEntity(entity);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Scene_nGetEntities(JNIEnv *env, jclass ,
jlong nativeScene, jintArray outArray, jint length) {
Scene const* const scene = (Scene*) nativeScene;
if (length < scene->getEntityCount()) {
// should not happen because we already checked on the java side
return JNI_FALSE;
}
jint *out = (jint *) env->GetIntArrayElements(outArray, nullptr);
scene->forEach([out, length, i = 0](Entity entity)mutable {
if (i < length) { // this is just paranoia here
out[i++] = (jint) entity.getId();
}
});
env->ReleaseIntArrayElements(outArray, (jint*) out, 0);
return JNI_TRUE;
}
@@ -480,17 +480,6 @@ 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,
@@ -111,8 +111,6 @@ public class Engine {
private long mNativeObject;
private Config mConfig;
@NonNull private final TransformManager mTransformManager;
@NonNull private final LightManager mLightManager;
@NonNull private final RenderableManager mRenderableManager;
@@ -152,10 +150,8 @@ public class Engine {
FEATURE_LEVEL_0,
/** OpenGL ES 3.0 features (default) */
FEATURE_LEVEL_1,
/** OpenGL ES 3.1 features + 16 textures units + cubemap arrays */
FEATURE_LEVEL_2,
/** OpenGL ES 3.1 features + 31 textures units + cubemap arrays */
FEATURE_LEVEL_3,
FEATURE_LEVEL_2
};
/**
@@ -165,7 +161,6 @@ public class Engine {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
private Config mConfig;
public Builder() {
mNativeBuilder = nCreateBuilder();
@@ -207,11 +202,10 @@ public class Engine {
* @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.stereoscopicEyeCount);
config.jobSystemThreadCount);
return this;
}
@@ -239,7 +233,7 @@ public class Engine {
public Engine build() {
long nativeEngine = nBuilderBuild(mNativeBuilder);
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine, mConfig);
return new Engine(nativeEngine);
}
private static class BuilderFinalizer {
@@ -347,24 +341,14 @@ public class Engine {
* 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, Config config) {
private Engine(long nativeEngine) {
mNativeObject = nativeEngine;
mTransformManager = new TransformManager(nGetTransformManager(nativeEngine));
mLightManager = new LightManager(nGetLightManager(nativeEngine));
mRenderableManager = new RenderableManager(nGetRenderableManager(nativeEngine));
mEntityManager = new EntityManager(nGetEntityManager(nativeEngine));
mConfig = config;
}
/**
@@ -557,37 +541,6 @@ public class Engine {
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
@@ -1124,18 +1077,6 @@ public class Engine {
nFlushAndWait(getNativeObject());
}
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait
* for commands to be either executed or the hardware finished.
*
* <p>This is typically used after creating a lot of objects to start draining the command
* queue which has a limited size.</p>
*/
public void flush() {
nFlush(getNativeObject());
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
if (mNativeObject == 0) {
@@ -1208,7 +1149,6 @@ public class Engine {
private static native boolean nIsValidSwapChain(long nativeEngine, long nativeSwapChain);
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native void nFlush(long nativeEngine);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
@@ -1216,7 +1156,6 @@ public class Engine {
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);
@@ -1226,8 +1165,7 @@ public class Engine {
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 stereoscopicEyeCount);
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount);
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);
@@ -18,7 +18,6 @@ package com.google.android.filament;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
import com.google.android.filament.proguard.UsedByNative;
@@ -239,31 +238,6 @@ public class Material {
FRONT_AND_BACK
}
public enum CompilerPriorityQueue {
HIGH,
LOW
}
public static class UserVariantFilterBit {
/** Directional lighting */
public static int DIRECTIONAL_LIGHTING = 0x01;
/** Dynamic lighting */
public static int DYNAMIC_LIGHTING = 0x02;
/** Shadow receiver */
public static int SHADOW_RECEIVER = 0x04;
/** Skinning */
public static int SKINNING = 0x08;
/** Fog */
public static int FOG = 0x10;
/** Variance shadow maps */
public static int VSM = 0x20;
/** Screen-space reflections */
public static int SSR = 0x40;
/** Instanced stereo rendering */
public static int STE = 0x80;
public static int ALL = 0xFF;
}
@UsedByNative("Material.cpp")
public static class Parameter {
private static final Type[] sTypeValues = Type.values();
@@ -378,55 +352,6 @@ public class Material {
}
}
/**
* Asynchronously ensures that a subset of this Material's variants are compiled. After issuing
* several compile() calls in a row, it is recommended to call {@link Engine#flush}
* such that the backend can start the compilation work as soon as possible.
* The provided callback is guaranteed to be called on the main thread after all specified
* variants of the material are compiled. This can take hundreds of milliseconds.
*<p>
* If all the material's variants are already compiled, the callback will be scheduled as
* soon as possible, but this might take a few dozen millisecond, corresponding to how
* many previous frames are enqueued in the backend. This also varies by backend. Therefore,
* it is recommended to only call this method once per material shortly after creation.
*</p>
*<p>
* If the same variant is scheduled for compilation multiple times, the first scheduling
* takes precedence; later scheduling are ignored.
*</p>
*<p>
* caveat: A consequence is that if a variant is scheduled on the low priority queue and later
* scheduled again on the high priority queue, the later scheduling is ignored.
* Therefore, the second callback could be called before the variant is compiled.
* However, the first callback, if specified, will trigger as expected.
*</p>
*<p>
* The callback is guaranteed to be called. If the engine is destroyed while some material
* variants are still compiling or in the queue, these will be discarded and the corresponding
* callback will be called. In that case however the Material pointer passed to the callback
* is guaranteed to be invalid (either because it's been destroyed by the user already, or,
* because it's been cleaned-up by the Engine).
*</p>
*<p>
* {@link UserVariantFilterBit#ALL} should be used with caution. Only variants that an application
* needs should be included in the variants argument. For example, the STE variant is only used
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
* should be turned off to avoid unnecessary material compilations.
*</p>
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*/
public void compile(@NonNull CompilerPriorityQueue priority,
int variants,
@Nullable Object handler,
@Nullable Runnable callback) {
nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback);
}
/**
* Creates a new instance of this material. Material instances should be freed using
* {@link Engine#destroyMaterialInstance(MaterialInstance)}.
@@ -1028,7 +953,6 @@ public class Material {
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
private static native long nGetDefaultInstance(long nativeMaterial);
private static native void nCompile(long nativeMaterial, int priority, int variants, Object handler, Runnable runnable);
private static native String nGetName(long nativeMaterial);
private static native int nGetShading(long nativeMaterial);
private static native int nGetInterpolation(long nativeMaterial);
@@ -16,7 +16,6 @@
package com.google.android.filament;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
@@ -191,52 +190,6 @@ public class Scene {
return mNativeObject;
}
/**
* Returns the list of all entities in the Scene. If outArray is provided and large enough,
* it is used to store the list and returned, otherwise a new array is allocated and returned.
* @param outArray an array to store the list of entities in the scene.
* @return outArray if it was used or a newly allocated array.
* @see #getEntityCount
*/
public int[] getEntities(@Nullable int[] outArray) {
int c = getEntityCount();
if (outArray == null || outArray.length < c) {
outArray = new int[c];
}
boolean success = nGetEntities(getNativeObject(), outArray, outArray.length);
if (!success) {
throw new IllegalStateException("Error retriving Scene's entities");
}
return outArray;
}
/**
* Returns the list of all entities in the Scene in a newly allocated array.
* @return an array containing the list of all entities in the scene.
* @see #getEntityCount
*/
public int[] getEntities() {
return getEntities(null);
}
public interface EntityProcessor {
void process(@Entity int entity);
}
/**
* Invokes user functor on each entity in the scene.
*
* It is not allowed to add or remove an entity from the scene within the functor.
*
* @param entityProcessor User provided functor called for each entity in the scene
*/
public void forEach(@NonNull EntityProcessor entityProcessor) {
int[] entities = getEntities(null);
for (int entity : entities) {
entityProcessor.process(entity);
}
}
void clearNativeObject() {
mNativeObject = 0;
}
@@ -251,5 +204,4 @@ public class Scene {
private static native int nGetRenderableCount(long nativeScene);
private static native int nGetLightCount(long nativeScene);
private static native boolean nHasEntity(long nativeScene, int entity);
private static native boolean nGetEntities(long nativeScene, int[] outArray, int length);
}
@@ -75,7 +75,6 @@ public class View {
private AmbientOcclusionOptions mAmbientOcclusionOptions;
private BloomOptions mBloomOptions;
private FogOptions mFogOptions;
private StereoscopicOptions mStereoscopicOptions;
private RenderTarget mRenderTarget;
private BlendMode mBlendMode;
private DepthOfFieldOptions mDepthOfFieldOptions;
@@ -1056,51 +1055,6 @@ public class View {
return nIsStencilBufferEnabled(getNativeObject());
}
/**
* Sets the stereoscopic rendering options for this view.
*
* <p>
* 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.
* </p>
*
* <p>
* Currently, the following features are not supported with stereoscopic rendering:
* - post-processing
* - shadowing
* - punctual lights
* </p>
*
* <p>
* 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.
* </p>
*
* @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
*/
@@ -1266,7 +1220,6 @@ public class View {
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);
@@ -182,85 +182,28 @@ public class UiHelper {
void detach();
}
private class SurfaceViewHandler implements RenderSurface, SurfaceHolder.Callback {
@NonNull private final SurfaceView mSurfaceView;
private static class SurfaceViewHandler implements RenderSurface {
private final SurfaceView mSurfaceView;
SurfaceViewHandler(@NonNull SurfaceView surfaceView) {
mSurfaceView = surfaceView;
@NonNull SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceViewHandler(SurfaceView surface) {
mSurfaceView = surface;
}
@Override
public void resize(int width, int height) {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.setFixedSize(width, height);
mSurfaceView.getHolder().setFixedSize(width, height);
}
@Override
public void detach() {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class SurfaceHolderHandler implements RenderSurface, SurfaceHolder.Callback {
private static class SurfaceHolderHandler implements RenderSurface {
private final SurfaceHolder mSurfaceHolder;
SurfaceHolderHandler(@NonNull SurfaceHolder holder) {
mSurfaceHolder = holder;
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceHolderHandler(SurfaceHolder surface) {
mSurfaceHolder = surface;
}
@Override
@@ -270,55 +213,19 @@ public class UiHelper {
@Override
public void detach() {
mSurfaceHolder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class TextureViewHandler implements RenderSurface, TextureView.SurfaceTextureListener {
private class TextureViewHandler implements RenderSurface {
private final TextureView mTextureView;
private Surface mSurface;
TextureViewHandler(@NonNull TextureView view) {
mTextureView = view;
mTextureView.setSurfaceTextureListener(this);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
if (surfaceTexture != null) {
this.onSurfaceTextureAvailable(surfaceTexture,
mDesiredWidth, mDesiredHeight);
}
}
}
TextureViewHandler(TextureView surface) { mTextureView = surface; }
@Override
public void resize(int width, int height) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture != null) {
surfaceTexture.setDefaultBufferSize(width, height);
}
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
}
if (mRenderCallback != null) {
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
@@ -328,69 +235,10 @@ public class UiHelper {
@Override
public void detach() {
mTextureView.setSurfaceTextureListener(null);
setSurface(null);
}
@Override
public void onSurfaceTextureAvailable(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
final Surface surface = new Surface(surfaceTexture);
setSurface(surface);
createSwapChain(surface);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mRenderCallback != null) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
final Surface surface = getSurface();
if (surface != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
}
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
private void setSurface(@Nullable Surface surface) {
void setSurface(Surface surface) {
if (surface == null) {
if (mSurface != null) {
mSurface.release();
@@ -399,7 +247,7 @@ public class UiHelper {
mSurface = surface;
}
private Surface getSurface() {
public Surface getSurface() {
return mSurface;
}
}
@@ -554,8 +402,48 @@ public class UiHelper {
view.setZOrderOnTop(translucent);
}
view.getHolder().setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
view.getHolder().setFormat(format);
mRenderSurface = new SurfaceViewHandler(view);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
SurfaceHolder holder = view.getHolder();
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}
@@ -567,7 +455,76 @@ public class UiHelper {
public void attachTo(@NonNull TextureView view) {
if (attach(view)) {
view.setOpaque(isOpaque());
mRenderSurface = new TextureViewHandler(view);
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
Surface surface = new Surface(surfaceTexture);
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
textureViewHandler.setSurface(surface);
createSwapChain(surface);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mRenderCallback != null) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
mRenderCallback.onNativeWindowChanged(textureViewHandler.getSurface());
}
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
};
view.setSurfaceTextureListener(listener);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
if (surfaceTexture != null) {
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
}
}
}
}
@@ -578,8 +535,46 @@ public class UiHelper {
*/
public void attachTo(@NonNull SurfaceHolder holder) {
if (attach(holder)) {
holder.setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
holder.setFormat(format);
mRenderSurface = new SurfaceHolderHandler(holder);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.49.0
VERSION_NAME=1.45.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -26,7 +26,6 @@ import android.widget.TextView
import android.widget.Toast
import com.google.android.filament.Fence
import com.google.android.filament.IndirectLight
import com.google.android.filament.Material
import com.google.android.filament.Skybox
import com.google.android.filament.View
import com.google.android.filament.View.OnPickCallback
@@ -393,36 +392,6 @@ class MainActivity : Activity() {
Log.i(TAG, "The Filament backend took $total ms to load the model geometry.")
modelViewer.engine.destroyFence(it)
loadStartFence = null
val materials = mutableSetOf<Material>()
val rcm = modelViewer.engine.renderableManager
modelViewer.scene.forEach {
val entity = it
if (rcm.hasComponent(entity)) {
val ri = rcm.getInstance(entity)
val c = rcm.getPrimitiveCount(ri)
for (i in 0 until c) {
val mi = rcm.getMaterialInstanceAt(ri, i)
val ma = mi.material
materials.add(ma)
}
}
}
materials.forEach {
it.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.DIRECTIONAL_LIGHTING or
Material.UserVariantFilterBit.DYNAMIC_LIGHTING or
Material.UserVariantFilterBit.SHADOW_RECEIVER,
null, null)
it.compile(
Material.CompilerPriorityQueue.LOW,
Material.UserVariantFilterBit.FOG or
Material.UserVariantFilterBit.SKINNING or
Material.UserVariantFilterBit.SSR or
Material.UserVariantFilterBit.VSM,
null, null)
}
}
}
@@ -20,8 +20,6 @@ import android.animation.ValueAnimator
import android.app.Activity
import android.opengl.Matrix
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceView
@@ -159,14 +157,6 @@ class MainActivity : Activity() {
private fun loadMaterial() {
readUncompressedAsset("materials/baked_color.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
material.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.ALL,
Handler(Looper.getMainLooper())) {
android.util.Log.i("hellotriangle",
"Material " + material.name + " compiled.")
}
engine.flush()
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ function replace {
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
local FILE_NAME="$2"
if [ $IS_DARWIN == 1 ]; then
if [ IS_DARWIN ]; then
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
else
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -28
View File
@@ -218,6 +218,7 @@ set(MATERIAL_SRCS
src/materials/colorGrading/customResolveAsSubpass.mat
src/materials/debugShadowCascades.mat
src/materials/defaultMaterial.mat
src/materials/defaultMaterial0.mat
src/materials/dof/dof.mat
src/materials/dof/dofCoc.mat
src/materials/dof/dofDownsample.mat
@@ -228,6 +229,7 @@ set(MATERIAL_SRCS
src/materials/dof/dofMipmap.mat
src/materials/dof/dofMedian.mat
src/materials/flare/flare.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
@@ -236,18 +238,13 @@ set(MATERIAL_SRCS
src/materials/ssao/bilateralBlurBentNormals.mat
src/materials/ssao/mipmapDepth.mat
src/materials/skybox.mat
src/materials/skybox0.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/separableGaussianBlur.mat
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/vsmMipmap.mat
src/materials/blitLow.mat
)
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
# Embed the binary resource blob for materials.
@@ -274,11 +271,6 @@ if (NOT DFG_LUT_SIZE)
endif()
message(STATUS "DFG LUT size set to ${DFG_LUT_SIZE}x${DFG_LUT_SIZE}")
# Whether to include FL0 materials.
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -320,23 +312,6 @@ foreach (mat_src ${MATERIAL_SRCS})
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)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_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 ()
endif ()
# Additional dependencies on included files for materials
add_custom_command(
+16 -31
View File
@@ -169,8 +169,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/spirv/VulkanSpirvUtils.cpp
src/vulkan/spirv/VulkanSpirvUtils.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
@@ -315,7 +313,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
endif()
if (FILAMENT_SUPPORTS_METAL)
@@ -400,8 +397,8 @@ install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
# ==================================================================================================
option(INSTALL_BACKEND_TEST "Install the backend test library so it can be consumed on iOS" OFF)
if (APPLE OR LINUX)
set(BACKEND_TEST_SRC
if (APPLE)
add_library(backend_test STATIC
test/BackendTest.cpp
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
@@ -413,26 +410,19 @@ if (APPLE OR LINUX)
test/test_BufferUpdates.cpp
test/test_MRT.cpp
test/test_LoadImage.cpp
test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
test/test_MipLevels.cpp
)
set(BACKEND_TEST_LIBS
)
target_link_libraries(backend_test PRIVATE
backend
getopt
gtest
imageio
filamat
SPIRV
spirv-cross-glsl)
endif()
if (APPLE)
# TODO: we should expand this test to Linux and other platforms.
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS
OGLCompiler
@@ -446,11 +436,12 @@ if (APPLE)
glslang
spirv-cross-core
spirv-cross-glsl
spirv-cross-msl)
spirv-cross-msl
)
if (NOT IOS)
target_link_libraries(backend_test PRIVATE image imageio)
list(APPEND BACKEND_TEST_DEPS image)
list(APPEND BACKEND_TEST_DEPS image imageio)
endif()
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
@@ -464,21 +455,15 @@ if (APPLE)
endif()
set_target_properties(backend_test PROPERTIES FOLDER Tests)
if (APPLE AND NOT IOS)
add_executable(backend_test_mac test/mac_runner.mm)
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
# linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
endif()
endif()
if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
if (APPLE AND NOT IOS)
add_executable(backend_test_mac test/mac_runner.mm)
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
# linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
endif()
# ==================================================================================================
@@ -113,7 +113,7 @@ public:
/**
* Helper to create a BufferDescriptor that uses a KNOWN method pointer w/ object passed
* by pointer as the callback. e.g.:
* auto bd = BufferDescriptor::make<Foo, &Foo::method>(buffer, size, foo);
* auto bd = BufferDescriptor::make(buffer, size, &Foo::method, foo);
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
@@ -121,12 +121,12 @@ public:
* @return a new BufferDescriptor
*/
template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(void const* buffer, size_t size, T* data,
CallbackHandler* handler = nullptr) noexcept {
static BufferDescriptor make(
void const* buffer, size_t size, T* data, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s);
(*static_cast<T**>(u)->*method)(b, s);
}, data
};
}
@@ -145,14 +145,14 @@ public:
* @return a new BufferDescriptor
*/
template<typename T>
static BufferDescriptor make(void const* buffer, size_t size, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
static BufferDescriptor make(
void const* buffer, size_t size, T&& functor, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
},
new T(std::forward<T>(functor))
};
@@ -201,7 +201,7 @@ public:
return mUser;
}
//! CPU memory-buffer virtual address
//! CPU mempry-buffer virtual address
void* buffer = nullptr;
//! CPU memory-buffer size in bytes
@@ -356,6 +356,13 @@ enum class SamplerFormat : uint8_t {
SHADOW = 3 //!< shadow sampler (PCF)
};
//! Texture color space
enum class SamplerTransferFunction : uint8_t {
UNDEFINED = 0, //!< don't care
IDENTITY = 1, //!< sampled as-is
SRGB_TO_LINEAR = 2, //!< sampled as sRGB and converted to linear
};
/**
* Supported element types
*/
@@ -1180,8 +1187,8 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
enum class Workaround : uint16_t {
// The EASU pass must split because shader compiler flattens early-exit branch
SPLIT_EASU,
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're
// read-only for the whole render pass.
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for
// the whole render pass.
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
ADRENO_UNIFORM_ARRAY_CRASH,
@@ -141,7 +141,7 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
@@ -149,7 +149,7 @@ public:
PixelDataFormat format, PixelDataType type, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
@@ -157,7 +157,7 @@ public:
backend::CompressedPixelDataType format, uint32_t imageSize, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data
(*static_cast<T**>(u)->*method)(b, s); }, data
};
}
@@ -168,9 +168,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}
@@ -181,9 +181,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}
@@ -194,9 +194,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}
@@ -170,6 +170,9 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
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);
@@ -284,7 +284,6 @@ void OpenGLContext::setDefaultState() noexcept {
if (ext.EXT_clip_cull_distance) {
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
}
+23 -40
View File
@@ -172,7 +172,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mShaderCompilerService(*this),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mSamplerMap(32) {
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
// set a reasonable default value for our stream array
@@ -262,27 +262,21 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
mContext.bindTexture(unit, t->gl.target, t->gl.id, t->gl.targetIndex);
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
if (UTILS_UNLIKELY(!p->isValid())) {
// If the program is not valid, we can't call use().
return false;
}
void OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
p->use(this, mContext);
if (UTILS_UNLIKELY(mContext.isES2())) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto [id, buffer, age] = mUniformBindings[i];
auto [buffer, age] = mUniformBindings[i];
if (buffer) {
p->updateUniforms(i, id, buffer, age);
p->updateUniforms(i, buffer, age);
}
}
// Set the output colorspace for this program (linear or rec709). This in only relevant
// when mPlatform.isSRGBSwapChainSupported() is false (no need to check though).
p->setRec709ColorSpace(mRec709OutputColorspace);
}
return true;
}
@@ -485,7 +479,6 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
GLBufferObject* bo = construct<GLBufferObject>(boh, byteCount, bindingType, usage);
if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
bo->gl.id = ++mLastAssignedEmulatedUboId;
bo->gl.buffer = malloc(byteCount);
memset(bo->gl.buffer, 0, byteCount);
} else {
@@ -602,9 +595,8 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
}
} else {
glTexImage2D(t->gl.target, level, GLint(t->gl.internalFormat),
std::max(GLsizei(1), GLsizei(width >> level)),
std::max(GLsizei(1), GLsizei(height >> level)),
0, format, type, nullptr);
GLsizei(width), GLsizei(height), 0,
format, type, nullptr);
}
}
}
@@ -1975,13 +1967,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
GLSwapChain* scRead = handle_cast<GLSwapChain*>(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 = {};
}
// ------------------------------------------------------------------------------------------------
@@ -2916,11 +2901,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
assert_invariant(offset + size <= ub->byteCount);
if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
mUniformBindings[index] = {
ub->gl.id,
static_cast<uint8_t const*>(ub->gl.buffer) + offset,
ub->age,
};
mUniformBindings[index] = { static_cast<uint8_t const*>(ub->gl.buffer) + offset, ub->age };
} else {
GLenum const target = GLUtils::getBufferBindingType(bindingType);
@@ -3537,17 +3518,18 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
DEBUG_MARKER()
auto& gl = mContext;
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(state.program);
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors that can occur during the draw call when the program
// is invalid. The shader compile error has already been dumped to the console at this
// point, so it's fine to simply return early.
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
useProgram(p);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive *>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
@@ -3558,7 +3540,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
const GLVertexBuffer* glvb = handle_cast<GLVertexBuffer*>(vb);
if (UTILS_UNLIKELY(rp->gl.vertexBufferVersion != glvb->bufferObjectsVersion)) {
updateVertexArrayObject(rp, glvb);
}
@@ -3592,16 +3574,17 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(program);
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors that can occur during the draw call when the program
// is invalid. The shader compile error has already been dumped to the console at this
// point, so it's fine to simply return early.
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
useProgram(p);
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
#if defined(__ANDROID__)
+6 -5
View File
@@ -83,9 +83,11 @@ public:
}
struct {
GLuint id;
union {
GLenum binding;
struct {
GLuint id;
GLenum binding;
};
void* buffer;
};
} gl;
@@ -318,7 +320,7 @@ private:
void bindTexture(GLuint unit, GLTexture const* t) noexcept;
void bindSampler(GLuint unit, GLuint sampler) noexcept;
inline bool useProgram(OpenGLProgram* p) noexcept;
inline void useProgram(OpenGLProgram* p) noexcept;
enum class ResolveAction { LOAD, STORE };
void resolvePass(ResolveAction action, GLRenderTarget const* rt,
@@ -361,8 +363,7 @@ private:
void setScissor(Viewport const& scissor) noexcept;
// ES2 only. Uniform buffer emulation binding points
GLuint mLastAssignedEmulatedUboId = 0;
std::array<std::tuple<GLuint, void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
std::array<std::pair<void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
// sampler buffer binding points (nullptr if not used)
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
@@ -129,7 +129,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
#endif
{
// ES2 initialization of (fake) UBOs
UniformsRecord* const uniformsRecords = new(std::nothrow) UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UniformsRecord* const uniformsRecords = new UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UTILS_NOUNROLL
for (GLuint binding = 0, n = Program::UNIFORM_BINDING_COUNT; binding < n; binding++) {
Program::UniformInfo& uniforms = lazyInitializationData.bindingUniformInfo[binding];
@@ -229,16 +229,15 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLProgram::updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept {
void OpenGLProgram::updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept {
assert_invariant(mUniformsRecords);
assert_invariant(buffer);
// only update the uniforms if the UBO has changed since last time we updated
UniformsRecord const& records = mUniformsRecords[index];
if (records.id == id && records.age == age) {
if (records.age == age) {
return;
}
records.id = id;
records.age = age;
assert_invariant(records.uniforms.size() == records.locations.size());
+2 -3
View File
@@ -59,7 +59,7 @@ public:
// - the content of any bound sampler buffer has changed
// ... since last time we used this program
// Turns out the former might be relatively cheap to check, the latter requires
// turns out the former might be relatively cheap to check, the later requires
// a bit less. Compared to what updateSamplers() actually does, which is
// pretty little, I'm not sure if we'll get ahead.
@@ -72,7 +72,7 @@ public:
} gl; // 12 bytes
// For ES2 only
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept;
void updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept;
void setRec709ColorSpace(bool rec709) const noexcept;
private:
@@ -98,7 +98,6 @@ private:
struct UniformsRecord {
Program::UniformInfo uniforms;
LocationInfo locations;
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
};
UniformsRecord const* mUniformsRecords = nullptr;
@@ -22,6 +22,7 @@
#include <private/backend/BackendUtils.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/Program.h>
#include <utils/compiler.h>
@@ -253,6 +254,8 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
programData.program = glProgram;
token->gl.program = programData.program;
// we don't need to check for success here, it'll be done on the
// main thread side.
token->set(programData);
@@ -334,12 +337,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
return program;
}
void ShaderCompilerService::terminate(program_token_t& token) {
/* static*/ void ShaderCompilerService::terminate(program_token_t& token) {
assert_invariant(token);
token->canceled = true;
bool const canceled = token->compiler.cancelTickOp(token);
bool canceled = token->compiler.cancelTickOp(token);
if (token->compiler.mShaderCompilerThreadCount) {
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
-2
View File
@@ -190,10 +190,8 @@ using namespace glext;
#if defined(GL_EXT_clip_cull_distance)
# define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT
# define GL_CLIP_DISTANCE1 GL_CLIP_DISTANCE1_EXT
#else
# define GL_CLIP_DISTANCE0 0x3000
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_KHR_debug)
+17 -9
View File
@@ -268,18 +268,13 @@ bool VulkanCommands::flush() {
VK_NULL_HANDLE,
VK_NULL_HANDLE,
};
uint32_t waitSemaphoreCount = 0;
if (mSubmissionSignal) {
signals[waitSemaphoreCount++] = mSubmissionSignal;
}
if (mInjectedSignal) {
signals[waitSemaphoreCount++] = mInjectedSignal;
}
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = waitSemaphoreCount,
.pWaitSemaphores = waitSemaphoreCount > 0 ? signals : nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = signals,
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer,
@@ -287,6 +282,19 @@ bool VulkanCommands::flush() {
.pSignalSemaphores = &renderingFinished,
};
if (mSubmissionSignal) {
signals[submitInfo.waitSemaphoreCount++] = mSubmissionSignal;
}
if (mInjectedSignal) {
signals[submitInfo.waitSemaphoreCount++] = mInjectedSignal;
}
// To address a validation warning.
if (submitInfo.waitSemaphoreCount == 0) {
submitInfo.pWaitSemaphores = VK_NULL_HANDLE;
}
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "Submitting cmdbuffer=" << cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
+2 -4
View File
@@ -54,8 +54,7 @@ VmaAllocator createAllocator(VkInstance instance, VkPhysicalDevice physicalDevic
VmaAllocator allocator;
VmaVulkanFunctions const funcs {
#if VMA_DYNAMIC_VULKAN_FUNCTIONS
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
.vkGetInstanceProcAddr = vkGetInstanceProcAddr, .vkGetDeviceProcAddr = vkGetDeviceProcAddr,
#else
.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties,
.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties,
@@ -427,8 +426,7 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
auto vkprogram
= mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
auto vkprogram = mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
mResourceManager.acquire(vkprogram);
}
+43 -22
View File
@@ -18,11 +18,10 @@
#include "VulkanConstants.h"
#include "VulkanMemory.h"
#include "spirv/VulkanSpirvUtils.h"
#include <backend/platforms/VulkanPlatform.h>
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/Panic.h>
using namespace bluevk;
@@ -47,39 +46,61 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig
rect->extent.height = std::max(top - y, 0);
}
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new PipelineInfo()),
mInfo(new PipelineInfo(builder.getSpecializationConstants().size())),
mDevice(device) {
Program::ShaderSource const& blobs = builder.getShadersSource();
auto& blobs = builder.getShadersSource();
auto& modules = mInfo->shaders;
auto const& specializationConstants = builder.getSpecializationConstants();
std::vector<uint32_t> shader;
for (size_t i = 0; i < MAX_SHADER_MODULES; i++) {
Program::ShaderBlob const& blob = blobs[i];
uint32_t* data = (uint32_t*) blob.data();
size_t dataSize = blob.size();
if (!specializationConstants.empty()) {
workaroundSpecConstant(blob, specializationConstants, shader);
data = (uint32_t*) shader.data();
dataSize = shader.size() * 4;
}
const auto& blob = blobs[i];
uint32_t* data = (uint32_t*)blob.data();
VkShaderModule& module = modules[i];
VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = dataSize,
.codeSize = blob.size(),
.pCode = data,
};
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
}
// Note that bools are 4-bytes in Vulkan
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
constexpr uint32_t const CONSTANT_SIZE = 4;
// populate the specialization constants requirements right now
auto const& specializationConstants = builder.getSpecializationConstants();
uint32_t const specConstCount = static_cast<uint32_t>(specializationConstants.size());
char* specData = mInfo->specConstData.get();
if (specConstCount > 0) {
mInfo->specializationInfo = {
.mapEntryCount = specConstCount,
.pMapEntries = mInfo->specConsts.data(),
.dataSize = specConstCount * CONSTANT_SIZE,
.pData = specData,
};
}
for (uint32_t i = 0; i < specConstCount; ++i) {
uint32_t const offset = i * CONSTANT_SIZE;
mInfo->specConsts[i] = {
.constantID = specializationConstants[i].id,
.offset = offset,
.size = CONSTANT_SIZE,
};
using SpecConstant = Program::SpecializationConstant::Type;
char const* addr = (char*)specData + offset;
SpecConstant const& arg = specializationConstants[i].value;
if (std::holds_alternative<bool>(arg)) {
*((VkBool32*)addr) = std::get<bool>(arg) ? VK_TRUE : VK_FALSE;
} else if (std::holds_alternative<float>(arg)) {
*((float*)addr) = std::get<float>(arg);
} else {
*((int32_t*)addr) = std::get<int32_t>(arg);
}
}
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -102,7 +123,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
CustomSamplerInfoList const& samplerInfo) noexcept
: VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new PipelineInfo()),
mInfo(new PipelineInfo(0)),
mDevice(device) {
mInfo->shaders[0] = vs;
mInfo->shaders[1] = fs;
+16 -5
View File
@@ -25,6 +25,7 @@
#include "VulkanResources.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "private/backend/SamplerGroup.h"
@@ -37,7 +38,7 @@ class VulkanTimestamps;
struct VulkanProgram : public HwProgram, VulkanResource {
VulkanProgram(VkDevice device, Program const& builder) noexcept;
VulkanProgram(VkDevice device, const Program& builder) noexcept;
struct CustomSamplerInfo {
uint8_t groupIndex;
@@ -64,14 +65,21 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
inline VkSpecializationInfo const& getSpecConstInfo() const {
return mInfo->specializationInfo;
}
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
static constexpr uint8_t MAX_SHADER_MODULES = 2;
struct PipelineInfo {
PipelineInfo()
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
PipelineInfo(size_t specConstsCount) :
bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
specConsts(specConstsCount, VkSpecializationMapEntry{}),
specConstData(new char[specConstsCount * 4])
{}
// This bitset maps to each of the sampler in the sampler groups associated with this
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
@@ -79,7 +87,10 @@ private:
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
VkShaderModule shaders[MAX_SHADER_MODULES] = {VK_NULL_HANDLE};
VkSpecializationInfo specializationInfo = {};
utils::FixedCapacityVector<VkSpecializationMapEntry> specConsts;
std::unique_ptr<char[]> specConstData;
};
PipelineInfo* mInfo;
@@ -360,7 +360,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
// If we reach this point, we need to create and stash a brand new pipeline object.
shaderStages[0].module = mPipelineRequirements.shaders[0];
shaderStages[0].pSpecializationInfo = mSpecializationRequirements;
shaderStages[1].module = mPipelineRequirements.shaders[1];
shaderStages[1].pSpecializationInfo = mSpecializationRequirements;
// Expand our size-optimized structs into the proper Vk structs.
uint32_t numVertexAttribs = 0;
@@ -566,6 +568,7 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
mSpecializationRequirements = &program->getSpecConstInfo();
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
@@ -416,6 +416,7 @@ private:
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
VkSpecializationInfo const* mSpecializationRequirements = nullptr;
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
@@ -99,16 +99,11 @@ void VulkanSwapChain::present() {
mColors[mCurrentSwapIndex]->transitionLayout(cmdbuf, subresources, VulkanLayout::PRESENT);
}
mCommands->flush();
// We only present if it is not headless. No-op for headless (but note that we still need the
// flush() in the above line).
if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
result == VK_ERROR_OUT_OF_DATE_KHR,
"Cannot present in swapchain.");
}
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR
|| result == VK_ERROR_OUT_OF_DATE_KHR,
"Cannot present in swapchain.");
// We presented the last acquired buffer.
mAcquired = false;
@@ -1,148 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanSpirvUtils.h"
#include <utils/compiler.h> // UTILS_UNUSED_IN_RELEASE, UTILS_FALLTHROUGH
#include <utils/debug.h> // assert_invariant
#include <utils/FixedCapacityVector.h>
#include <spirv/unified1/spirv.hpp>
#include <unordered_map>
#include <variant>
namespace filament::backend {
namespace {
// This function transforms an OpSpecConstant instruction into just a OpConstant instruction.
// Additionally, it will adjust the value of the constant as given by the program.
void getTransformedConstantInst(SpecConstantValue* value, uint32_t* inst) {
// The first word is the size of the instruction and the instruction type.
// The second word is the type of the instruction.
// The third word is the "result id" (i.e. the variable to store into).
// The fourth word (only for floats and ints) is the literal representing the value.
// We only want to change the first and fourth words.
constexpr size_t const OP = 0;
constexpr size_t const VALUE = 3;
if (!value) {
// If a specialization wasn't provided, just switch the opcode.
uint32_t const op = inst[OP] & 0x0000FFFF;
if (op == spv::Op::OpSpecConstantFalse) {
inst[OP] = (3 << 16) | spv::Op::OpConstantFalse;
} else if (op == spv::Op::OpSpecConstantTrue) {
inst[OP] = (3 << 16) | spv::Op::OpConstantTrue;
} else if (op == spv::Op::OpSpecConstant) {
inst[OP] = (4 << 16) | spv::Op::OpConstant;
}
} else if (std::holds_alternative<bool>(*value)) {
inst[OP] = (3 << 16)
| (std::get<bool>(*value) ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse);
} else if (std::holds_alternative<float>(*value)) {
float const fval = std::get<float>(*value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&fval);
} else {
int const ival = std::get<int>(*value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&ival);
}
}
} // anonymous namespace
void workaroundSpecConstant(Program::ShaderBlob const& blob,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::vector<uint32_t>& output) {
using WordMap = std::unordered_map<uint32_t, uint32_t>;
using SpecValueMap = std::unordered_map<uint32_t, SpecConstantValue>;
constexpr size_t const HEADER_SIZE = 5;
WordMap varToIdMap;
SpecValueMap idToValue;
for (auto spec: specConstants) {
idToValue[spec.id] = spec.value;
}
// The follow loop will iterate through all the instructions and look for instructions of the
// form:
//
// OpDecorate %1 SpecId 0
// %1 = OpSpecConstantFalse %bool
//
// We want to track the mapping between the variable id %1 and the specId 0. And when seeing a
// %1 as the result, we want to change that instruction to just an OpConsant where the constant
// value is adjusted by the spec values provided in the program.
// Additionally, we will turn the SpecId decorations to no-ops.
size_t const dataSize = blob.size() / 4;
output.resize(dataSize);
uint32_t const* data = (uint32_t*) blob.data();
uint32_t* outputData = output.data();
std::memcpy(&outputData[0], &data[0], HEADER_SIZE * 4);
size_t outputCursor = HEADER_SIZE;
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
switch(op) {
case spv::Op::OpSpecConstant:
case spv::Op::OpSpecConstantTrue:
case spv::Op::OpSpecConstantFalse: {
uint32_t const targetVar = data[cursor + 2];
UTILS_UNUSED_IN_RELEASE WordMap::const_iterator idItr = varToIdMap.find(targetVar);
assert_invariant(idItr != varToIdMap.end() &&
"Cannot find variable previously decorated with SpecId");
auto valItr = idToValue.find(idItr->second);
SpecConstantValue* val = (valItr != idToValue.end()) ? &valItr->second : nullptr;
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
getTransformedConstantInst(val, &outputData[outputCursor]);
outputCursor += wordCount;
break;
}
case spv::Op::OpDecorate: {
if (data[cursor + 2] == spv::Decoration::DecorationSpecId) {
uint32_t const targetVar = data[cursor + 1];
uint32_t const specId = data[cursor + 3];
varToIdMap[targetVar] = specId;
// Note these decorations do not need to be written to the output.
break;
}
// else fallthrough and copy like all other instructions
UTILS_FALLTHROUGH;
}
default:
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
outputCursor += wordCount;
break;
}
cursor += wordCount;
}
output.resize(outputCursor);
}
} // namespace filament::backend
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
#define TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
#include "backend/Program.h"
#include <utils/FixedCapacityVector.h>
#include <vector>
namespace filament::backend {
using SpecConstantValue = Program::SpecializationConstant::Type;
// For certain drivers, using spec constant can lead to compile errors [1] or undesirable behaviors
// [2]. In those instances, we simply change the spirv and set them to constants.
//
// (Implemenation note: we cannot write to the blob because spirv-validator does not properly handle
// the Nop (no-op) instruction, and swiftshader validates the shader before compilation. So we need
// to skip those instructions instead).
//
// [1]: QC driver cannot use spec constant to size fields
// (https://github.com/google/filament/issues/6444).
// [2]: An internal driver does not DCE a block guarded by a spec-const boolean set to false
// (b/310603393).
void workaroundSpecConstant(Program::ShaderBlob const& blob,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::vector<uint32_t>& output);
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
+3 -5
View File
@@ -86,16 +86,13 @@ void BackendTest::executeCommands() {
}
void BackendTest::flushAndWait() {
getDriverApi().finish();
auto& api = getDriverApi();
api.finish();
executeCommands();
getDriver().purge();
}
Handle<HwSwapChain> BackendTest::createSwapChain() {
const NativeView& view = getNativeView();
if (!view.ptr) {
return getDriverApi().createSwapChainHeadless(view.width, view.height, 0);
}
return getDriverApi().createSwapChain(view.ptr, 0);
}
@@ -215,3 +212,4 @@ int runTests() {
}
} // namespace test
-3
View File
@@ -22,9 +22,6 @@
namespace test {
constexpr uint32_t const WINDOW_WIDTH = 512;
constexpr uint32_t const WINDOW_HEIGHT = 512;
// To avoid a dependency on filabridge, the Backend enum is replicated here.
enum class Backend : uint8_t {
OPENGL = 1,
-56
View File
@@ -1,56 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BackendTest.h"
#include "PlatformRunner.h"
#include <array>
#include <iostream>
namespace test {
test::NativeView getNativeView() {
return {
.ptr = nullptr,
.width = WINDOW_WIDTH,
.height = WINDOW_HEIGHT,
};
}
}// namespace test
namespace {
std::array<test::Backend, 2> const VALID_BACKENDS{
test::Backend::OPENGL,
test::Backend::VULKAN,
};
}// namespace
int main(int argc, char* argv[]) {
auto backend = test::parseArgumentsForBackend(argc, argv);
if (!std::any_of(VALID_BACKENDS.begin(), VALID_BACKENDS.end(),
[backend](test::Backend validBackend) { return backend == validBackend; })) {
std::cerr << "Specified an invalid backend. Only GL and Vulkan are available" << std::endl;
return 1;
}
test::initTests(backend, false, argc, argv);
return test::runTests();
}
+1 -1
View File
@@ -58,7 +58,7 @@ test::NativeView getNativeView() {
}
- (NSView*)createView {
NSRect frame = NSMakeRect(0, 0, test::WINDOW_WIDTH, test::WINDOW_HEIGHT);
NSRect frame = NSMakeRect(0, 0, 512, 512);
NSWindow* window = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSWindowStyleMaskBorderless
backing:NSBackingStoreBuffered
+112 -40
View File
@@ -193,6 +193,60 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
}
TEST_F(BackendTest, CubemapMinify) {
auto& api = getDriverApi();
Handle<HwTexture> 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<HwRenderTarget> srcRenderTarget;
srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
256 >> srcLevel, 256 >> srcLevel, 1, { srcInfo }, {}, {});
Handle<HwRenderTarget> 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, &params);
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();
@@ -209,7 +263,7 @@ TEST_F(BackendTest, ColorMagnify) {
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> 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;
@@ -217,7 +271,7 @@ TEST_F(BackendTest, ColorMagnify) {
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
@@ -284,7 +338,7 @@ TEST_F(BackendTest, ColorMinify) {
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> 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;
@@ -292,7 +346,7 @@ TEST_F(BackendTest, ColorMinify) {
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
@@ -308,20 +362,29 @@ 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);
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);
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
api.beginFrame(0, 0);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
api.endFrame(0);
// Wait for the ReadPixels result to come back.
flushAndWait();
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0xf3d9c53f;
const uint32_t expected = 0x7739bef5;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
@@ -354,6 +417,8 @@ 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));
}
@@ -485,82 +550,85 @@ 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 const triangle(api);
TrianglePrimitive* triangle = new TrianglePrimitive(api);
// Create 4-sample texture.
Handle<HwTexture> const srcColorTexture = api.createTexture(
Handle<HwTexture> srcColorTexture = api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT);
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = api.createTexture(
Handle<HwTexture> 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<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount,
{{ srcColorTexture }}, {}, {});
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount,{{ srcColorTexture }}, {}, {});
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1,
{{ dstColorTexture }}, {}, {});
Handle<HwRenderTarget> 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.program = program;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
state.rasterState.culling = RasterState::CullingMode::NONE;
state.program = program;
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),
});
// 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.beginRenderPass(srcRenderTarget, params);
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::NEAREST);
api.blit(TargetBufferFlags::COLOR, dstRenderTarget,
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
{0, 0, kSrcTexWidth, kSrcTexHeight}, SamplerMagFilter::LINEAR);
// Grab a screenshot.
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
ScreenshotParams sparams { kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
api.beginFrame(0, 0);
dumpScreenshot(api, dstRenderTarget, &sparams);
api.commit(swapChain);
api.endFrame(0);
// Wait for the ReadPixels result to come back.
flushAndWait();
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0xebfac2ef;
@@ -572,9 +640,11 @@ TEST_F(BackendTest, ColorResolve) {
api.destroyProgram(program);
api.destroyTexture(srcColorTexture);
api.destroyTexture(dstColorTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
flushAndWait();
delete triangle;
executeCommands();
}
TEST_F(BackendTest, DepthResolve) {
@@ -597,6 +667,8 @@ 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));
}
+7 -8
View File
@@ -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 test_tex;
layout(location = 0, set = 1) uniform sampler2D 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(test_tex, uv, params.sourceLevel);
fragColor = textureLod(tex, uv, params.sourceLevel);
})";
static uint32_t sPixelHashResult = 0;
@@ -112,7 +112,6 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
#endif
free(buffer);
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
@@ -134,24 +133,24 @@ TEST_F(BackendTest, FeedbackLoops) {
// Create a program.
ProgramHandle program;
{
SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.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("test_tex"), 0 };
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));
}
TrianglePrimitive const triangle(getDriverApi());
TrianglePrimitive triangle(getDriverApi());
// Create a texture.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = api.createTexture(
Handle<HwTexture> texture = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage);
// Create a RenderTarget for each miplevel.
+67 -86
View File
@@ -174,41 +174,6 @@ 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) {
@@ -266,10 +231,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)"
@@ -297,45 +261,48 @@ TEST_F(BackendTest, UpdateImage2D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder()
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH }} )
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string const fragment = stringReplace("{samplerType}",
ProgramHandle program;
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
ProgramHandle const program = api.createProgram(std::move(prog));
program = api.createProgram(std::move(prog));
// Create a Texture.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
auto usage = TextureUsage::SAMPLEABLE;
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage);
// Upload some pixel data.
if (t.uploadSubregions) {
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));
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));
} else {
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
PixelBufferDescriptor descriptor
= checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding);
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
}
SamplerGroup samplers(1);
samplers.setSampler(0, { texture, {
.filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST } });
SamplerParams sparams = {};
sparams.filterMag = SamplerMagFilter::LINEAR;
sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
samplers.setSampler(0, { texture, sparams });
auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test"));
api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api));
@@ -345,6 +312,7 @@ TEST_F(BackendTest, UpdateImage2D) {
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
api.flush();
api.commit(swapChain);
api.endFrame(0);
@@ -352,21 +320,25 @@ 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();
flushAndWait();
executeCommands();
getDriver().purge();
}
TEST_F(BackendTest, UpdateImageSRGB) {
auto& api = getDriverApi();
api.startCapture();
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
PixelDataType const pixelType = PixelDataType::UBYTE;
TextureFormat const textureFormat = TextureFormat::SRGB8_A8;
PixelDataFormat pixelFormat = PixelDataFormat::RGB;
PixelDataType pixelType = PixelDataType::UBYTE;
TextureFormat textureFormat = TextureFormat::SRGB8_A8;
// Create a platform-specific SwapChain and make it current.
auto swapChain = createSwapChain();
@@ -374,22 +346,22 @@ TEST_F(BackendTest, UpdateImageSRGB) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder()
SamplerInterfaceBlock 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 const fragment = stringReplace("{samplerType}",
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
ProgramHandle const program = api.createProgram(std::move(prog));
ProgramHandle program = api.createProgram(std::move(prog));
// Create a texture.
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE);
// Create image data.
size_t components; int bpp;
@@ -429,7 +401,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
renderTriangle(defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 359858623;
static const uint32_t expectedHash = 519370995;
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
api.flush();
@@ -443,9 +415,12 @@ TEST_F(BackendTest, UpdateImageSRGB) {
// This ensures all driver commands have finished before exiting the test.
api.finish();
api.stopCapture();
flushAndWait();
executeCommands();
getDriver().purge();
}
TEST_F(BackendTest, UpdateImageMipLevel) {
@@ -465,26 +440,26 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
SamplerInterfaceBlock 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_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string const fragment = stringReplace("{samplerType}",
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
ProgramHandle const program = api.createProgram(std::move(prog));
ProgramHandle 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<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 3,
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE);
// Create image data.
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.update3DImage(texture, 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.beginFrame(0, 0);
@@ -515,9 +490,12 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
// This ensures all driver commands have finished before exiting the test.
api.finish();
api.stopCapture();
flushAndWait();
executeCommands();
getDriver().purge();
}
TEST_F(BackendTest, UpdateImage3D) {
@@ -528,7 +506,7 @@ TEST_F(BackendTest, UpdateImage3D) {
PixelDataType pixelType = PixelDataType::FLOAT;
TextureFormat textureFormat = TextureFormat::RGBA16F;
SamplerType samplerType = SamplerType::SAMPLER_2D_ARRAY;
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
TextureUsage usage = TextureUsage::SAMPLEABLE;
// Create a platform-specific SwapChain and make it current.
auto swapChain = createSwapChain();
@@ -539,15 +517,15 @@ TEST_F(BackendTest, UpdateImage3D) {
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT, Precision::HIGH }} )
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
ProgramHandle const program = api.createProgram(std::move(prog));
ProgramHandle program = api.createProgram(std::move(prog));
// Create a texture.
Handle<HwTexture> texture = api.createTexture(samplerType, 1,
@@ -599,9 +577,12 @@ TEST_F(BackendTest, UpdateImage3D) {
// This ensures all driver commands have finished before exiting the test.
api.finish();
api.stopCapture();
flushAndWait();
executeCommands();
getDriver().purge();
}
} // namespace test
+11 -7
View File
@@ -23,7 +23,6 @@
#include <utils/Hash.h>
#include <fstream>
#include <algorithm>
using namespace filament;
using namespace filament::backend;
@@ -108,7 +107,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// The size of the actual render target, taking mip level into account;
size_t getRenderTargetSize () const {
return std::max(size_t(1), renderTargetBaseSize >> mipLevel);
return renderTargetBaseSize >> mipLevel;
}
// The rect that read pixels will read from.
@@ -170,7 +169,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 const t0;
TestCase t;
// Check that a subregion of the render target can be read into a pixel buffer.
TestCase t2;
@@ -234,7 +233,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
t8.testName = "readPixels_swapchain";
t8.useDefaultRT = true;
TestCase const testCases[] = { t0, t2, t3, t4, t5, t6, t7, t8 };
TestCase testCases[] = { t, t2, t3, t4, t5, t6, t7, t8 };
// Create programs.
Handle<HwProgram> programFloat, programUint;
@@ -265,7 +264,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
Handle<HwTexture> texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1,
usage);
@@ -283,7 +282,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
{});
}
TrianglePrimitive const triangle(getDriverApi());
TrianglePrimitive triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -357,6 +356,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().endRenderPass();
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
@@ -369,7 +369,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
getDriverApi().destroyProgram(programUint);
// This ensures all driver commands have finished before exiting the test.
flushAndWait();
getDriverApi().finish();
executeCommands();
getDriver().purge();
}
TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
@@ -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 test_tex;
layout(location = 0, set = 1) uniform sampler2D tex;
void main() {
fragColor = texture(test_tex, uv);
fragColor = texture(tex, uv);
}
)");
@@ -66,7 +66,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto swapChain = createSwapChain();
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.name("backend_test_sib")
.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("test_tex"), 0 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
backend::Handle<HwProgram> program = getDriverApi().createProgram(std::move(p));
@@ -146,7 +146,7 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = createSwapChain();
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.name("backend_test_sib")
.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("test_tex"), 0 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
auto program = getDriverApi().createProgram(std::move(p));
+7 -10
View File
@@ -286,20 +286,17 @@ public:
/** Sets a custom projection matrix for each eye.
*
* The projectionForCulling, near, and far parameters establish a "culling frustum" which must
* encompass anything any eye can see. All projection matrices must be set simultaneously. The
* number of stereoscopic eyes is controlled by the stereoscopicEyeCount setting inside of
* Engine::Config.
* encompass anything either eye can see.
*
* @param projection an array of projection matrices, only the first config.stereoscopicEyeCount
* are read
* @param projection an array of projection matrices, only the first
* CONFIG_STEREOSCOPIC_EYES (2) are read
* @param count size of the projection matrix array to set, must be
* >= config.stereoscopicEyeCount
* >= CONFIG_STEREOSCOPIC_EYES (2)
* @param projectionForCulling custom projection matrix for culling, must encompass both eyes
* @param near distance in world units from the camera to the culling near plane. \p near > 0.
* @param far distance in world units from the camera to the culling far plane. \p far > \p
* near.
* @see setCustomProjection
* @see Engine::Config::stereoscopicEyeCount
*/
void setCustomEyeProjection(math::mat4 const* projection, size_t count,
math::mat4 const& projectionForCulling, double near, double far);
@@ -360,8 +357,8 @@ public:
* The projection matrix used for rendering always has its far plane set to infinity. This
* is why it may differ from the matrix set through setProjection() or setLensProjection().
*
* @param eyeId the index of the eye to return the projection matrix for, must be
* < config.stereoscopicEyeCount
* @param eyeId the index of the eye to return the projection matrix for, must be <
* CONFIG_STEREOSCOPIC_EYES (2)
* @return The projection matrix used for rendering
*
* @see setProjection, setLensProjection, setCustomProjection, getCullingProjectionMatrix,
@@ -419,7 +416,7 @@ public:
* This method is not intended to be called every frame. Instead, to update the position of the
* head, use Camera::setModelMatrix.
*
* @param eyeId the index of the eye to set, must be < config.stereoscopicEyeCount
* @param eyeId the index of the eye to set, must be < CONFIG_STEREOSCOPIC_EYES (2)
* @param model the model matrix for an individual eye
*/
void setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model);
-32
View File
@@ -291,15 +291,6 @@ public:
* Currently only respected by the Metal backend.
*/
size_t textureUseAfterFreePoolSize = 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
*/
uint8_t stereoscopicEyeCount = 2;
};
@@ -563,29 +554,6 @@ public:
*/
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
*/
+5 -5
View File
@@ -50,11 +50,11 @@ class UTILS_PUBLIC Material : public FilamentAPI {
struct BuilderDetails;
public:
using BlendingMode = filament::BlendingMode;
using Shading = filament::Shading;
using Interpolation = filament::Interpolation;
using VertexDomain = filament::VertexDomain;
using TransparencyMode = filament::TransparencyMode;
using BlendingMode = BlendingMode;
using Shading = Shading;
using Interpolation = Interpolation;
using VertexDomain = VertexDomain;
using TransparencyMode = TransparencyMode;
using ParameterType = backend::UniformType;
using Precision = backend::Precision;
+33 -34
View File
@@ -66,26 +66,26 @@ class Viewport;
*/
class UTILS_PUBLIC View : public FilamentAPI {
public:
using QualityLevel = filament::QualityLevel;
using BlendMode = filament::BlendMode;
using AntiAliasing = filament::AntiAliasing;
using Dithering = filament::Dithering;
using ShadowType = filament::ShadowType;
using QualityLevel = QualityLevel;
using BlendMode = BlendMode;
using AntiAliasing = AntiAliasing;
using Dithering = Dithering;
using ShadowType = ShadowType;
using DynamicResolutionOptions = filament::DynamicResolutionOptions;
using BloomOptions = filament::BloomOptions;
using FogOptions = filament::FogOptions;
using DepthOfFieldOptions = filament::DepthOfFieldOptions;
using VignetteOptions = filament::VignetteOptions;
using RenderQuality = filament::RenderQuality;
using AmbientOcclusionOptions = filament::AmbientOcclusionOptions;
using TemporalAntiAliasingOptions = filament::TemporalAntiAliasingOptions;
using MultiSampleAntiAliasingOptions = filament::MultiSampleAntiAliasingOptions;
using VsmShadowOptions = filament::VsmShadowOptions;
using SoftShadowOptions = filament::SoftShadowOptions;
using ScreenSpaceReflectionsOptions = filament::ScreenSpaceReflectionsOptions;
using GuardBandOptions = filament::GuardBandOptions;
using StereoscopicOptions = filament::StereoscopicOptions;
using DynamicResolutionOptions = DynamicResolutionOptions;
using BloomOptions = BloomOptions;
using FogOptions = FogOptions;
using DepthOfFieldOptions = DepthOfFieldOptions;
using VignetteOptions = VignetteOptions;
using RenderQuality = RenderQuality;
using AmbientOcclusionOptions = AmbientOcclusionOptions;
using TemporalAntiAliasingOptions = TemporalAntiAliasingOptions;
using MultiSampleAntiAliasingOptions = MultiSampleAntiAliasingOptions;
using VsmShadowOptions = VsmShadowOptions;
using SoftShadowOptions = SoftShadowOptions;
using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions;
using GuardBandOptions = GuardBandOptions;
using StereoscopicOptions = StereoscopicOptions;
/**
* Sets the View's name. Only useful for debugging.
@@ -691,12 +691,11 @@ public:
* - punctual lights
*
* Stereo rendering depends on device and platform support. To check if stereo rendering is
* supported, use Engine::isStereoSupported(). If stereo rendering is not supported, then the
* stereoscopic options have no effect.
* supported, use Engine::isStereoSupported().
*
* @param options The stereoscopic options to use on this view
*/
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
void setStereoscopicOptions(StereoscopicOptions const& options);
/**
* Returns the stereoscopic options associated with this View.
@@ -764,10 +763,10 @@ public:
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T* instance,
backend::CallbackHandler* handler = nullptr) noexcept {
void pick(uint32_t x, uint32_t y, T* instance, backend::CallbackHandler* handler = nullptr) noexcept {
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
(static_cast<T*>(pq->storage[0])->*method)(result);
void* user = pq->storage;
(*static_cast<T**>(user)->*method)(result);
}, handler);
query.storage[0] = instance;
}
@@ -784,11 +783,11 @@ public:
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T instance,
backend::CallbackHandler* handler = nullptr) noexcept {
void pick(uint32_t x, uint32_t y, T instance, backend::CallbackHandler* handler = nullptr) noexcept {
static_assert(sizeof(instance) <= sizeof(PickingQuery::storage), "user data too large");
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
T* const that = static_cast<T*>(reinterpret_cast<void*>(pq->storage));
void* user = pq->storage;
T* that = static_cast<T*>(user);
(that->*method)(result);
that->~T();
}, handler);
@@ -805,15 +804,15 @@ public:
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T>
void pick(uint32_t x, uint32_t y, T functor,
backend::CallbackHandler* handler = nullptr) noexcept {
void pick(uint32_t x, uint32_t y, T functor, backend::CallbackHandler* handler = nullptr) noexcept {
static_assert(sizeof(functor) <= sizeof(PickingQuery::storage), "functor too large");
PickingQuery& query = pick(x, y, handler,
(PickingQueryResultCallback)[](PickingQueryResult const& result, PickingQuery* pq) {
T* const that = static_cast<T*>(reinterpret_cast<void*>(pq->storage));
that->operator()(result);
that->~T();
});
void* user = pq->storage;
T& that = *static_cast<T*>(user);
that(result);
that.~T();
});
new(query.storage) T(std::move(functor));
}
-8
View File
@@ -327,18 +327,10 @@ 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();
+7 -1
View File
@@ -426,6 +426,7 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
uint8_t fieldFormat = 0;
uint8_t fieldPrecision = 0;
bool fieldMultisample = false;
uint8_t fieldTransferFunction = 0;
if (!unflattener.read(&fieldName)) {
return false;
@@ -447,10 +448,15 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
return false;
}
if (!unflattener.read(&fieldTransferFunction)) {
return false;
}
builder.add({ fieldName.data(), fieldName.size() }, SamplerInterfaceBlock::Type(fieldType),
SamplerInterfaceBlock::Format(fieldFormat),
SamplerInterfaceBlock::Precision(fieldPrecision),
fieldMultisample);
fieldMultisample,
SamplerInterfaceBlock::SamplerTransferFunction(fieldTransferFunction));
}
*sib = builder.build();
+1 -2
View File
@@ -82,8 +82,7 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
mat4f const& headFromWorld = camera.view;
Engine::Config const& config = engine.getConfig();
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
mat4f const& eyeFromHead = camera.eyeFromView[i]; // identity for monoscopic rendering
mat4f const& clipFromEye = camera.eyeProjection[i];
// clipFromEye * eyeFromHead * headFromWorld
+1 -2
View File
@@ -2714,8 +2714,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
backend::SamplerMagFilter filter) noexcept {
if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW &&
mEngine.getDriverApi().getFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW)) {
return opaqueBlit(fg, input, vp, outDesc, filter);
}
+9 -14
View File
@@ -116,17 +116,14 @@ void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandT
commandCount += 1; // for the sentinel
Command* const curr = append(commandCount);
auto stereoscopicEyeCount =
renderFlags & IS_STEREOSCOPIC ? engine.getConfig().stereoscopicEyeCount : 1;
const float3 cameraPosition(mCameraPosition);
const float3 cameraForwardVector(mCameraForwardVector);
auto work = [commandTypeFlags, curr, &soa, variant, renderFlags, visibilityMask, cameraPosition,
cameraForwardVector, stereoscopicEyeCount]
cameraForwardVector]
(uint32_t startIndex, uint32_t indexCount) {
RenderPass::generateCommands(commandTypeFlags, curr,
soa, { startIndex, startIndex + indexCount }, variant, renderFlags, visibilityMask,
cameraPosition, cameraForwardVector, stereoscopicEyeCount);
cameraPosition, cameraForwardVector);
};
if (vr.size() <= JOBS_PARALLEL_FOR_COMMANDS_COUNT) {
@@ -377,8 +374,8 @@ UTILS_NOINLINE
void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const commands,
FScene::RenderableSoa const& soa, Range<uint32_t> range,
Variant variant, RenderFlags renderFlags,
FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward,
uint8_t instancedStereoEyeCount) noexcept {
FScene::VisibleMaskType visibilityMask,
float3 cameraPosition, float3 cameraForward) noexcept {
SYSTRACE_CALL();
@@ -409,13 +406,11 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) {
case CommandTypeFlags::COLOR:
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
instancedStereoEyeCount);
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
break;
case CommandTypeFlags::DEPTH:
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
instancedStereoEyeCount);
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
break;
default:
// we should never end-up here
@@ -438,7 +433,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
Command* UTILS_RESTRICT curr,
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
float3 cameraPosition, float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept {
float3 cameraPosition, float3 cameraForward) noexcept {
// generateCommands() writes both the draw and depth commands simultaneously such that
// we go throw the list of renderables just once.
@@ -534,7 +529,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
// eye count.
if (UTILS_UNLIKELY(hasInstancedStereo)) {
cmdColor.primitive.instanceCount =
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
(soaInstanceInfo[i].count * CONFIG_STEREOSCOPIC_EYES) |
PrimitiveInfo::USER_INSTANCE_MASK;
}
@@ -560,7 +555,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
if (UTILS_UNLIKELY(hasInstancedStereo)) {
cmdColor.primitive.instanceCount =
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
(soaInstanceInfo[i].count * CONFIG_STEREOSCOPIC_EYES) |
PrimitiveInfo::USER_INSTANCE_MASK;
}
}
+2 -4
View File
@@ -408,15 +408,13 @@ private:
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
Variant variant, RenderFlags renderFlags,
FScene::VisibleMaskType visibilityMask,
math::float3 cameraPosition, math::float3 cameraForward,
uint8_t instancedStereoEyeCount) noexcept;
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
template<uint32_t commandTypeFlags>
static inline Command* generateCommandsImpl(uint32_t extraFlags, Command* curr,
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
math::float3 cameraPosition, math::float3 cameraForward,
uint8_t instancedStereoEyeCount) noexcept;
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
+1 -4
View File
@@ -108,13 +108,11 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo) noexcept {
// reset the visible shadow status
mHasVisibleShadows = false;
FLightManager const& lcm = engine.getLightManager();
FLightManager::Instance const li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
// We can't use LISPSM in stable mode
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
@@ -139,7 +137,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
*/
mat4f W, Wp, L;
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
if (useLispsm) {
// Orient the shadow map in the direction of the view vector by constructing a
+1 -1
View File
@@ -522,7 +522,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = cascadedShadowMaps[0];
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(0);
const auto direction = options.transform * lightData.elementAt<FScene::DIRECTION>(0);
// We compute the directional light's model matrix using the origin's as the light position.
// The choice of the light's origin initially doesn't matter for a directional light.
+1 -1
View File
@@ -283,7 +283,7 @@ bool View::isStencilBufferEnabled() const noexcept {
return downcast(this)->isStencilBufferEnabled();
}
void View::setStereoscopicOptions(const StereoscopicOptions& options) noexcept {
void View::setStereoscopicOptions(const StereoscopicOptions& options) {
return downcast(this)->setStereoscopicOptions(options);
}
+9 -23
View File
@@ -96,11 +96,11 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p,
void UTILS_NOINLINE FCamera::setCustomEyeProjection(math::mat4 const* projection, size_t count,
math::mat4 const& projectionForCulling, double near, double far) {
const Engine::Config& config = mEngine.getConfig();
ASSERT_PRECONDITION(count >= config.stereoscopicEyeCount,
ASSERT_PRECONDITION(count >= CONFIG_STEREOSCOPIC_EYES,
"All eye projections must be supplied together, count must be >= "
"config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
"CONFIG_STEREOSCOPIC_EYES(%d)",
CONFIG_STEREOSCOPIC_EYES);
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
mEyeProjection[i] = projection[i];
}
mProjectionForCulling = projectionForCulling;
@@ -165,8 +165,7 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection,
}
math::mat4 FCamera::getProjectionMatrix(uint8_t eye) const noexcept {
UTILS_UNUSED_IN_RELEASE const Engine::Config& config = mEngine.getConfig();
assert_invariant(eye < config.stereoscopicEyeCount);
assert_invariant(eye < CONFIG_STEREOSCOPIC_EYES);
// This is where we transform the user clip-space (GL convention) to our virtual clip-space
// (inverted DX convention)
// Note that this math ends up setting the projection matrix' p33 to 0, which is where we're
@@ -191,13 +190,6 @@ math::mat4 FCamera::getCullingProjectionMatrix() const noexcept {
return m * mProjectionForCulling;
}
const math::mat4& FCamera::getUserProjectionMatrix(uint8_t eyeId) const {
const Engine::Config& config = mEngine.getConfig();
ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount,
"eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
return mEyeProjection[eyeId];
}
void UTILS_NOINLINE FCamera::setModelMatrix(const mat4f& modelMatrix) noexcept {
FTransformManager& transformManager = mEngine.getTransformManager();
transformManager.setTransform(transformManager.getInstance(mEntity), modelMatrix);
@@ -209,9 +201,8 @@ void UTILS_NOINLINE FCamera::setModelMatrix(const mat4& modelMatrix) noexcept {
}
void UTILS_NOINLINE FCamera::setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model) {
const Engine::Config& config = mEngine.getConfig();
ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount,
"eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
ASSERT_PRECONDITION(eyeId < CONFIG_STEREOSCOPIC_EYES,
"eyeId must be < CONFIG_STEREOSCOPIC_EYES(%d)", CONFIG_STEREOSCOPIC_EYES);
mEyeFromView[eyeId] = inverse(model);
}
@@ -258,15 +249,10 @@ double FCamera::computeEffectiveFov(double fovInDegrees, double focusDistance) n
return fov * math::d::RAD_TO_DEG;
}
uint8_t FCamera::getStereoscopicEyeCount() const noexcept {
const Engine::Config& config = mEngine.getConfig();
return config.stereoscopicEyeCount;
}
// ------------------------------------------------------------------------------------------------
CameraInfo::CameraInfo(FCamera const& camera) noexcept {
for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) {
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
}
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
@@ -282,7 +268,7 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept {
const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() };
for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) {
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) };
}
+16 -14
View File
@@ -85,7 +85,11 @@ public:
math::mat4 getEyeFromViewMatrix(uint8_t eye) const noexcept { return mEyeFromView[eye]; }
// viewing projection matrix set by the user
const math::mat4& getUserProjectionMatrix(uint8_t eyeId) const;
const math::mat4& getUserProjectionMatrix(uint8_t eyeId) const {
ASSERT_PRECONDITION(eyeId < CONFIG_STEREOSCOPIC_EYES,
"eyeId must be < CONFIG_STEREOSCOPIC_EYES(%d)", CONFIG_STEREOSCOPIC_EYES);
return mEyeProjection[eyeId];
}
// culling projection matrix set by the user
math::mat4 getUserCullingProjectionMatrix() const noexcept { return mProjectionForCulling; }
@@ -185,8 +189,6 @@ public:
static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept;
uint8_t getStereoscopicEyeCount() const noexcept;
utils::Entity getEntity() const noexcept {
return mEntity;
}
@@ -202,10 +204,10 @@ private:
utils::Entity mEntity;
// For monoscopic cameras, mEyeProjection[0] == mEyeProjection[1].
math::mat4 mEyeProjection[CONFIG_MAX_STEREOSCOPIC_EYES]; // projection matrix per eye (infinite far)
math::mat4 mProjectionForCulling; // projection matrix (with far plane)
math::mat4 mEyeFromView[CONFIG_MAX_STEREOSCOPIC_EYES]; // transforms from the main view (head)
// space to each eye's unique view space
math::mat4 mEyeProjection[CONFIG_STEREOSCOPIC_EYES]; // projection matrix per eye (infinite far)
math::mat4 mProjectionForCulling; // projection matrix (with far plane)
math::mat4 mEyeFromView[CONFIG_STEREOSCOPIC_EYES]; // transforms from the main view (head)
// space to each eye's unique view space
math::double2 mScalingCS = {1.0}; // additional scaling applied to projection
math::double2 mShiftCS = {0.0}; // additional translation applied to projection
@@ -230,15 +232,15 @@ struct CameraInfo {
math::mat4f projection;
// for stereo rendering, one matrix per eye
math::mat4f eyeProjection[CONFIG_MAX_STEREOSCOPIC_EYES] = {};
math::mat4f eyeProjection[CONFIG_STEREOSCOPIC_EYES] = {};
};
math::mat4f cullingProjection; // projection matrix for culling
math::mat4f model; // camera model matrix
math::mat4f view; // camera view matrix (inverse(model))
math::mat4f eyeFromView[CONFIG_MAX_STEREOSCOPIC_EYES]; // eye view matrix (only for stereoscopic)
math::mat4 worldTransform; // world transform (already applied
// to model and view)
math::mat4f cullingProjection; // projection matrix for culling
math::mat4f model; // camera model matrix
math::mat4f view; // camera view matrix (inverse(model))
math::mat4f eyeFromView[CONFIG_STEREOSCOPIC_EYES]; // eye view matrix (only for stereoscopic)
math::mat4 worldTransform; // world transform (already applied
// to model and view)
math::float4 clipTransform{1, 1, 0, 0}; // clip-space transform, only for VERTEX_DOMAIN_DEVICE
float zn{}; // distance (positive) to the near plane
float zf{}; // distance (positive) to the far plane
+1 -11
View File
@@ -244,10 +244,6 @@ void FEngine::init() {
mActiveFeatureLevel = std::min(mActiveFeatureLevel, driverApi.getFeatureLevel());
#ifndef FILAMENT_ENABLE_FEATURE_LEVEL_0
assert_invariant(mActiveFeatureLevel > FeatureLevel::FEATURE_LEVEL_0);
#endif
slog.i << "Backend feature level: " << int(driverApi.getFeatureLevel()) << io::endl;
slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl;
@@ -332,15 +328,12 @@ void FEngine::init() {
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0
if (UTILS_UNLIKELY(mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0)) {
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
defaultMaterialBuilder.package(
MATERIALS_DEFAULTMATERIAL0_DATA, MATERIALS_DEFAULTMATERIAL0_SIZE);
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast<FEngine*>(this)));
} else
#endif
{
} else {
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
@@ -1273,9 +1266,6 @@ Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfi
// This value gets validated during driver creation, so pass it through
config.driverHandleArenaSizeMB = config.driverHandleArenaSizeMB;
config.stereoscopicEyeCount =
std::clamp(config.stereoscopicEyeCount, uint8_t(1), CONFIG_MAX_STEREOSCOPIC_EYES);
return config;
}
-4
View File
@@ -184,10 +184,6 @@ public:
bool isStereoSupported() const noexcept { return getDriver().isStereoSupported(); }
static size_t getMaxStereoscopicEyes() noexcept {
return CONFIG_MAX_STEREOSCOPIC_EYES;
}
PostProcessManager const& getPostProcessManager() const noexcept {
return mPostProcessManager;
}
-3
View File
@@ -260,9 +260,6 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS,
(bool)powerVrShaderWorkarounds });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT,
(int)engine.getConfig().stereoscopicEyeCount });
if (UTILS_UNLIKELY(engine.getShaderLanguage() == ShaderLanguage::ESSL1)) {
// The actual value of this spec-constant is set in the OpenGLDriver backend.
mSpecializationConstants.push_back({
+8 -6
View File
@@ -267,16 +267,16 @@ void FScene::prepare(utils::JobSystem& js,
// in the code below, we only transform directions, so the translation of the
// world transform is irrelevant, and we don't need to use getWorldTransformAccurate()
mat3 const worldDirectionTransform =
mat3::getTransformForNormals(tcm.getWorldTransformAccurate(ti).upperLeft());
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
float3 const localDirection = worldDirectionTransform * lcm.getLocalDirection(li);
double3 const shadowLocalDirection = params.options.transform * localDirection;
float3 const localDirection = lcm.getLocalDirection(li);
float3 const shadowLocalDirection = params.options.transform * localDirection;
mat3 const worldDirectionTransform = tcm.getWorldTransformAccurate(ti).upperLeft();
mat3 const shaderWorldTransform = worldTransform.upperLeft() * worldDirectionTransform;
// using mat3::getTransformForNormals handles non-uniform scaling
// note: in the common case of the rigid-body transform, getTransformForNormals() returns
// identity.
mat3 const worlTransformNormals = mat3::getTransformForNormals(worldTransform.upperLeft());
mat3 const worlTransformNormals = mat3::getTransformForNormals(shaderWorldTransform);
double3 const d = worlTransformNormals * localDirection;
double3 const s = worlTransformNormals * shadowLocalDirection;
@@ -290,8 +290,10 @@ void FScene::prepare(utils::JobSystem& js,
// is pointing down, which is a common case for lights. See ShadowMap.cpp.
return transpose(mat3::lookTo(direction, double3{ 1, 0, 0 }));
};
double3 const worldDirection =
mat3::getTransformForNormals(worldDirectionTransform) * shadowLocalDirection;
double3 const worldOrigin = transpose(worldTransform.upperLeft()) * worldTransform[3].xyz;
mat3 const Mv = getMv(shadowLocalDirection);
mat3 const Mv = getMv(worldDirection);
double2 const lsReferencePoint = (Mv * worldOrigin).xy;
constexpr float inf = std::numeric_limits<float>::infinity();
+1 -4
View File
@@ -118,12 +118,9 @@ FSkybox::FSkybox(FEngine& engine, const Builder& builder) noexcept
FMaterial const* FSkybox::createMaterial(FEngine& engine) {
Material::Builder builder;
#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0
if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == Engine::FeatureLevel::FEATURE_LEVEL_0)) {
builder.package(MATERIALS_SKYBOX0_DATA, MATERIALS_SKYBOX0_SIZE);
} else
#endif
{
} else {
builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE);
}
auto material = builder.build(engine);
+3 -1
View File
@@ -1126,7 +1126,9 @@ View::PickingQuery& FView::pick(uint32_t x, uint32_t y, backend::CallbackHandler
return *pQuery;
}
void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept {
void FView::setStereoscopicOptions(const StereoscopicOptions& options) {
ASSERT_PRECONDITION(!options.enabled || mIsStereoSupported,
"Stereo rendering is not supported.");
mStereoscopicOptions = options;
}
+2 -4
View File
@@ -168,9 +168,7 @@ public:
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 mIsStereoSupported && mStereoscopicOptions.enabled;
}
bool hasInstancedStereo() const noexcept { return mStereoscopicOptions.enabled; }
FrameGraphId<FrameGraphTexture> renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, math::float4 const& userTime,
@@ -194,7 +192,7 @@ public:
bool isStencilBufferEnabled() const noexcept { return mStencilBufferEnabled; }
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
void setStereoscopicOptions(StereoscopicOptions const& options);
FCamera const* getDirectionalLightCamera() const noexcept {
return mShadowMapManager.getDirectionalLightCamera();
+1 -1
View File
@@ -527,7 +527,7 @@ private:
template<typename Data, typename Setup, typename Execute>
FrameGraphPass<Data>& FrameGraph::addPass(char const* name, Setup setup, Execute&& execute) {
static_assert(sizeof(Execute) < 2048, "Execute() lambda is capturing too much data.");
static_assert(sizeof(Execute) < 1024, "Execute() lambda is capturing too much data.");
// create the FrameGraph pass
auto* const pass = mArena.make<FrameGraphPassConcrete<Data, Execute>>(std::forward<Execute>(execute));
+6 -6
View File
@@ -82,22 +82,22 @@ uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder
RenderPassData data;
data.name = name;
data.descriptor = descriptor;
FrameGraphRenderPass::Attachments const& attachments = data.descriptor.attachments;
// retrieve the ResourceNode of the attachments coming to us -- this will be used later
// to compute the discard flags.
DependencyGraph const& dependencyGraph = fg.getGraph();
auto incomingEdges = dependencyGraph.getIncomingEdges(this);
auto outgoingEdges = dependencyGraph.getOutgoingEdges(this);
for (size_t i = 0; i < RenderPassData::ATTACHMENT_COUNT; i++) {
FrameGraphId<FrameGraphTexture> const& handle =
data.descriptor.attachments.array[i];
if (handle) {
data.attachmentInfo[i] = handle;
if (descriptor.attachments.array[i]) {
data.attachmentInfo[i] = attachments.array[i];
// TODO: this is not very efficient
auto incomingPos = std::find_if(incomingEdges.begin(), incomingEdges.end(),
[&dependencyGraph, handle]
[&dependencyGraph, handle = descriptor.attachments.array[i]]
(DependencyGraph::Edge const* edge) {
ResourceNode const* node = static_cast<ResourceNode const*>(
dependencyGraph.getNode(edge->from));
@@ -111,7 +111,7 @@ uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder
}
// this could be either outgoing or incoming (if there are no outgoing)
data.outgoing[i] = fg.getActiveResourceNode(handle);
data.outgoing[i] = fg.getActiveResourceNode(descriptor.attachments.array[i]);
if (data.outgoing[i] == data.incoming[i]) {
data.outgoing[i] = nullptr;
}
+1 -1
View File
@@ -35,7 +35,7 @@ vertex {
fragment {
void postProcess(inout PostProcessInputs postProcess) {
#if FILAMENT_EFFECTIVE_VERSION == 100
#if __VERSION__ == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.49.0"
spec.version = "1.45.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.49.0/filament-v1.49.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.45.0/filament-v1.45.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -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 = 49;
static constexpr size_t MATERIAL_VERSION = 45;
/**
* Supported shading models
@@ -66,7 +66,6 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_POWER_VR_SHADER_WORKAROUNDS = 5,
CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6,
CONFIG_DEBUG_FROXEL_VISUALIZATION = 7,
CONFIG_STEREO_EYE_COUNT = 8,
};
// This value is limited by UBO size, ES3.0 only guarantees 16 KiB.
@@ -76,8 +75,7 @@ constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1;
// The number of specialization constants that Filament reserves for its own use. These are always
// the first constants (from 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1).
// Updating this value necessitates a material version bump.
constexpr size_t CONFIG_MAX_RESERVED_SPEC_CONSTANTS = 16;
constexpr size_t CONFIG_MAX_RESERVED_SPEC_CONSTANTS = 8;
// The maximum number of shadowmaps.
// There is currently a maximum limit of 128 shadowmaps.
@@ -122,10 +120,8 @@ constexpr size_t CONFIG_MAX_BONE_COUNT = 256;
// Furthermore, this is constrained by CONFIG_MINSPEC_UBO_SIZE (16 bytes per morph target).
constexpr size_t CONFIG_MAX_MORPH_TARGET_COUNT = 256;
// The max number of eyes supported in stereoscopic mode.
// The number of eyes actually rendered is set at Engine creation time, see
// Engine::Config::stereoscopicEyeCount.
constexpr uint8_t CONFIG_MAX_STEREOSCOPIC_EYES = 4;
// The number of eyes in stereoscopic mode.
constexpr uint8_t CONFIG_STEREOSCOPIC_EYES = 2;
} // namespace filament
@@ -46,6 +46,7 @@ public:
using Format = backend::SamplerFormat;
using Precision = backend::Precision;
using SamplerParams = backend::SamplerParams;
using SamplerTransferFunction = backend::SamplerTransferFunction;
struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init)
utils::CString name; // name of this sampler
@@ -55,6 +56,7 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample; // multisample capable
SamplerTransferFunction transferFunction;
};
class Builder {
@@ -73,6 +75,7 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample = false; // multisample capable
SamplerTransferFunction transferFunction;
};
// Give a name to this sampler interface block
@@ -83,7 +86,8 @@ public:
// Add a sampler
Builder& add(std::string_view samplerName, Type type, Format format,
Precision precision = Precision::MEDIUM,
bool multisample = false) noexcept;
bool multisample = false,
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED) noexcept;
// Add multiple samplers
Builder& add(std::initializer_list<ListEntry> list) noexcept;
@@ -76,7 +76,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::mat4f worldFromViewMatrix; // clip view -> world : model matrix
math::mat4f clipFromViewMatrix; // clip <- view world : projection matrix
math::mat4f viewFromClipMatrix; // clip -> view world : inverse projection matrix
math::mat4f clipFromWorldMatrix[CONFIG_MAX_STEREOSCOPIC_EYES]; // clip <- view <- world
math::mat4f clipFromWorldMatrix[CONFIG_STEREOSCOPIC_EYES]; // clip <- view <- world
math::mat4f worldFromClipMatrix; // clip -> view -> world
math::mat4f userWorldFromWorldMatrix; // userWorld <- world
math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE
@@ -203,7 +203,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float es2Reserved2;
// bring PerViewUib to 2 KiB
math::float4 reserved[40];
math::float4 reserved[48];
};
// 2 KiB == 128 float4s
@@ -44,10 +44,10 @@ SamplerInterfaceBlock::Builder::stageFlags(backend::ShaderStageFlags stageFlags)
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::string_view samplerName, Type type, Format format,
Precision precision, bool multisample) noexcept {
Precision precision, bool multisample, SamplerTransferFunction transferFunction) noexcept {
mEntries.push_back({
{ samplerName.data(), samplerName.size() }, { },
uint8_t(mEntries.size()), type, format, precision, multisample });
uint8_t(mEntries.size()), type, format, precision, multisample, transferFunction });
return *this;
}
@@ -58,7 +58,7 @@ SamplerInterfaceBlock SamplerInterfaceBlock::Builder::build() {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::initializer_list<ListEntry> list) noexcept {
for (auto& e : list) {
add(e.name, e.type, e.format, e.precision, e.multisample);
add(e.name, e.type, e.format, e.precision, e.multisample, e.transferFunction);
}
return *this;
}
-1
View File
@@ -39,7 +39,6 @@ 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")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
+2
View File
@@ -31,6 +31,7 @@ using SamplerType = MaterialBuilder::SamplerType;
using SubpassType = MaterialBuilder::SubpassType;
using SamplerFormat = MaterialBuilder::SamplerFormat;
using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using SamplerTransferFunction = MaterialBuilder::SamplerTransferFunction;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
@@ -75,6 +76,7 @@ private:
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string, SamplerTransferFunction> mStringToSamplerTransferFunction;
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
+12 -13
View File
@@ -135,7 +135,6 @@ protected:
Optimization mOptimization = Optimization::PERFORMANCE;
bool mPrintShaders = false;
bool mGenerateDebugInfo = false;
bool mIncludeEssl1 = true;
utils::bitset32 mShaderModels;
struct CodeGenParams {
ShaderModel shaderModel;
@@ -240,6 +239,7 @@ public:
using SubpassType = filament::backend::SubpassType;
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using SamplerTransferFunction = filament::backend::SamplerTransferFunction;
using Precision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
using FeatureLevel = filament::backend::FeatureLevel;
@@ -272,9 +272,6 @@ public:
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;
@@ -311,11 +308,8 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT,
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -622,16 +616,20 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p,
SamplerTransferFunction tf)
: name(paramName), size(1), precision(p), samplerType(t), format(f),
parameterType(SAMPLER), transferFunction(tf) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
: name(paramName), size(typeSize), uniformType(t), precision(p),
parameterType(UNIFORM) { }
// Subpass
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
: name(paramName), size(1), precision(p), subpassType(t), format(f),
parameterType(SUBPASS) { }
utils::CString name;
size_t size;
@@ -640,6 +638,7 @@ public:
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
SamplerTransferFunction transferFunction;
enum {
INVALID,
UNIFORM,
+11
View File
@@ -117,6 +117,17 @@ std::unordered_map<std::string, ParameterPrecision>& Enums::getMap<ParameterPrec
return mStringToSamplerPrecision;
};
std::unordered_map<std::string, SamplerTransferFunction> Enums::mStringToSamplerTransferFunction = {
{ "undefined", SamplerTransferFunction::UNDEFINED },
{ "identity", SamplerTransferFunction::IDENTITY },
{ "srgbToLinear", SamplerTransferFunction::SRGB_TO_LINEAR },
};
template <>
std::unordered_map<std::string, SamplerTransferFunction>& Enums::getMap<SamplerTransferFunction>() noexcept {
return mStringToSamplerTransferFunction;
};
std::unordered_map<std::string, OutputTarget> Enums::mStringToOutputTarget = {
{ "color", OutputTarget::COLOR },
{ "depth", OutputTarget::DEPTH }
+3 -17
View File
@@ -23,7 +23,6 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include "backend/DriverEnums.h"
#include "sca/builtinResource.h"
#include "sca/GLSLTools.h"
@@ -33,7 +32,6 @@
#include "MetalArgumentBuffer.h"
#include "SpirvFixup.h"
#include "utils/ostream.h"
#include <filament/MaterialEnums.h>
@@ -397,9 +395,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
break;
case MaterialBuilder::Optimization::SIZE:
case MaterialBuilder::Optimization::PERFORMANCE:
if (!fullOptimization(tShader, config, internalConfig)) {
return false;
}
fullOptimization(tShader, config, internalConfig);
break;
}
@@ -482,7 +478,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
}
}
bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
void GLSLPostProcessor::fullOptimization(const TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
SpirvBlob spirv;
@@ -550,29 +546,19 @@ bool 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.
// According to EXT_clip_cull_distance, gl_ClipDistance can be
// "implicitly sized by indexing it only with integral constant expressions".
std::string& str = *internalConfig.glslOutput;
const std::string clipDistanceDefinition = "out float gl_ClipDistance[2];";
const std::string clipDistanceDefinition = "out float gl_ClipDistance[1];";
size_t const found = str.find(clipDistanceDefinition);
if (found != std::string::npos) {
str.replace(found, clipDistanceDefinition.length(), "");
}
}
return true;
}
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
+1 -1
View File
@@ -93,7 +93,7 @@ private:
ShaderMinifier minifier;
};
bool fullOptimization(const glslang::TShader& tShader,
void fullOptimization(const glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;
void preprocessOptimization(glslang::TShader& tShader,
+14 -23
View File
@@ -139,13 +139,13 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics,
glTargetLanguage,
effectiveFeatureLevel,
});
if (mIncludeEssl1
&& featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0
&& shaderModel == ShaderModel::MOBILE) {
if (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,
glTargetLanguage,
TargetLanguage::GLSL,
filament::backend::FeatureLevel::FEATURE_LEVEL_0
});
}
@@ -248,19 +248,14 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type,
return parameter(name, 1, type, precision);
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision) noexcept {
SamplerFormat format, ParameterPrecision precision,
SamplerTransferFunction transferFunction) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, samplerType, format, precision };
mParameters[mParameterCount++] = { name, samplerType, format, precision, transferFunction };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept {
return parameter(name, samplerType, SamplerFormat::FLOAT, precision);
}
template<typename T, typename>
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType type, T defaultValue) {
auto result = std::find_if(mConstants.begin(), mConstants.end(), [name](const Constant& c) {
@@ -556,7 +551,8 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
param.samplerType, param.format, param.precision);
param.samplerType, param.format, param.precision, /*multisample=*/false,
param.transferFunction);
} else if (param.isUniform()) {
ibb.add({{{ param.name.data(), param.name.size() },
uint32_t(param.size == 1u ? 0u : param.size), param.uniformType,
@@ -912,8 +908,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
if (targetApi == TargetApi::OPENGL) {
if (targetLanguage == TargetLanguage::SPIRV) {
ShaderGenerator::fixupExternalSamplers(shaderModel, shader, featureLevel,
info);
ShaderGenerator::fixupExternalSamplers(shaderModel, shader, info);
}
}
@@ -1163,6 +1158,7 @@ error:
mVariantFilter |= uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING);
mVariantFilter |= uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING);
mVariantFilter |= uint32_t(UserVariantFilterBit::SHADOW_RECEIVER);
mVariantFilter |= uint32_t(UserVariantFilterBit::SKINNING);
mVariantFilter |= uint32_t(UserVariantFilterBit::VSM);
mVariantFilter |= uint32_t(UserVariantFilterBit::SSR);
mVariantFilter |= uint32_t(UserVariantFilterBit::STE);
@@ -1421,15 +1417,15 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
uniforms.push_back({
"objectUniforms.data[0].morphTargetCount",
offsetof(PerRenderableUib, data[0].morphTargetCount), 1,
UniformType::INT });
UniformType::UINT });
uniforms.push_back({
"objectUniforms.data[0].flagsChannels",
offsetof(PerRenderableUib, data[0].flagsChannels), 1,
UniformType::INT });
UniformType::UINT });
uniforms.push_back({
"objectUniforms.data[0].objectId",
offsetof(PerRenderableUib, data[0].objectId), 1,
UniformType::INT });
UniformType::UINT });
uniforms.push_back({
"objectUniforms.data[0].userData",
offsetof(PerRenderableUib, data[0].userData), 1,
@@ -1565,9 +1561,4 @@ MaterialBuilder& MaterialBuilder::noSamplerValidation(bool enabled) noexcept {
return *this;
}
MaterialBuilder& MaterialBuilder::includeEssl1(bool enabled) noexcept {
mIncludeEssl1 = enabled;
return *this;
}
} // namespace filamat
@@ -65,6 +65,7 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener& f) {
f.writeUint8(static_cast<uint8_t>(sInfo.format));
f.writeUint8(static_cast<uint8_t>(sInfo.precision));
f.writeBool(sInfo.multisample);
f.writeUint8(static_cast<uint8_t>(sInfo.transferFunction));
}
}
-2
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
#include "glslang/Include/intermediate.h"
const TBuiltInResource DefaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
+10 -50
View File
@@ -151,36 +151,6 @@ 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);
@@ -283,13 +253,10 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
generateSpecializationConstant(out, "CONFIG_POWER_VR_SHADER_WORKAROUNDS",
+ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS, false);
generateSpecializationConstant(out, "CONFIG_STEREO_EYE_COUNT",
+ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, 2);
// CONFIG_MAX_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to
// CONFIG_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to
// be explicitly, statically defined (as in #define). Otherwise (using const int for
// example), we'd run into a GPU crash.
out << "#define CONFIG_MAX_STEREOSCOPIC_EYES " << (int) CONFIG_MAX_STEREOSCOPIC_EYES << "\n";
out << "#define CONFIG_STEREOSCOPIC_EYES " << (int) CONFIG_STEREOSCOPIC_EYES << "\n";
if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
// On ES2 since we don't have post-processing, we need to emulate EGL_GL_COLORSPACE_KHR,
@@ -301,9 +268,8 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
out << '\n';
out << SHADERS_COMMON_DEFINES_GLSL_DATA;
if (material.featureLevel == FeatureLevel::FEATURE_LEVEL_0 &&
(mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0
|| mTargetLanguage == TargetLanguage::SPIRV)) {
if (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 &&
material.featureLevel == FeatureLevel::FEATURE_LEVEL_0) {
// 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
@@ -491,14 +457,11 @@ 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 (generate_essl3_code) {
out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str();
} else {
if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor";
} else {
out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str();
}
out << "\n#define FRAG_OUTPUT_MATERIAL_TYPE" << index << " " << materialTypeString;
out << "\n#define FRAG_OUTPUT_PRECISION" << index << " " << precisionString;
@@ -506,7 +469,7 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type,
out << "\n#define FRAG_OUTPUT_SWIZZLE" << index << " " << swizzleString;
out << "\n";
if (generate_essl3_code) {
if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
out << "\nlayout(location=" << index << ") out " << precisionString << " "
<< typeString << " output_" << name.c_str() << ";\n";
}
@@ -778,7 +741,7 @@ io::sstream& CodeGenerator::generateSubpass(io::sstream& out, SubpassInfo subpas
}
void CodeGenerator::fixupExternalSamplers(
std::string& shader, SamplerInterfaceBlock const& sib, FeatureLevel featureLevel) noexcept {
std::string& shader, SamplerInterfaceBlock const& sib) noexcept {
auto const& infos = sib.getSamplerInfoList();
if (infos.empty()) {
return;
@@ -813,10 +776,7 @@ void CodeGenerator::fixupExternalSamplers(
while (shader[index] != '\n') index++;
index++;
const char *extensionLine = (featureLevel >= FeatureLevel::FEATURE_LEVEL_1)
? "#extension GL_OES_EGL_image_external_essl3 : require\n\n"
: "#extension GL_OES_EGL_image_external : require\n\n";
shader.insert(index, extensionLine);
shader.insert(index, "#extension GL_OES_EGL_image_external_essl3 : require\n");
}
}
+1 -2
View File
@@ -161,8 +161,7 @@ public:
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderStage type);
static void fixupExternalSamplers(
std::string& shader, filament::SamplerInterfaceBlock const& sib,
FeatureLevel featureLevel) noexcept;
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
// These constants must match the equivalent in MetalState.h.
// These values represent the starting index for uniform, ssbo, and sampler group [[buffer(n)]]

Some files were not shown because too many files have changed in this diff Show More