Compare commits

..
Author SHA1 Message Date
Eliza Velasquez ab552d3619 wip: color space stuff 2023-11-13 14:32:31 -08:00
Eliza Velasquez 4fd7c418e5 Remove invalid FeatureLevel0Sampler3D test 2023-11-01 21:17:22 +00:00
Eliza Velasquez a3fdca7997 Fix basic post processing on ES2
First, this commit introduces some very simple bugfixes regarding ES2
compatibility related to postprocessing.

Second, this commit adds support for creating textures specified as R8, SRGB8,
and SRGB8_A8 in ES2. R8 is trivial: just use GL_LUMINANCE instead. The sRGB
formats, however, are maybe a bit more controversial. As implemented, they
instead just use the equivalent non-sRGB formats. This is of course technically
incorrect. There are a few approaches to how to add sRGB compatibility for ES2
that I can think of.

1. Do a bunch of complex shader nonsense in matc. Maybe even traversing the AST
and ensuring any texture lookup of a texture flagged as sRGB uses some
compatibility function. This would require static analysis to track if samplers
are reassigned to another variable, for example. This of course also breaks down
if you don't know at compile time if the shader will receive an RGB or an sRGB
sampler, or if the shader should be able to support both RGB or sRGB samplers.
Really only worth mentioning here for the sake of completion.

2. You could also generate simple compatibility functions to look up each
sampler, which would only apply to FL0 materials.

First, we would have to extend the material format to be able to explicitly
"color" a sampler as sRGB or not, like:

```
parameters : [
    {
        type : sampler2d,
        name : albedo,
        precision : medium,
        colorSpace : srgb,
    },
    {
        type : sampler2d,
        name : normal,
        precision : medium,
        colorSpace : linear,
    }
],
```

Then, the following GLSL code would be generated.

```glsl
\#if __VERSION__ == 100
vec4 texture_albedo(vec2 position) {
  return sRGBtoLinear(texture2D(materialParams_albedo, position));
}
vec4 texture_normal(vec2 position) {
  return texture2D(materialParams_albedo, position);
}
\#else
vec4 texture_albedo(vec2 position) {
  return texture(materialParams_albedo, position);
}
vec4 texture_normal(vec2 normal) {
  return texture(materialParams_normal, position);
}
\#endif
```

Finally, at runtime, if a sampler is "colored" one way or the other, we would
verify that only the appropriate kinds of samplers are bound.

I'm actually very partial to this solution. Since sRGB compatibility is only a
concern on ES2, we can generate this code only for FL0 shaders, which already
require GLSL shader authors to care about ESSL 1.0 compatibility by calling the
appropriate `textureXX` functions. Additionally, it provides a layer of
high-level validation that texture lookups are correct, even if a real ES2
context is not available on the device being tested.

3. Leave it entirely up to the client. (What this commit does.) This leaves
client code ripe for making mistakes, but luckily, we can go back and do
solution 2 whenever. If specifying a color space for a sampler remains optional,
then if this feature is retrofitted in the future, client code will continue to
compile.
2023-11-01 18:23:27 +00:00
Eliza Velasquez 7f1704481e Force post-processing materials to be unlit
This both fixes compilation of FL0 ES2 materials and unsaddles post-processing
materials with a lot of requirements added on by being considered lit.
2023-11-01 18:23:27 +00:00
Eliza Velasquez e6b962b038 Further improve feature level 0 support
Enable a limited subset of materials in PostProcessManager for FL0.

Create new function Material::getFeatureLevel() in C++ and Java.

Create missing Material::getReflectionMode() method in Java.
2023-11-01 18:23:27 +00:00
Eliza Velasquez 668fb07ac2 matc: support basic post-process materials in FL0
This change hard-codes writing the post process output at index 0 (i.e. color)
to gl_FragColor when generating ESSL 1.0 shaders. Any other outputs (besides
depth) are discarded with a warning, but as far as I can tell, no such cases yet
exist in Filament.
2023-11-01 18:23:27 +00:00
Eliza Velasquez f64eef02a3 Miscellaneous feature level 0 fixes
Fix edge case where an empty struct could be generated in an ESSL 1.0 shader.

Include _maskThreshold and _doubleSided in ESSL 1.0 shaders.

Add GL_OES_standard_derivatives extension to ESSL 1.0 shaders. According to
gpuinfo.org, this has 96% device coverage and supports both Mali-400 and Adreno
(TM) 304.

Remove 3D sampler support from ESSL 1.0 shaders. This extension is only
supported by 62% of devices.

Change filagui material to a FL0 material.
2023-11-01 18:23:27 +00:00
Eliza Velasquez 239e98ccec Add basic Emacs support 2023-11-01 17:28:26 +00:00
Mathias Agopian 919cfae6b2 improve ShadowMapManager slightly
don't use FixedCapacityVector to store pointers to active shadowmaps,
that's just not needed. They're all stored un a static array already
and directional and spot shadows are partitioned. 

This saves a couple heap allocations as well a an pointer dereference.
2023-11-01 09:59:32 -07:00
Mathias Agopian 0b9389430b avoid to dereference nullable objects (#7322)
Add missing @NonNull annotations

FIXES=[308443790]
2023-10-31 16:12:03 -07:00
Eliza Velasquez 1d157677d1 Add proper language server support
compile_commands.json was being generated, but hidden away inside of the cmake
build directories. This change makes build.sh link it to the main project dir
and adds some associated .gitignore entries. Now compile_commands.json is
properly read when starting clangd from Emacs, for example, and probably many
other editors.
2023-10-31 22:54:38 +00:00
Powei Feng ef3f0cb326 vk: assert updateImage is called with non-empty size (#7315)
BUG=303073160
2023-10-31 10:36:07 -07:00
Mathias Agopian fcf53f2c3e fix SSR artifact when enabling SSR
When we enable SSR the first time, the SSR buffer is not initialized,
this can result in the color pass fragment shader aborting, which in 
turn prevents the SSR history buffer from being initialized (since 
it's made from the result of the color pass), repeating the cycle.

In some other case, the system somehow recovers but we still see a
flicker when enabling SSR.

The solution here is to disable SSR in the shader until the history
buffer is ready (i.e. a frame later).
2023-10-31 10:32:14 -07:00
Mathias Agopian d6fda03b06 fix logging typo 2023-10-30 15:08:56 -07:00
Mathias Agopian a76addd2bf disable multiple context support on WGL
The reason is that some implementations of WGL require all contexts to
be created on the same thread, which we're not necessarily doing here.

fixes #7078
2023-10-27 15:53:48 -07:00
Mathias Agopian b1f7731dbe fix max lod level computation in IBLPrefilter
FIXES=[308012116]
2023-10-27 15:28:24 -07:00
Mathias Agopian e3e12dbf73 Make sure to unbind imported textures when destroying one
fixes #7280
2023-10-27 15:28:07 -07:00
Benjamin Doherty 31a75029f0 Update RELEASE_GUIDE with npm and CocoaPods instructions 2023-10-27 16:22:47 -04:00
Mathias Agopian e5c24cc718 Fix dangling pointer where destroying a samplergroup 2023-10-27 13:10:21 -07:00
Mathias Agopian 2b86c8df6f cleanup and better bone weight checks
- only check/log in debug builds
- use epsilon = 2e-7 * double(tempPairCount)
- compute boneWeightsSum in double
- don't modify the weights if they're within the threshold

FIXES=[306565054]
2023-10-27 11:33:03 -07:00
Mathias Agopian 4d8e6eefa1 don't use a spinlock for the HandleArena
We've seen hangs/ANR that are not well understood on that spinlock, so
for now we're going back to mutexes, which, on android, are very 
efficient under low contention (no syscall).

FIXES=[308029108]
2023-10-27 11:32:38 -07:00
Ben Doherty 8aeec2ba35 Fix iOS transparent rendering sample (#7300) 2023-10-27 13:44:28 -04:00
Eliza Velasquez 4eb4fd5aba Explicitly prevent upgrading from feature level 0 2023-10-26 22:20:57 +00:00
Eliza Velasquez 56355231bd Allow explicitly initializing at feature level 0
This change does three main things. First, it adds an option to the Engine
Builder to pick the feature level at which to instantiate Filament. The only
real practical purpose of allowing this is to be able to instantiate at feature
level 0. Secondly, it allows feature level 0 to properly work on non-ES2
devices. Thirdly, it changes both Android and desktop hellotriangle samples to
explicitly opt-in to feature level 0.

Unfortunately, feature levels are used in two different, somewhat contradictory
ways presently in Filament, which can make reasoning about this change a bit
confusing. From a client perspective, feature levels refer to buckets of
capabilities which are guaranteed to be supported. Internally, there is a
separate "feature level" stored internally at the Driver subclass level which
generally corresponds to the maximum supported feature level, but is also
referenced when activating workarounds for limited devices. For example, Uniform
Buffer Objects are not supported in ES2, however, Filament supports emulating
them such that the client does not need to care at all; a supported feature is a
supported feature. But internally, Filament uses this "Driver" feature level to
determine whether or not a given workaround is needed. There were several cases
where the "active feature level" was being examined in order to activate these
workarounds rather than the "driver feature level", which was incorrect.

Why should non-ES2-only devices want to activate feature level 0? Allowing this
behavior 1. makes feature level 0 more consistent with the behavior of other
feature levels and 2. allows clients a layer of validation that their software
will work on all devices supported by Filament if they explicitly opt into it.

Consistency: Filament guarantees that any given device which supports a given
feature level will also support running on every feature level below, except for
feature level 0. This change removes that exception.

Validation: It's not perfect, and there will likely be bugs and unexpected
differences in behavior between ES2 and non-ES2 devices that crop up in the
future between two devices running on the same feature level. However, it's at
least a basic high level layer of validation that enables more rapid testing
workflows directly via desktop versions of Filament rather than having to fiddle
with something like ANGLE to get perfect GLES 2.0 compliance. Additionally, it
expands options for automated testing (with the same caveats).

This change has been tested on both the desktop and Android versions of
hellotriangle.
2023-10-26 22:20:57 +00:00
Powei Feng 9ccb8fce31 matdbg: UI refresh (#7301) 2023-10-26 14:14:01 -07:00
Mathias Agopian e674420e9c improvements to EntityManagers and Filament APIs (#7302)
* prevent public classes from being created on the stack

- we used to to this by deleting operator delete, but this prevented
  the internal "F" classes from being virtual; which can be useful
  when using EntityManger::Listener.
  now we just make the destructor protected in each class.

- EntityManger::Listener now has a virtual destructor so that
  objects could be correctly destroyed from Listener*

* improve EntityManger and Component managers

- all component managers now have the same "base" API
    - getComponentCount()
	- empty()
    - getEntity()
    - getEntities()

- Scene now has getEntityCount()

- EntityManager now has getEntityCount()

- all component manager implement gc() the same way, by calling destroy()

- SingleInstanceComponentManager::gc() that calls removeComponent() has
  been removed because it's dangerous. removeComponent() is often
  not enough, some additional cleanup might be needed.
2023-10-26 13:10:43 -07:00
Mathias Agopian 8a9cbcfb99 fix a Transform component leak in CameraManager
CameraManager creates a Transform component for each Camera component
is not already present. However, it didn't destroy the transform
component when it's itself destroyed. the leaked transform component
would eventually be garbage collected, but caused significant
slow down and memory pressure. This is because camera components are
created every frame for the shadow maps.

FIXES=[303914944]
2023-10-26 13:05:35 -07:00
Mathias Agopian f0d5cd3fa1 improve BlobCache API and compatibility
- the insert and retrieve handlers can now be set/unset independently.
  this could be useful for debugging.

- program caching is disabled if the GL implementation doesn't support it.

- removed unused code

FIXES=[307549547]
2023-10-25 22:15:25 -07:00
90 changed files with 1059 additions and 1464 deletions
+7
View File
@@ -0,0 +1,7 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(apheleia-inhibit . t))))
+2
View File
@@ -16,3 +16,5 @@ settings.json
test*.png
test*.json
results
/compile_commands.json
/.cache
+9
View File
@@ -7,3 +7,12 @@ for next branch cut* header.
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.45.1'
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.45.1'
pod 'Filament', '~> 1.45.0'
```
### Snapshots
+12
View File
@@ -128,3 +128,15 @@ Navigate to [Filament's release
workflow](https://github.com/google/filament/actions/workflows/release.yml). Hit the _Run workflow_
dropdown. Modify _Platform to build_ and _Release tag to build_, then hit _Run workflow_. This will
initiate a new release run.
## 11. Kick off the npm and CocoaPods release jobs
Navigate to [Filament's npm deploy
workflow](https://github.com/google/filament/actions/workflows/npm-deploy.yml).
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
release (for example, v1.42.2).
Navigate to [Filament's CocoaPods deploy
workflow](https://github.com/google/filament/actions/workflows/cocopods-deploy.yml).
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
release (for example, v1.42.2).
+1
View File
@@ -15,6 +15,7 @@ Instead, if you are authoring a PR for the main branch, add your release note to
- engine: New tone mapper: `AgXTonemapper`.
- matinfo: Add support for viewing ESSL 1.0 shaders
- engine: Add `Renderer::getClearOptions()` [b/243846268]
- engine: Fix stable shadows (again) when an IBL rotation is used
## v1.45.0
@@ -483,6 +483,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
builder->config(&config);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderFeatureLevel(
JNIEnv*, jclass, jlong nativeBuilder, jint ordinal) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->featureLevel((Engine::FeatureLevel)ordinal);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderSharedContext(
JNIEnv*, jclass, jlong nativeBuilder, jlong sharedContext) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
@@ -105,6 +105,22 @@ Java_com_google_android_filament_Material_nGetRefractionType(JNIEnv*, jclass,
return (jint) material->getRefractionType();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetReflectionMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getReflectionMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetFeatureLevel(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getFeatureLevel();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetVertexDomain(JNIEnv*, jclass,
@@ -209,6 +209,17 @@ public class Engine {
return this;
}
/**
* Sets the initial featureLevel for the Engine.
*
* @param featureLevel The feature level at which initialize Filament.
* @return A reference to this Builder for chaining calls.
*/
public Builder featureLevel(FeatureLevel featureLevel) {
nSetBuilderFeatureLevel(mNativeBuilder, featureLevel.ordinal());
return this;
}
/**
* Creates an instance of Engine
*
@@ -468,17 +479,23 @@ public class Engine {
}
/**
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
* active. The selected feature level must not be higher than the value returned by
* getActiveFeatureLevel() and it's not possible lower the active feature level.
* Activate all features of a given feature level. If an explicit feature level is not specified
* at Engine initialization time via {@link Builder#featureLevel}, the default feature level is
* {@link FeatureLevel#FEATURE_LEVEL_0} on devices not compatible with GLES 3.0; otherwise, the
* default is {@link FeatureLevel::FEATURE_LEVEL_1}. The selected feature level must not be
* higher than the value returned by {@link #getActiveFeatureLevel} and it's not possible lower
* the active feature level. Additionally, it is not possible to modify the feature level at all
* if the Engine was initialized at {@link FeatureLevel#FEATURE_LEVEL_0}.
*
* @param featureLevel the feature level to activate. If featureLevel is lower than
* getActiveFeatureLevel(), the current (higher) feature level is kept.
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
* is thrown, or the program is terminated if exceptions are disabled.
* @param featureLevel the feature level to activate. If featureLevel is lower than {@link
* #getActiveFeatureLevel}, the current (higher) feature level is kept. If
* featureLevel is higher than {@link #getSupportedFeatureLevel}, or if the
* engine was initialized at feature level 0, an exception is thrown, or the
* program is terminated if exceptions are disabled.
*
* @return the active feature level.
*
* @see Builder#featureLevel
* @see #getSupportedFeatureLevel
* @see #getActiveFeatureLevel
*/
@@ -1149,6 +1166,7 @@ public class Engine {
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
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);
}
@@ -21,6 +21,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Size;
import com.google.android.filament.proguard.UsedByNative;
import com.google.android.filament.Engine.FeatureLevel;
import java.nio.Buffer;
import java.util.ArrayList;
@@ -46,6 +47,8 @@ public class Material {
static final BlendingMode[] sBlendingModeValues = BlendingMode.values();
static final RefractionMode[] sRefractionModeValues = RefractionMode.values();
static final RefractionType[] sRefractionTypeValues = RefractionType.values();
static final ReflectionMode[] sReflectionModeValues = ReflectionMode.values();
static final FeatureLevel[] sFeatureLevelValues = FeatureLevel.values();
static final VertexDomain[] sVertexDomainValues = VertexDomain.values();
static final CullingMode[] sCullingModeValues = CullingMode.values();
static final VertexBuffer.VertexAttribute[] sVertexAttributeValues =
@@ -181,6 +184,18 @@ public class Material {
THIN
}
/**
* Supported reflection modes
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
* Lighting: reflections</a>
*/
public enum ReflectionMode {
DEFAULT,
SCREEN_SPACE
}
/**
* Supported types of vertex domains
*
@@ -437,6 +452,28 @@ public class Material {
return EnumCache.sRefractionTypeValues[nGetRefractionType(getNativeObject())];
}
/**
* Returns the reflection mode of this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
* Lighting: reflections</a>
*/
public ReflectionMode getReflectionMode() {
return EnumCache.sReflectionModeValues[nGetReflectionMode(getNativeObject())];
}
/**
* Returns the minimum required feature level for this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:featurelevel">
* General: featureLevel</a>
*/
public FeatureLevel getFeatureLevel() {
return EnumCache.sFeatureLevelValues[nGetFeatureLevel(getNativeObject())];
}
/**
* Returns the vertex domain of this material.
*
@@ -932,6 +969,8 @@ public class Material {
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial);
private static native int nGetRefractionMode(long nativeMaterial);
private static native int nGetRefractionType(long nativeMaterial);
private static native int nGetReflectionMode(long nativeMaterial);
private static native int nGetFeatureLevel(long nativeMaterial);
private static native int nGetParameterCount(long nativeMaterial);
@@ -227,8 +227,10 @@ public class UiHelper {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
}
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
mRenderCallback.onResized(width, height);
if (mRenderCallback != null) {
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
mRenderCallback.onResized(width, height);
}
}
@Override
@@ -298,7 +300,6 @@ public class UiHelper {
/**
* Checks whether we are ready to render into the attached surface.
*
* Using OpenGL ES when this returns true, will result in drawing commands being lost,
* HOWEVER, GLES state will be preserved. This is useful to initialize the engine.
*
@@ -343,7 +344,6 @@ public class UiHelper {
/**
* Controls whether the render target (SurfaceView or TextureView) is opaque or not.
* The render target is considered opaque by default.
*
* Must be called before calling {@link #attachTo(SurfaceView)}, {@link #attachTo(TextureView)},
* or {@link #attachTo(SurfaceHolder)}.
*
@@ -366,10 +366,8 @@ public class UiHelper {
* positioned above other surfaces but below the activity's surface. This property
* only has an effect when used in combination with {@link #setOpaque(boolean) setOpaque(false)}
* and does not affect TextureView targets.
*
* Must be called before calling {@link #attachTo(SurfaceView)}
* or {@link #attachTo(TextureView)}.
*
* Has no effect when using {@link #attachTo(SurfaceHolder)}.
*
* @param overlay Indicates whether the render target should be rendered below the activity's
@@ -390,7 +388,6 @@ public class UiHelper {
/**
* Associate UiHelper with a SurfaceView.
*
* As soon as SurfaceView is ready (i.e. has a Surface), we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
@@ -412,21 +409,23 @@ public class UiHelper {
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
SurfaceHolder holder, int format, int width, int height) {
@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 + ")");
mRenderCallback.onResized(width, height);
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
@@ -450,7 +449,6 @@ public class UiHelper {
/**
* Associate UiHelper with a TextureView.
*
* As soon as TextureView is ready (i.e. has a buffer), we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
@@ -463,7 +461,7 @@ public class UiHelper {
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
SurfaceTexture surfaceTexture, int width, int height) {
@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) {
@@ -478,40 +476,44 @@ public class UiHelper {
createSwapChain(surface);
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
SurfaceTexture surfaceTexture, int width, int height) {
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
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());
}
// 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(SurfaceTexture surfaceTexture) {
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
};
view.setSurfaceTextureListener(listener);
@@ -519,14 +521,15 @@ public class UiHelper {
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
if (surfaceTexture != null) {
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
}
}
}
}
/**
* Associate UiHelper with a SurfaceHolder.
*
* As soon as a Surface is created, we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
@@ -539,20 +542,22 @@ public class UiHelper {
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
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 + ")");
mRenderCallback.onResized(width, height);
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
@@ -587,7 +592,9 @@ public class UiHelper {
}
private void createSwapChain(@NonNull Surface surface) {
mRenderCallback.onNativeWindowChanged(surface);
if (mRenderCallback != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
mHasSwapChain = true;
}
@@ -595,7 +602,9 @@ public class UiHelper {
if (mRenderSurface != null) {
mRenderSurface.detach();
}
mRenderCallback.onDetachedFromSurface();
if (mRenderCallback != null) {
mRenderCallback.onDetachedFromSurface();
}
mHasSwapChain = false;
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.45.1
VERSION_NAME=1.45.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -110,7 +110,7 @@ class MainActivity : Activity() {
}
private fun setupFilament() {
engine = Engine.create()
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
@@ -120,13 +120,8 @@ class MainActivity : Activity() {
private fun setupView() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
// post-processing is not supported at feature level 0
view.isPostProcessingEnabled = false
} else {
// NOTE: Try to disable post-processing (tone-mapping, etc.) to see the difference
// view.isPostProcessingEnabled = false
}
// post-processing is not supported at feature level 0
view.isPostProcessingEnabled = false
// Tell the view which camera we want to use
view.camera = camera
+9
View File
@@ -191,6 +191,7 @@ function build_clean {
rm -Rf android/filamat-android/build android/filamat-android/.externalNativeBuild android/filamat-android/.cxx
rm -Rf android/gltfio-android/build android/gltfio-android/.externalNativeBuild android/gltfio-android/.cxx
rm -Rf android/filament-utils-android/build android/filament-utils-android/.externalNativeBuild android/filament-utils-android/.cxx
rm -f compile_commands.json
}
function build_clean_aggressive {
@@ -232,6 +233,8 @@ function build_desktop_target {
${ASAN_UBSAN_OPTION} \
${architectures} \
../..
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
../../compile_commands.json
fi
${BUILD_COMMAND} ${build_targets}
@@ -287,6 +290,8 @@ function build_webgl_with_target {
-DCMAKE_INSTALL_PREFIX="../webgl-${lc_target}/filament" \
-DWEBGL=1 \
../..
ln -sf "out/cmake-webgl-${lc_target}/compile_commands.json" \
../../compile_commands.json
${BUILD_COMMAND} ${BUILD_TARGETS}
)
fi
@@ -359,6 +364,8 @@ function build_android_target {
${MATOPT_OPTION} \
${VULKAN_ANDROID_OPTION} \
../..
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
fi
# We must always install Android libraries to build the AAR
@@ -591,6 +598,8 @@ function build_ios_target {
${MATDBG_OPTION} \
${MATOPT_OPTION} \
../..
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
fi
${BUILD_COMMAND}
+2 -22
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
@@ -237,6 +238,7 @@ 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
@@ -245,11 +247,6 @@ set(MATERIAL_SRCS
src/materials/vsmMipmap.mat
)
set(MATERIAL_ES2_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
# Embed the binary resource blob for materials.
get_resgen_vars(${RESOURCE_DIR} materials)
list(APPEND PRIVATE_HDRS ${RESGEN_HEADER})
@@ -315,23 +312,6 @@ foreach (mat_src ${MATERIAL_SRCS})
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (IS_MOBILE_TARGET AND FILAMENT_SUPPORTS_OPENGL)
foreach (mat_src ${MATERIAL_ES2_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(
@@ -154,6 +154,19 @@ enum class ShaderLanguage {
MSL = 3,
};
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
switch (shaderLanguage) {
case ShaderLanguage::ESSL1:
return "ESSL 1.0";
case ShaderLanguage::ESSL3:
return "ESSL 3.0";
case ShaderLanguage::SPIRV:
return "SPIR-V";
case ShaderLanguage::MSL:
return "MSL";
}
}
/**
* Bitmask for selecting render buffers
*/
@@ -343,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
*/
+12 -9
View File
@@ -373,16 +373,19 @@ constexpr inline GLenum getCullingMode(CullingMode mode) noexcept {
constexpr inline std::pair<GLenum, GLenum> textureFormatToFormatAndType(
TextureFormat format) noexcept {
switch (format) {
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::SRGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::SRGB8_A8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
case TextureFormat::DEPTH24_STENCIL8:
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
default: return { GL_NONE, GL_NONE };
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
default: return { GL_NONE, GL_NONE };
}
}
+22 -11
View File
@@ -147,17 +147,28 @@ OpenGLContext::OpenGLContext() noexcept {
#ifndef NDEBUG
// this is useful for development
slog.v
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" << gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" << gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_DRAW_BUFFERS" << gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE" << gets.max_renderbuffer_size << '\n'
<< "GL_MAX_SAMPLES" << gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS" << gets.max_texture_image_units << '\n'
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS" << gets.max_transform_feedback_separate_attribs << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE" << gets.max_uniform_block_size << '\n'
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS" << gets.max_uniform_buffer_bindings << '\n'
<< "GL_NUM_PROGRAM_BINARY_FORMATS" << gets.num_program_binary_formats << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" << gets.uniform_buffer_offset_alignment << '\n'
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = "
<< gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
<< gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_DRAW_BUFFERS = "
<< gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = "
<< gets.max_renderbuffer_size << '\n'
<< "GL_MAX_SAMPLES = "
<< gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS = "
<< gets.max_texture_image_units << '\n'
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
<< gets.max_transform_feedback_separate_attribs << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE = "
<< gets.max_uniform_block_size << '\n'
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS = "
<< gets.max_uniform_buffer_bindings << '\n'
<< "GL_NUM_PROGRAM_BINARY_FORMATS = "
<< gets.num_program_binary_formats << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = "
<< gets.uniform_buffer_offset_alignment << '\n'
;
flush(slog.v);
#endif
+25 -4
View File
@@ -1290,7 +1290,9 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
checkDimensions(rt->gl.color[i], color[i].level);
}
}
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
if (UTILS_LIKELY(!getContext().isES2())) {
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
}
CHECK_GL_ERROR(utils::slog.e)
}
#endif
@@ -1451,6 +1453,11 @@ void OpenGLDriver::destroySamplerGroup(Handle<HwSamplerGroup> sbh) {
DEBUG_MARKER()
if (sbh) {
GLSamplerGroup* sb = handle_cast<GLSamplerGroup*>(sbh);
for (auto& binding : mSamplerBindings) {
if (binding == sb) {
binding = nullptr;
}
}
destruct(sbh, sb);
}
}
@@ -1459,9 +1466,9 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
DEBUG_MARKER()
if (th) {
auto& gl = mContext;
GLTexture* t = handle_cast<GLTexture*>(th);
if (UTILS_LIKELY(!t->gl.imported)) {
auto& gl = mContext;
if (UTILS_LIKELY(t->usage & TextureUsage::SAMPLEABLE)) {
gl.unbindTexture(t->gl.target, t->gl.id);
if (UTILS_UNLIKELY(t->hwStream)) {
@@ -1479,6 +1486,8 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
if (t->gl.sidecarRenderBufferMS) {
glDeleteRenderbuffers(1, &t->gl.sidecarRenderBufferMS);
}
} else {
gl.unbindTexture(t->gl.target, t->gl.id);
}
destruct(th, t);
}
@@ -1775,6 +1784,10 @@ bool OpenGLDriver::isRenderTargetFormatSupported(TextureFormat format) {
// support more formats, but it requires querying GL_INTERNALFORMAT_SUPPORTED which is not
// available in OpenGL ES.
auto& gl = mContext;
if (UTILS_UNLIKELY(gl.isES2())) {
auto [es2format, type] = textureFormatToFormatAndType(format);
return es2format != GL_NONE && type != GL_NONE;
}
switch (format) {
// Core formats.
case TextureFormat::R8:
@@ -2269,8 +2282,16 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
return;
}
GLenum const glFormat = getFormat(p.format);
GLenum const glType = getType(p.type);
GLenum glFormat;
GLenum glType;
if (mContext.isES2()) {
auto formatAndType = textureFormatToFormatAndType(t->format);
glFormat = formatAndType.first;
glType = formatAndType.second;
} else {
glFormat = getFormat(p.format);
glType = getType(p.type);
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
@@ -212,6 +212,7 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
assert_invariant(binding < Program::SAMPLER_BINDING_COUNT);
auto const * const sb = samplerBindings[binding];
assert_invariant(sb);
if (!sb) continue; // should never happen, this would be a user error.
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
const GLTexture* const t = sb->textureUnitEntries[j].texture;
if (t) { // program may not use all samplers of sampler group
@@ -168,7 +168,7 @@ error:
}
bool PlatformWGL::isExtraContextSupported() const noexcept {
return true;
return false;
}
void PlatformWGL::createContext(bool shared) {
@@ -272,6 +272,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
return;
}
assert_invariant(hostData->size > 0 && "Data is empty");
// Otherwise, use vkCmdCopyBufferToImage.
void* mapped = nullptr;
VulkanStage const* stage = mStagePool.acquireStage(hostData->size);
+24 -14
View File
@@ -172,10 +172,11 @@ public:
using Platform = backend::Platform;
using Backend = backend::Backend;
using DriverConfig = backend::Platform::DriverConfig;
using FeatureLevel = backend::FeatureLevel;
/**
* Config is used to define the memory footprint used by the engine, such as the
* command buffer size. Config can be used to customize engine requirements based
* command buffer size. Config can be used to customize engine requirements based
* on the applications needs.
*
* .perRenderPassArenaSizeMB (default: 3 MiB)
@@ -351,6 +352,12 @@ public:
*/
Builder& sharedContext(void* sharedContext) noexcept;
/**
* @param featureLevel The feature level at which initialize Filament.
* @return A reference to this Builder for chaining calls.
*/
Builder& featureLevel(FeatureLevel featureLevel) noexcept;
#if UTILS_HAS_THREADING
/**
* Creates the filament Engine asynchronously.
@@ -482,9 +489,6 @@ public:
*/
static void destroy(Engine* engine);
using FeatureLevel = backend::FeatureLevel;
/**
* Query the feature level supported by the selected backend.
*
@@ -496,17 +500,23 @@ public:
FeatureLevel getSupportedFeatureLevel() const noexcept;
/**
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
* active. The selected feature level must not be higher than the value returned by
* getActiveFeatureLevel() and it's not possible lower the active feature level.
* Activate all features of a given feature level. If an explicit feature level is not specified
* at Engine initialization time via Builder::featureLevel, the default feature level is
* FeatureLevel::FEATURE_LEVEL_0 on devices not compatible with GLES 3.0; otherwise, the default
* is FeatureLevel::FEATURE_LEVEL_1. The selected feature level must not be higher than the
* value returned by getActiveFeatureLevel() and it's not possible lower the active feature
* level. Additionally, it is not possible to modify the feature level at all if the Engine was
* initialized at FeatureLevel::FEATURE_LEVEL_0.
*
* @param featureLevel the feature level to activate. If featureLevel is lower than
* getActiveFeatureLevel(), the current (higher) feature level is kept.
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
* is thrown, or the program is terminated if exceptions are disabled.
* getActiveFeatureLevel(), the current (higher) feature level is kept. If
* featureLevel is higher than getSupportedFeatureLevel(), or if the engine
* was initialized at feature level 0, an exception is thrown, or the
* program is terminated if exceptions are disabled.
*
* @return the active feature level.
*
* @see Builder::featureLevel
* @see getSupportedFeatureLevel
* @see getActiveFeatureLevel
*/
@@ -827,14 +837,14 @@ public:
#if defined(__EMSCRIPTEN__)
/**
* WebGL only: Tells the driver to reset any internal state tracking if necessary.
*
* This is only useful when integrating an external renderer into Filament on platforms
*
* This is only useful when integrating an external renderer into Filament on platforms
* like WebGL, where share contexts do not exist. Filament keeps track of the GL
* state it has set (like which texture is bound), and does not re-set that state if
* it does not think it needs to. However, if an external renderer has set different
* state in the mean time, Filament will use that new state unknowingly.
*
* If you are in this situation, call this function - ideally only once per frame,
*
* If you are in this situation, call this function - ideally only once per frame,
* immediately after calling Engine::execute().
*/
void resetBackendState() noexcept;
+3
View File
@@ -294,6 +294,9 @@ public:
//! Returns the reflection mode used by this material.
ReflectionMode getReflectionMode() const noexcept;
//! Returns the minimum required feature level for this material.
backend::FeatureLevel getFeatureLevel() const noexcept;
/**
* Returns the number of parameters declared by this material.
* The returned value can be 0.
+3 -3
View File
@@ -104,12 +104,12 @@ Froxelizer::Froxelizer(FEngine& engine)
static_assert(std::is_same_v<RecordBufferType, uint8_t>,
"Record Buffer must use bytes");
if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)) {
DriverApi& driverApi = engine.getDriverApi();
if (UTILS_UNLIKELY(driverApi.getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)) {
return;
}
DriverApi& driverApi = engine.getDriverApi();
mFroxelBufferEntryCount = std::min(
FROXEL_BUFFER_MAX_ENTRY_COUNT,
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
+4
View File
@@ -120,6 +120,10 @@ ReflectionMode Material::getReflectionMode() const noexcept {
return downcast(this)->getReflectionMode();
}
FeatureLevel Material::getFeatureLevel() const noexcept {
return downcast(this)->getFeatureLevel();
}
bool Material::hasParameter(const char* name) const noexcept {
return downcast(this)->hasParameter(name);
}
+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 -1
View File
@@ -60,7 +60,7 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction,
s.viewFromClipMatrix = viewFromClip; // 1/projection
s.clipFromWorldMatrix[0] = clipFromWorld; // projection * view
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin));
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
s.clipTransform = camera.clipTransform;
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
+4 -3
View File
@@ -75,7 +75,7 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n
s.clipFromViewMatrix = clipFromView; // projection
s.viewFromClipMatrix = viewFromClip; // 1/projection
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin));
s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform));
s.clipTransform = camera.clipTransform;
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
@@ -151,7 +151,7 @@ void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo,
// why we store the cofactor matrix.
mat4f const viewFromWorld = cameraInfo.view;
mat4 const worldFromUserWorld = cameraInfo.worldOrigin;
mat4 const worldFromUserWorld = cameraInfo.worldTransform;
mat4 const worldFromFog = worldFromUserWorld * userWorldFromFog;
mat4 const viewFromFog = viewFromWorld * worldFromFog;
@@ -245,6 +245,7 @@ void PerViewUniforms::prepareMaterialGlobals(
}
void PerViewUniforms::prepareSSR(Handle<HwTexture> ssr,
bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
@@ -255,7 +256,7 @@ void PerViewUniforms::prepareSSR(Handle<HwTexture> ssr,
auto& s = mUniforms.edit();
s.refractionLodOffset = refractionLodOffset;
s.ssrDistance = ssrOptions.enabled ? ssrOptions.maxDistance : 0.0f;
s.ssrDistance = (ssrOptions.enabled && !disableSSR) ? ssrOptions.maxDistance : 0.0f;
}
void PerViewUniforms::prepareHistorySSR(Handle<HwTexture> ssr,
+1
View File
@@ -95,6 +95,7 @@ public:
// screen-space reflection and/or refraction (SSR)
void prepareSSR(TextureHandle ssr,
bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
+13 -3
View File
@@ -207,10 +207,13 @@ PostProcessManager::PostProcessMaterial& PostProcessManager::getPostProcessMater
#define MATERIAL(n) MATERIALS_ ## n ## _DATA, MATERIALS_ ## n ## _SIZE
static const PostProcessManager::MaterialInfo sMaterialListFeatureLevel0[] = {
{ "blitLow", MATERIAL(BLITLOW) },
};
static const PostProcessManager::MaterialInfo sMaterialList[] = {
{ "bilateralBlur", MATERIAL(BILATERALBLUR) },
{ "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) },
{ "blitLow", MATERIAL(BLITLOW) },
{ "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) },
{ "bloomDownsample2x", MATERIAL(BLOOMDOWNSAMPLE2X) },
{ "bloomDownsample9", MATERIAL(BLOOMDOWNSAMPLE9) },
@@ -273,10 +276,17 @@ void PostProcessManager::init() noexcept {
driver.isWorkaroundNeeded(Workaround::ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP);
#pragma nounroll
for (auto const& info : sMaterialList) {
for (auto const& info : sMaterialListFeatureLevel0) {
registerPostProcessMaterial(info.name, info);
}
if (mEngine.getActiveFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1) {
#pragma nounroll
for (auto const& info : sMaterialList) {
registerPostProcessMaterial(info.name, info);
}
}
mStarburstTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::R8, 1, 256, 1, 1, TextureUsage::DEFAULT);
@@ -380,7 +390,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
// generate depth pass at the requested resolution
auto& structurePass = fg.addPass<StructurePassData>("Structure Pass",
[&](FrameGraph::Builder& builder, auto& data) {
bool const isES2 = mEngine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0;
bool const isES2 = mEngine.getDriverApi().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0;
data.depth = builder.createTexture("Structure Buffer", {
.width = width, .height = height,
.levels = uint8_t(levelCount),
+3 -3
View File
@@ -100,7 +100,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
"Depth/Stencil Buffer" : "Depth Buffer";
bool const isES2 =
engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0;
engine.getDriverApi().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0;
TextureFormat const stencilFormat = isES2 ?
TextureFormat::DEPTH24_STENCIL8 : TextureFormat::DEPTH32F_STENCIL8;
@@ -193,8 +193,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
TextureHandle const ssr = data.ssr ?
resources.getTexture(data.ssr) : engine.getOneTextureArray();
view.prepareSSR(ssr, config.ssrLodOffset,
view.getScreenSpaceReflectionsOptions());
view.prepareSSR(ssr, config.screenSpaceReflectionHistoryNotReady,
config.ssrLodOffset, view.getScreenSpaceReflectionsOptions());
// Note: here we can't use data.color's descriptor for the viewport because
// the actual viewport might be offset when the target is the swapchain.
+2
View File
@@ -67,6 +67,8 @@ public:
bool hasScreenSpaceReflectionsOrRefractions;
// Use a depth format with a stencil component.
bool enabledStencilBuffer;
// whether the screenspace reflections history buffer is initialized
bool screenSpaceReflectionHistoryNotReady;
};
static FrameGraphId<FrameGraphTexture> colorPass(
+73 -112
View File
@@ -77,20 +77,13 @@ void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType,
mFace = face;
}
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
auto z_axis = direction;
auto norm_up = float3{ 0, 1, 0 };
if (UTILS_UNLIKELY(std::abs(dot(z_axis, norm_up)) > 0.999f)) {
// Fix up vector if we're degenerate (looking straight up, basically)
norm_up = { norm_up.z, norm_up.x, norm_up.y };
}
auto x_axis = normalize(cross(z_axis, norm_up));
auto y_axis = cross(x_axis, z_axis);
const mat4f Mm{
float4{ x_axis, 0 },
float4{ y_axis, 0 },
float4{ -z_axis, 0 },
float4{ position, 1 }};
math::mat4f ShadowMap::getDirectionalLightViewMatrix(math::float3 direction, math::float3 up,
math::float3 position) noexcept {
// 1. we use the x-axis as the "up" reference so that the math is stable when the light
// is pointing down, which is a common case for lights.
// 2. we do the math in double to avoid some precision issues when the light is almost
// straight (i.e. parallel to the x-axis)
mat4f const Mm = mat4f{ mat4::lookTo(direction, position, up) };
return FCamera::rigidTransformInverse(Mm);
}
@@ -105,7 +98,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
case TextureCubemapFace::POSITIVE_Z: direction = { 0, 0, 1 }; break;
case TextureCubemapFace::NEGATIVE_Z: direction = { 0, 0, -1 }; break;
}
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
const mat4f Mv = getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position);
return Mv;
}
@@ -120,7 +113,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
// We can't use LISPSM in stable mode
const auto direction = params.options.transform * lightData.elementAt<FScene::DIRECTION>(index);
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
@@ -166,11 +159,22 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// This is the most important step to increase the quality of the shadow map.
//
// In LiPSM mode, we're using the warped space here.
const mat4f F = computeFocusMatrix(LMpMv, WLMp,
sceneInfo.wsShadowReceiversVolume,
float4 f = computeFocusParams(LMpMv, WLMp,
lsClippedShadowVolume, vertexCount,
camera, sceneInfo.csNearFar,
shadowMapInfo.shadowDimension, params.options.stable);
params.options.shadowFar, params.options.stable);
if (params.options.stable) {
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
snapLightFrustum(f.xy, f.zw, lsRef, shadowMapInfo.shadowDimension);
}
const mat4f F(mat4f::row_major_init {
f.x, 0.0f, 0.0f, f.z,
0.0f, f.y, 0.0f, f.w,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
/*
* Final shadow map transform
@@ -222,7 +226,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
mCamera->setCustomProjection(mat4(Mn * F * WLMp), znear, zfar);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldTransform), znear, zfar);
mHasVisibleShadows = true;
@@ -297,7 +301,7 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
const mat4f Mv = getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position);
// We only keep this for reference. updateSceneInfoSpot() is quite expensive on large scenes
// currently, and only needed to find a near/far. Instead, we just use a small near and the
@@ -401,7 +405,8 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
// 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.
// This will be adjusted later because of how we compute the depth metric for VSM.
mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction);
mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction,
normalize(camera.worldTransform[0].xyz));
Aabb lsLightFrustumBounds = computeLightFrustumBounds(
@@ -455,10 +460,6 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
std::max(lsLightFrustumBounds.min.z, sceneInfo.lsCastersNearFar[1]);
}
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
// that znear = 0, this is only needed for VSM, but doesn't hurt PCF.
const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z);
// near / far planes are specified relative to the direction the eye is looking at
// i.e. the -z axis (see: ortho)
const float znear = 0.0f;
@@ -473,6 +474,11 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
v.z -= lsLightFrustumBounds.max.z;
}
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
// that znear = 0, this is only needed for VSM, but doesn't hurt PCF.
const mat4f Mv = getDirectionalLightViewMatrix(direction, normalize(camera.worldTransform[0].xyz),
direction * -lsLightFrustumBounds.max.z);
return { Mv, znear, zfar, lsClippedShadowVolume, vertexCount, true };
}
@@ -577,65 +583,36 @@ math::mat4f ShadowMap::computeLightRotation(math::float3 const& lsDirection) noe
return L;
}
math::mat4f ShadowMap::computeFocusMatrix(
const mat4f& LMpMv, const mat4f& WLMp,
Aabb const& wsShadowReceiversVolume,
math::float4 ShadowMap::computeFocusParams(
mat4f const& LMpMv,
mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera, float2 const& csNearFar,
uint16_t shadowDimension, bool stable) noexcept {
float shadowFar, bool stable) noexcept {
float2 s, o;
float4 wsViewVolumeBoundingSphere = {};
if (stable) {
// In stable mode, the light frustum size must be fixed, so we can choose either the
// whole view frustum, or the whole scene bounding volume. We simply pick whichever
// is smaller.
// in stable mode we simply take the shadow receivers volume
const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere(
wsShadowReceiversVolume.getCorners().data(), 8);
// in stable mode we simply take the view volume bounding sphere, but we calculate it
// In stable mode, the light frustum size must be fixed, so we choose the
// whole view frustum.
// We simply take the view volume bounding sphere, but we calculate it
// in view space, so that it's perfectly stable.
mat4f const viewFromClip = inverse(camera.cullingProjection);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
wsViewVolumeBoundingSphere = computeBoundingSphere(wsFrustumVertices.vertices, 8);
if (shadowReceiverVolumeBoundingSphere.w < wsViewVolumeBoundingSphere.w) {
// When using the shadowReceiver volume, we don't have to use its enclosing sphere
// because (we assume) the scene volume doesn't change. Seen from the light it only
// changes when the light moves or rotates, and it is acceptable in that case to have
// non "stable" shadows (the shadow will never be stable when the light moves).
//
// On the other hand, when using the view volume, we must use a sphere because otherwise
// its projection's bounds in light space change with the camera, leading to unstable
// shadows with camera movement.
auto getViewVolumeBoundingSphere = [&]() {
if (shadowFar > 0) {
float4 const wsViewVolumeBoundingSphere = { camera.getPosition(), shadowFar };
return wsViewVolumeBoundingSphere;
} else {
mat4f const viewFromClip = inverse(camera.cullingProjection);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
float4 const wsViewVolumeBoundingSphere =
computeBoundingSphere(wsFrustumVertices.vertices, 8);
return wsViewVolumeBoundingSphere;
}
};
wsViewVolumeBoundingSphere.w = 0;
}
if (wsViewVolumeBoundingSphere.w > 0) {
s = 1.0f / wsViewVolumeBoundingSphere.w;
o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy;
} else {
// TODO: another options is the sphere around the intersections of receiver & casters
// FIXME: this is not stable with the global rotation because wsShadowReceiversVolume
// is not stable with it.
Aabb const bounds = compute2DBounds(LMpMv,
wsShadowReceiversVolume.getCorners().data(),
wsShadowReceiversVolume.getCorners().size());
assert_invariant(bounds.min.x < bounds.max.x);
assert_invariant(bounds.min.y < bounds.max.y);
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
o = float2(bounds.max.xy + bounds.min.xy) * 0.5f;
// Quantize the scale in world-space units. This value can be very small because
// if it wasn't for floating-point imprecision, the scale would be a constant.
double2 const quantizer = 0.0625;
s = 1.0 / (ceil(1.0 / (s * quantizer)) * quantizer);
}
float4 const wsViewVolumeBoundingSphere = getViewVolumeBoundingSphere();
s = 1.0f / wsViewVolumeBoundingSphere.w;
o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy;
o = -s * o;
} else {
Aabb const bounds = compute2DBounds(WLMp, lsShadowVolume.data(), vertexCount);
assert_invariant(bounds.min.x < bounds.max.x);
@@ -643,29 +620,11 @@ math::mat4f ShadowMap::computeFocusMatrix(
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
o = float2(bounds.max.xy + bounds.min.xy) * 0.5f;
// TODO: we could quantize `s` here to give some stability when lispsm is disabled,
// however, the quantization paramater should probably be user settable.
o = -s * o;
}
// adjust offset for scale
o = -s * o;
if (stable) {
snapLightFrustum(s, o, LMpMv, wsShadowReceiversVolume.center(), shadowDimension);
}
const mat4f F(mat4f::row_major_init {
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
return F;
return { s, o };
}
// Apply these remapping in double to maintain a high precision for the depth axis
ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info,
backend::Viewport const& viewport) noexcept {
@@ -679,6 +638,8 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo
0.0f, 0.0f, 0.0f, 1.0f
}};
constexpr mat4f MtInverse = inverse(Mt);
// apply the viewport transform
const float2 o = float2{ viewport.left, viewport.bottom } / float(info.atlasDimension);
const float2 s = float2{ viewport.width, viewport.height } / float(info.atlasDimension);
@@ -700,7 +661,7 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo
}} : mat4f{};
// Compute shadow-map texture access and viewport transform
return { Mf * (Mv * Mt), inverse(Mt) * (Mv * Mt) };
return { Mf * (Mv * Mt), MtInverse * (Mv * Mt) };
}
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
@@ -841,8 +802,8 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners(
Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView,
Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume,
ShadowMap::SceneInfo const& sceneInfo, bool stable, bool focusShadowCasters,
bool farUsesShadowCasters) noexcept {
ShadowMap::SceneInfo const& sceneInfo,
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept {
Aabb lsLightFrustumBounds{};
float const receiversFar = sceneInfo.lsReceiversNearFar[1];
@@ -873,13 +834,13 @@ Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView,
}
void ShadowMap::snapLightFrustum(float2& s, float2& o,
mat4f const& Mv, double3 wsSnapCoords, int2 resolution) noexcept {
double2 lsRef, int2 resolution) noexcept {
auto proj = [](mat4 m, double3 v) -> double3 {
// for directional light p.w == 1, exactly
auto p = m * v;
assert_invariant(p.w == 1.0);
return p.xyz;
auto proj2 = [](mat4 m, double2 v) -> double2 {
double2 p;
p.x = dot(double2{ m[0].x, m[1].x }, v) + m[3].x;
p.y = dot(double2{ m[0].y, m[1].y }, v) + m[3].y;
return p;
};
auto fract = [](auto v) {
@@ -896,13 +857,13 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
});
// The (resolution * 0.5) comes from Mv having a NDC in the range -1,1 (so a range of 2).
// focused light-space
mat4 const FMv{ F * Mv };
// Another (resolution * 0.5) is there to snap on even texels, which helps with debugging
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world
double2 const lsOrigin = proj(FMv, wsSnapCoords).xy;
double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution;
// F * Mv * ref
double2 const lsFocusedOrigin = proj2(F, lsRef);
double2 const d = fract(lsFocusedOrigin * (resolution * 0.25)) / (resolution * 0.25);
// adjust offset
o -= d;
+7 -8
View File
@@ -122,8 +122,8 @@ public:
uint8_t visibleLayers;
};
static math::mat4f getDirectionalLightViewMatrix(
math::float3 direction, math::float3 position = {}) noexcept;
static math::mat4f getDirectionalLightViewMatrix(math::float3 direction, math::float3 up,
math::float3 position = {}) noexcept;
static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face,
math::float3 position) noexcept;
@@ -246,16 +246,15 @@ private:
static inline math::mat4f computeLightRotation(math::float3 const& lsDirection) noexcept;
static inline math::mat4f computeFocusMatrix(
const math::mat4f& LMpMv,
const math::mat4f& WLMp,
Aabb const& wsShadowReceiversVolume,
static inline math::float4 computeFocusParams(
math::mat4f const& LMpMv,
math::mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera, math::float2 const& csNearFar,
uint16_t shadowDimension, bool stable) noexcept;
float shadowFar, bool stable) noexcept;
static inline void snapLightFrustum(math::float2& s, math::float2& o,
math::mat4f const& Mv, math::double3 wsSnapCoords, math::int2 resolution) noexcept;
math::double2 lsRef, math::int2 resolution) noexcept;
static inline Aabb computeLightFrustumBounds(const math::mat4f& lightView,
Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume,
+42 -42
View File
@@ -90,40 +90,39 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FVie
}
void ShadowMapManager::reset() noexcept {
mCascadeShadowMaps.clear();
mSpotShadowMaps.clear();
mDirectionalShadowMapCount = 0;
mSpotShadowMapCount = 0;
}
void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
// this updates getCascadedShadowMap()
mDirectionalShadowMapCount = options->shadowCascades;
utils::Slice<ShadowMap> cascadedShadowMap = getCascadedShadowMap();
for (size_t c = 0; c < options->shadowCascades; c++) {
const size_t i = c;
assert_invariant(i < CONFIG_MAX_SHADOW_CASCADES);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, i, 0, options);
mCascadeShadowMaps.push_back(pShadowMap);
ShadowMap& shadowMap = cascadedShadowMap[c];
shadowMap.initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
}
}
void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
if (spotlight) {
const size_t c = mSpotShadowMaps.size();
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, i, 0, options);
mSpotShadowMaps.push_back(pShadowMap);
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::SPOT, i, 0, options);
} else {
// point-light, generate 6 independent shadowmaps
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMaps.size();
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::POINT, i, face, options);
mSpotShadowMaps.push_back(pShadowMap);
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::POINT, i, face, options);
}
}
}
@@ -179,11 +178,11 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// Directional, cascaded shadow maps
auto const directionalShadowCastersRange = view.getVisibleDirectionalShadowCasters();
if (!directionalShadowCastersRange.empty()) {
for (auto* pShadowMap : mCascadeShadowMaps) {
for (auto& shadowMap : getCascadedShadowMap()) {
// for the directional light, we already know if it has visible shadows.
if (pShadowMap->hasVisibleShadows()) {
if (shadowMap.hasVisibleShadows()) {
passList.push_back({
{}, pShadowMap, directionalShadowCastersRange,
{}, &shadowMap, directionalShadowCastersRange,
VISIBLE_DIR_SHADOW_RENDERABLE });
}
}
@@ -192,10 +191,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// Point lights and Spotlight shadow maps
auto const spotShadowCastersRange = view.getVisibleSpotShadowCasters();
if (!spotShadowCastersRange.empty()) {
for (auto* pShadowMap : mSpotShadowMaps) {
assert_invariant(!pShadowMap->isDirectionalShadow());
for (auto& shadowMap : getSpotShadowMaps()) {
assert_invariant(!shadowMap.isDirectionalShadow());
passList.push_back({
{}, pShadowMap, spotShadowCastersRange,
{}, &shadowMap, spotShadowCastersRange,
VISIBLE_DYN_SHADOW_RENDERABLE });
}
}
@@ -517,17 +516,19 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
};
bool hasVisibleShadows = false;
if (!mCascadeShadowMaps.empty()) {
utils::Slice<ShadowMap> cascadedShadowMaps = getCascadedShadowMap();
if (!cascadedShadowMaps.empty()) {
// Even if we have more than one cascade, we cull directional shadow casters against the
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
ShadowMap& shadowMap = cascadedShadowMaps[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.
// This will be adjusted later because of how we compute the depth metric for VSM.
const mat4f MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction);
const mat4f MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction,
normalize(cameraInfo.worldTransform[0].xyz));
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
@@ -557,7 +558,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
}
const size_t cascadeCount = mCascadeShadowMaps.size();
const size_t cascadeCount = cascadedShadowMaps.size();
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
// The first split position is the near plane; the last split position is the far plane.
@@ -591,11 +592,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
assert_invariant(mCascadeShadowMaps[i]);
for (size_t i = 0, c = cascadedShadowMaps.size(); i < c; i++) {
// Compute the frustum for the directional light.
ShadowMap& shadowMap = *mCascadeShadowMaps[i];
ShadowMap& shadowMap = cascadedShadowMaps[i];
assert_invariant(shadowMap.getLightIndex() == 0);
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
@@ -642,7 +641,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
}
uint32_t cascades = 0;
cascades |= uint32_t(mCascadeShadowMaps.size());
cascades |= uint32_t(cascadedShadowMaps.size());
cascades |= cascadeHasVisibleShadows << 8u;
mShadowMappingUniforms.directionalShadows = directionalShadowsMask;
@@ -695,7 +694,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
// compute shadow map frustum for culling
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position);
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius);
const mat4f MpMv = math::highPrecisionMultiply(Mp, Mv);
const Frustum frustum(MpMv);
@@ -851,16 +850,17 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
lightData.data<FScene::SHADOW_INFO>());
ShadowTechnique shadowTechnique{};
if (!mSpotShadowMaps.empty()) {
utils::Slice<ShadowMap> const spotShadowMaps = getSpotShadowMaps();
if (!spotShadowMaps.empty()) {
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
for (auto const* pShadowMap : mSpotShadowMaps) {
const size_t lightIndex = pShadowMap->getLightIndex();
for (ShadowMap const& shadowMap : spotShadowMaps) {
const size_t lightIndex = shadowMap.getLightIndex();
// gather the per-light (not per shadow map) information. For point lights we will
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
// knows how to find the entry for other faces (they're guaranteed to be sequential).
if (pShadowMap->getFace() == 0) {
if (shadowMap.getFace() == 0) {
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
shadowInfo[lightIndex].index = shadowMap.getShadowIndex();
}
}
}
@@ -890,18 +890,18 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
uint8_t layer = 0;
uint32_t maxDimension = 0;
bool elvsm = false;
for (auto* pShadowMap : mCascadeShadowMaps) {
for (ShadowMap& shadowMap : getCascadedShadowMap()) {
// Shadow map size should be the same for all cascades.
auto const& options = pShadowMap->getShadowOptions();
auto const& options = shadowMap.getShadowOptions();
maxDimension = std::max(maxDimension, options->mapSize);
elvsm = elvsm || options->vsm.elvsm;
pShadowMap->setLayer(layer++);
shadowMap.setLayer(layer++);
}
for (auto& pShadowMap : mSpotShadowMaps) {
auto const& options = pShadowMap->getShadowOptions();
for (ShadowMap& shadowMap : getSpotShadowMaps()) {
auto const& options = shadowMap.getShadowOptions();
maxDimension = std::max(maxDimension, options->mapSize);
elvsm = elvsm || options->vsm.elvsm;
pShadowMap->setLayer(layer++);
shadowMap.setLayer(layer++);
}
const uint8_t layersNeeded = layer;
+30 -20
View File
@@ -31,7 +31,7 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Slice.h>
#include <math/vec3.h>
@@ -89,15 +89,6 @@ public:
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
FView& view, CameraInfo const& mainCameraInfo, math::float4 const& userTime) noexcept;
ShadowMap* getShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[index]));
}
ShadowMap const* getShadowMap(size_t index) const noexcept {
return const_cast<ShadowMapManager*>(this)->getShadowMap(index);
}
// valid after calling update() above
ShadowMappingUniforms getShadowMappingUniforms() const noexcept {
return mShadowMappingUniforms;
@@ -105,7 +96,12 @@ public:
auto& getShadowUniformsHandle() const { return mShadowUbh; }
bool hasSpotShadows() const { return !mSpotShadowMaps.empty(); }
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
// for debugging only
FCamera const* getDirectionalLightCamera() const noexcept {
return &getShadowMap(0).getDebugCamera();
}
private:
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
@@ -184,19 +180,33 @@ private:
ShadowMap::SceneInfo mSceneInfo;
utils::FixedCapacityVector<ShadowMap*> mCascadeShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOW_CASCADES) };
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
// Inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// Because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
using ShadowMapCacheContainer = std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS>;
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
ShadowMap& getShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
return *std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[index]));
}
ShadowMap const& getShadowMap(size_t index) const noexcept {
return const_cast<ShadowMapManager*>(this)->getShadowMap(index);
}
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
ShadowMap* const p = &getShadowMap(0);
return { p, mDirectionalShadowMapCount };
}
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
return { p, mSpotShadowMapCount };
}
};
} // namespace filament
+40 -30
View File
@@ -214,12 +214,12 @@ RenderableManager::Builder& RenderableManager::Builder::enableSkinningBuffers(bo
RenderableManager::Builder& RenderableManager::Builder::boneIndicesAndWeights(size_t primitiveIndex,
math::float2 const* indicesAndWeights, size_t count, size_t bonesPerVertex) noexcept {
size_t vertexCount = count / bonesPerVertex;
size_t const vertexCount = count / bonesPerVertex;
utils::FixedCapacityVector<utils::FixedCapacityVector<filament::math::float2>> bonePairs(vertexCount);
for ( size_t iVertex = 0; iVertex < vertexCount; iVertex++) {
for (size_t iVertex = 0; iVertex < vertexCount; iVertex++) {
utils::FixedCapacityVector<float2> vertexData(bonesPerVertex);
std::copy_n(indicesAndWeights + iVertex * bonesPerVertex,
bonesPerVertex, vertexData.data());
bonesPerVertex, vertexData.data());
bonePairs[iVertex] = std::move(vertexData);
}
return boneIndicesAndWeights(primitiveIndex, bonePairs);
@@ -275,13 +275,13 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
size_t maxPairsCount = 0; //size of texture, number of bone pairs
size_t maxPairsCountPerVertex = 0; //maximum of number of bone per vertex
for (auto iBonePair = mBonePairs.begin(); iBonePair != mBonePairs.end(); ++iBonePair){
auto primitiveIndex = iBonePair->first;
for (auto& bonePair: mBonePairs) {
auto primitiveIndex = bonePair.first;
auto entries = mEntries;
ASSERT_PRECONDITION(primitiveIndex < entries.size() && primitiveIndex >= 0,
"[primitive @ %u] primitiveindex is out of size (%u)", primitiveIndex, entries.size());
auto entry = mEntries[primitiveIndex];
auto bonePairsForPrimitive = iBonePair->second;
auto bonePairsForPrimitive = bonePair.second;
auto vertexCount = entry.vertices->getVertexCount();
ASSERT_PRECONDITION(bonePairsForPrimitive.size() == vertexCount,
"[primitive @ %u] bone indices and weights pairs count (%u) must be equal to vertex count (%u)",
@@ -292,7 +292,7 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
"[entity=%u, primitive @ %u] for advanced skinning set VertexBuffer::Builder::advancedSkinning()",
entity.getId(), primitiveIndex);
for (size_t iVertex = 0; iVertex < vertexCount; iVertex++) {
size_t bonesPerVertex = bonePairsForPrimitive[iVertex].size();
size_t const bonesPerVertex = bonePairsForPrimitive[iVertex].size();
maxPairsCount += bonesPerVertex;
maxPairsCountPerVertex = std::max(bonesPerVertex, maxPairsCountPerVertex);
}
@@ -303,29 +303,28 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
// final texture data, indices and weights
mBoneIndicesAndWeights = utils::FixedCapacityVector<float2>(maxPairsCount);
// temporary indices and weights for one vertex
std::unique_ptr<float2[]> tempPairs = std::make_unique<float2[]>
(maxPairsCountPerVertex);
for (auto iBonePair = mBonePairs.begin(); iBonePair != mBonePairs.end(); ++iBonePair) {
auto primitiveIndex = iBonePair->first;
auto bonePairsForPrimitive = iBonePair->second;
if (!bonePairsForPrimitive.size()) {
continue;
auto const tempPairs = std::make_unique<float2[]>(maxPairsCountPerVertex);
for (auto& bonePair: mBonePairs) {
auto primitiveIndex = bonePair.first;
auto bonePairsForPrimitive = bonePair.second;
if (bonePairsForPrimitive.empty()) {
continue;
}
size_t vertexCount = mEntries[primitiveIndex].vertices->getVertexCount();
std::unique_ptr<uint16_t[]> skinJoints = std::make_unique<uint16_t[]>
(4 * vertexCount); // temporary indices for one vertex
std::unique_ptr<float[]> skinWeights = std::make_unique<float[]>
(4 * vertexCount); // temporary weights for one vertex
size_t const vertexCount = mEntries[primitiveIndex].vertices->getVertexCount();
// temporary indices for one vertex
auto skinJoints = std::make_unique<uint16_t[]>(4 * vertexCount);
// temporary weights for one vertex
auto skinWeights = std::make_unique<float[]>(4 * vertexCount);
for (size_t iVertex = 0; iVertex < vertexCount; iVertex++) {
size_t tempPairCount = 0;
float boneWeightsSum = 0;
double boneWeightsSum = 0;
for (size_t k = 0; k < bonePairsForPrimitive[iVertex].size(); k++) {
auto boneWeight = bonePairsForPrimitive[iVertex][k][1];
auto boneIndex = bonePairsForPrimitive[iVertex][k][0];
ASSERT_PRECONDITION(boneWeight >= 0,
"[entity=%u, primitive @ %u] bone weight (%f) of vertex=%u is negative ",
entity.getId(), primitiveIndex, boneWeight, iVertex);
if (boneWeight) {
if (boneWeight > 0.0f) {
ASSERT_PRECONDITION(boneIndex >= 0,
"[entity=%u, primitive @ %u] bone index (%i) of vertex=%u is negative ",
entity.getId(), primitiveIndex, (int) boneIndex, iVertex);
@@ -342,26 +341,37 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
ASSERT_PRECONDITION(boneWeightsSum > 0,
"[entity=%u, primitive @ %u] sum of bone weights of vertex=%u is %f, it should be positive.",
entity.getId(), primitiveIndex, iVertex, boneWeightsSum);
if (abs(boneWeightsSum - 1.f) > std::numeric_limits<float>::epsilon()) {
// see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
double const epsilon = 2e-7 * double(tempPairCount);
if (abs(boneWeightsSum - 1.0) <= epsilon) {
boneWeightsSum = 1.0;
}
#ifndef NDEBUG
else {
utils::slog.w << "Warning of skinning: [entity=%" << entity.getId()
<< ", primitive @ %" << primitiveIndex
<< "] sum of bone weights of vertex=" << iVertex << " is " << boneWeightsSum
<< ", it should be one. Weights will be normalized." << utils::io::endl;
}
#endif
// prepare data for vertex attributes
auto offset = iVertex * 4;
// set attributes, indices and weights, for <= 4 pairs
for (size_t j = 0, c = std::min((int) tempPairCount, 4); j < c; j++) {
skinJoints[j + offset] = tempPairs[j][0];
skinWeights[j + offset] = tempPairs[j][1] / boneWeightsSum;
for (size_t j = 0, c = std::min((int)tempPairCount, 4); j < c; j++) {
skinJoints[j + offset] = uint16_t(tempPairs[j][0]);
skinWeights[j + offset] = tempPairs[j][1] / float(boneWeightsSum);
}
// prepare data for texture
if (tempPairCount > 4) { // set attributes, indices and weights, for > 4 pairs
skinWeights[3 + offset] = -(float) (pairsCount + 1); // negative offset to texture 0..-1, 1..-2
skinJoints[3 + offset] = (uint16_t) tempPairCount; // number pairs per vertex in texture
// number pairs per vertex in texture
skinJoints[3 + offset] = (uint16_t)tempPairCount;
// negative offset to texture 0..-1, 1..-2
skinWeights[3 + offset] = -float(pairsCount + 1);
for (size_t j = 3; j < tempPairCount; j++) {
mBoneIndicesAndWeights[pairsCount][0] = tempPairs[j][0];
mBoneIndicesAndWeights[pairsCount][1] = tempPairs[j][1] / boneWeightsSum;
mBoneIndicesAndWeights[pairsCount][1] = tempPairs[j][1] / float(boneWeightsSum);
pairsCount++;
}
}
@@ -386,7 +396,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
mImpl->mInstanceCount,
CONFIG_MAX_INSTANCES);
if (mImpl->mInstanceBuffer) {
size_t bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount;
size_t const bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount;
ASSERT_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount,
"instance count (%zu) must be less than or equal to the InstanceBuffer's instance "
"count "
@@ -402,7 +412,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
auto& entry = mImpl->mEntries[i];
// entry.materialInstance must be set to something even if indices/vertices are null
FMaterial const* material = nullptr;
FMaterial const* material;
if (!entry.materialInstance) {
material = downcast(engine.getDefaultMaterial());
entry.materialInstance = material->getDefaultInstance();
+3 -3
View File
@@ -266,8 +266,8 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
d = std::max(zn, camera.getFocusDistance());
}
CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept {
const mat4 modelMatrix{ worldOriginCamera * camera.getModelMatrix() };
CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept {
const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() };
for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) {
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) };
@@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
model = mat4f{ modelMatrix };
view = mat4f{ inverse(modelMatrix) };
worldOrigin = worldOriginCamera;
worldTransform = inWorldTransform;
zn = (float)camera.getNear();
zf = (float)camera.getCullingFar();
ev100 = Exposure::ev100(camera);
+3 -3
View File
@@ -223,7 +223,7 @@ private:
struct CameraInfo {
CameraInfo() noexcept {}
explicit CameraInfo(FCamera const& camera) noexcept;
CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept;
CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept;
union {
// projection matrix for drawing (infinite zfar)
@@ -239,7 +239,7 @@ struct CameraInfo {
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 worldOrigin; // world origin transform (already applied
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
@@ -250,7 +250,7 @@ struct CameraInfo {
float d{}; // focus distance [m]
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; }
math::mat4 getUserViewMatrix() const noexcept { return view * worldTransform; }
};
FILAMENT_DOWNCAST(Camera)
+12 -5
View File
@@ -67,6 +67,7 @@ struct Engine::BuilderDetails {
Backend mBackend = Backend::DEFAULT;
Platform* mPlatform = nullptr;
Engine::Config mConfig;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
void* mSharedContext = nullptr;
static Config validateConfig(const Config* pConfig) noexcept;
};
@@ -185,6 +186,7 @@ static const uint16_t sFullScreenTriangleIndices[3] = { 0, 1, 2 };
FEngine::FEngine(Engine::Builder const& builder) :
mBackend(builder->mBackend),
mActiveFeatureLevel(builder->mFeatureLevel),
mPlatform(builder->mPlatform),
mSharedGLContext(builder->mSharedContext),
mPostProcessManager(*this),
@@ -326,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_TARGET_MOBILE
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;
@@ -359,11 +358,12 @@ void FEngine::init() {
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
mPostProcessManager.init();
mLightManager.init(*this);
mDFG.init(*this);
}
mPostProcessManager.init();
mDebugRegistry.registerProperty("d.shadowmap.debug_directional_shadowmap",
&debug.shadowmap.debug_directional_shadowmap, [this]() {
mMaterials.forEach([](FMaterial* material) {
@@ -1171,6 +1171,8 @@ Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
ASSERT_PRECONDITION(featureLevel <= getSupportedFeatureLevel(),
"Feature level %u not supported", (unsigned)featureLevel);
ASSERT_PRECONDITION(mActiveFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1,
"Cannot adjust feature level beyond 0 at runtime");
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
}
@@ -1204,6 +1206,11 @@ Engine::Builder& Engine::Builder::config(Engine::Config const* config) noexcept
return *this;
}
Engine::Builder& Engine::Builder::featureLevel(FeatureLevel featureLevel) noexcept {
mImpl->mFeatureLevel = featureLevel;
return *this;
}
Engine::Builder& Engine::Builder::sharedContext(void* sharedContext) noexcept {
mImpl->mSharedContext = sharedContext;
return *this;
+1 -1
View File
@@ -231,7 +231,7 @@ public:
default:
return backend::ShaderLanguage::ESSL3;
case Backend::OPENGL:
return mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0
return getDriver().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0
? backend::ShaderLanguage::ESSL1 : backend::ShaderLanguage::ESSL3;
case Backend::VULKAN:
return backend::ShaderLanguage::SPIRV;
+6 -5
View File
@@ -57,7 +57,9 @@ static MaterialParser* createParser(Backend backend, ShaderLanguage language,
}
ASSERT_PRECONDITION(materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND,
"the material was not built for the %s backend\n", backendToString(backend));
"the material was not built for the %s backend and %s shader language\n",
backendToString(backend),
shaderLanguageToString(language));
ASSERT_PRECONDITION(materialResult == MaterialParser::ParseResult::SUCCESS,
"could not parse the material package");
@@ -184,7 +186,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
success = parser->getUIB(&mUniformInterfaceBlock);
assert_invariant(success);
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
if (UTILS_UNLIKELY(engine.getShaderLanguage() == ShaderLanguage::ESSL1)) {
success = parser->getBindingUniformInfo(&mBindingUniformInfo);
assert_invariant(success);
@@ -258,7 +260,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS,
(bool)powerVrShaderWorkarounds });
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
if (UTILS_UNLIKELY(engine.getShaderLanguage() == ShaderLanguage::ESSL1)) {
// The actual value of this spec-constant is set in the OpenGLDriver backend.
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_SRGB_SWAPCHAIN_EMULATION,
@@ -640,7 +642,6 @@ Program FMaterial::getProgramWithVariants(
FEngine const& engine = mEngine;
const ShaderModel sm = engine.getShaderModel();
const bool isNoop = engine.getBackend() == Backend::NOOP;
const FeatureLevel engineFeatureLevel = engine.getActiveFeatureLevel();
/*
* Vertex shader
*/
@@ -694,7 +695,7 @@ Program FMaterial::getProgramWithVariants(
}
}
if (engineFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
if (UTILS_UNLIKELY(mEngine.getShaderLanguage() == ShaderLanguage::ESSL1)) {
assert_invariant(!mBindingUniformInfo.empty());
for (auto const& [index, uniforms] : mBindingUniformInfo) {
program.uniforms(uint32_t(index), uniforms);
+1
View File
@@ -859,6 +859,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
}
config.screenSpaceReflectionHistoryNotReady = !reflections;
}
// --------------------------------------------------------------------------------------------
+49 -17
View File
@@ -33,6 +33,8 @@
#include <utils/Range.h>
#include <utils/Systrace.h>
#include <math/quat.h>
#include <algorithm>
using namespace filament::backend;
@@ -52,7 +54,7 @@ FScene::~FScene() noexcept = default;
void FScene::prepare(utils::JobSystem& js,
LinearAllocatorArena& allocator,
const mat4& worldOriginTransform,
mat4 const& worldTransform,
bool shadowReceiversAreCasters) noexcept {
// TODO: can we skip this in most cases? Since we rely on indices staying the same,
// we could only skip, if nothing changed in the RCM.
@@ -168,7 +170,7 @@ void FScene::prepare(utils::JobSystem& js,
* Fill the SoA with the JobSystem
*/
auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldOriginTransform,
auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldTransform,
&sceneData, shadowReceiversAreCasters](auto* p, auto c) {
SYSTRACE_NAME("renderableWork");
@@ -176,12 +178,12 @@ void FScene::prepare(utils::JobSystem& js,
auto [ri, ti] = p[i];
// this is where we go from double to float for our transforms
const mat4f worldTransform{
worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
const bool reversedWindingOrder = det(worldTransform.upperLeft()) < 0;
const mat4f shaderWorldTransform{
worldTransform * tcm.getWorldTransformAccurate(ti) };
const bool reversedWindingOrder = det(shaderWorldTransform.upperLeft()) < 0;
// compute the world AABB so we can perform culling
const Box worldAABB = rigidTransform(rcm.getAABB(ri), worldTransform);
const Box worldAABB = rigidTransform(rcm.getAABB(ri), shaderWorldTransform);
auto visibility = rcm.getVisibility(ri);
visibility.reversedWindingOrder = reversedWindingOrder;
@@ -199,7 +201,7 @@ void FScene::prepare(utils::JobSystem& js,
assert_invariant(index < sceneData.size());
sceneData.elementAt<RENDERABLE_INSTANCE>(index) = ri;
sceneData.elementAt<WORLD_TRANSFORM>(index) = worldTransform;
sceneData.elementAt<WORLD_TRANSFORM>(index) = shaderWorldTransform;
sceneData.elementAt<VISIBILITY_STATE>(index) = visibility;
sceneData.elementAt<SKINNING_BUFFER>(index) = rcm.getSkinningBufferInfo(ri);
sceneData.elementAt<MORPHING_BUFFER>(index) = rcm.getMorphingBufferInfo(ri);
@@ -216,19 +218,20 @@ void FScene::prepare(utils::JobSystem& js,
}
};
auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldOriginTransform,
auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldTransform,
&lightData](auto* p, auto c) {
SYSTRACE_NAME("lightWork");
for (size_t i = 0; i < c; i++) {
auto [li, ti] = p[i];
// this is where we go from double to float for our transforms
const mat4f worldTransform{ worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
const float4 position = worldTransform * float4{ lcm.getLocalPosition(li), 1 };
mat4f const shaderWorldTransform{
worldTransform * tcm.getWorldTransformAccurate(ti) };
float4 const position = shaderWorldTransform * float4{ lcm.getLocalPosition(li), 1 };
float3 d = 0;
if (!lcm.isPointLight(li) || lcm.isIESLight(li)) {
d = lcm.getLocalDirection(li);
// using mat3f::getTransformForNormals handles non-uniform scaling
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
d = normalize(mat3f::getTransformForNormals(shaderWorldTransform.upperLeft()) * d);
}
size_t const index = DIRECTIONAL_LIGHTS_COUNT + std::distance(first, p) + i;
assert_invariant(index < lightData.size());
@@ -261,14 +264,43 @@ void FScene::prepare(utils::JobSystem& js,
*/
if (auto [li, ti] = directionalLightInstances ; li) {
const mat4f worldTransform{
worldOriginTransform * tcm.getWorldTransformAccurate(ti) };
// using mat3f::getTransformForNormals handles non-uniform scaling
float3 d = lcm.getLocalDirection(li);
d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d);
// 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()
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
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(shaderWorldTransform);
double3 const d = worlTransformNormals * localDirection;
double3 const s = worlTransformNormals * shadowLocalDirection;
// We compute the reference point for snapping shadowmaps without applying the
// rotation of `worldOriginTransform` on both sides, so that we don't have any instability
// due to the limited precision of the "light space" matrix (even at double precision).
// getMv() Returns the world-to-lightspace transformation. See ShadowMap.cpp.
auto getMv = [](double3 direction) -> mat3 {
// We use the x-axis as the "up" reference so that the math is stable when the light
// 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(worldDirection);
double2 const lsReferencePoint = (Mv * worldOrigin).xy;
constexpr float inf = std::numeric_limits<float>::infinity();
lightData.elementAt<POSITION_RADIUS>(0) = float4{ 0, 0, 0, inf };
lightData.elementAt<DIRECTION>(0) = d;
lightData.elementAt<DIRECTION>(0) = normalize(d);
lightData.elementAt<SHADOW_DIRECTION>(0) = normalize(s);
lightData.elementAt<SHADOW_REF>(0) = lsReferencePoint;
lightData.elementAt<LIGHT_INSTANCE>(0) = li;
} else {
lightData.elementAt<LIGHT_INSTANCE>(0) = 0;
+5 -1
View File
@@ -71,7 +71,7 @@ public:
void terminate(FEngine& engine);
void prepare(utils::JobSystem& js, LinearAllocatorArena& allocator,
math::mat4 const& worldOriginTransform, bool shadowReceiversAreCasters) noexcept;
math::mat4 const& worldTransform, bool shadowReceiversAreCasters) noexcept;
void prepareVisibleRenderables(utils::Range<uint32_t> visibleRenderables) noexcept;
@@ -162,6 +162,8 @@ public:
enum {
POSITION_RADIUS,
DIRECTION,
SHADOW_DIRECTION,
SHADOW_REF,
LIGHT_INSTANCE,
VISIBILITY,
SCREEN_SPACE_Z_RANGE,
@@ -171,6 +173,8 @@ public:
using LightSoa = utils::StructureOfArrays<
math::float4,
math::float3,
math::float3,
math::double2,
FLightManager::Instance,
Culler::result_type,
math::float2,
+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_TARGET_MOBILE
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);
+1 -1
View File
@@ -104,7 +104,7 @@ public:
// Returns the max number of levels for a texture of given dimensions
static inline uint8_t maxLevelCount(uint32_t width, uint32_t height) noexcept {
uint32_t maxDimension = std::max(width, height);
uint32_t const maxDimension = std::max(width, height);
return maxLevelCount(maxDimension);
}
+16 -11
View File
@@ -397,8 +397,8 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
* The "world origin" is also used to keep the origin close to the camera position to
* improve fp precision in the shader for large scenes.
*/
mat4 translation;
mat4 rotation;
double3 translation;
mat3 rotation;
/*
* Calculate all camera parameters needed to render this View for this frame.
@@ -409,16 +409,18 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
// view-space, which improves floating point precision in the shader by staying around
// zero, where fp precision is highest. This also ensures that when the camera is placed
// very far from the origin, objects are still rendered and lit properly.
translation = mat4::translation( -camera->getPosition() );
translation = -camera->getPosition();
}
FIndirectLight const* const ibl = scene->getIndirectLight();
if (ibl) {
// the IBL transformation must be a rigid transform
rotation = mat4{ transpose(scene->getIndirectLight()->getRotation()) };
rotation = mat3{ transpose(scene->getIndirectLight()->getRotation()) };
// it is important to orthogonalize the matrix when converting it to doubles, because
// as float, it only has about a 1e-8 precision on the size of the basis vectors
rotation = orthogonalize(rotation);
}
return { *camera, rotation * translation };
return { *camera, mat4{ rotation } * mat4::translation(translation) };
}
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
@@ -446,7 +448,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
// intent of the code, which is that we should only depend on CameraInfo here.
// This is an extremely uncommon case.
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix());
const mat4 view = inverse(cameraInfo.worldTransform * mCullingCamera->getModelMatrix());
return Frustum{ mat4f{ projection * view }};
}
};
@@ -459,7 +461,9 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Gather all information needed to render this scene. Apply the world origin to all
* objects in the scene.
*/
scene->prepare(js, arena.getAllocator(), cameraInfo.worldOrigin, hasVSM());
scene->prepare(js, arena.getAllocator(),
cameraInfo.worldTransform,
hasVSM());
/*
* Light culling: runs in parallel with Renderable culling (below)
@@ -658,7 +662,6 @@ void FView::bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noe
mPerViewUniforms.bind(driver);
if (UTILS_UNLIKELY(driver.getFeatureLevel() == backend::FeatureLevel::FEATURE_LEVEL_0)) {
// FIXME: should be okay to use driver (instead of engine) for FEATURE_LEVEL_0 checks
return;
}
@@ -743,9 +746,11 @@ void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
mPerViewUniforms.prepareSSAO(ssao, mAmbientOcclusionOptions);
}
void FView::prepareSSR(Handle<HwTexture> ssr, float refractionLodOffset,
void FView::prepareSSR(Handle<HwTexture> ssr,
bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
mPerViewUniforms.prepareSSR(ssr, refractionLodOffset, ssrOptions);
mPerViewUniforms.prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions);
}
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
+3 -2
View File
@@ -145,7 +145,8 @@ public:
void prepareLighting(FEngine& engine, ArenaScope& arena, CameraInfo const& cameraInfo) noexcept;
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset,
void prepareSSR(backend::Handle<backend::HwTexture> ssr, bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
@@ -194,7 +195,7 @@ public:
void setStereoscopicOptions(StereoscopicOptions const& options);
FCamera const* getDirectionalLightCamera() const noexcept {
return &mShadowMapManager.getShadowMap(0)->getDebugCamera();
return mShadowMapManager.getDirectionalLightCamera();
}
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
+6 -2
View File
@@ -22,7 +22,8 @@ material {
],
depthWrite : false,
depthCulling : false,
domain: postprocess
domain: postprocess,
featureLevel : 0
}
vertex {
@@ -34,7 +35,10 @@ vertex {
fragment {
void postProcess(inout PostProcessInputs postProcess) {
#if __VERSION__ == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
#endif
}
}
+2 -2
View File
@@ -702,8 +702,8 @@ TEST(FilamentTest, FroxelData) {
LightManager::Instance instance = engine->getLightManager().getInstance(e);
FScene::LightSoa lights;
lights.push_back({}, {}, {}, {}, {}, {}); // first one is always skipped
lights.push_back(float4{ 0, 0, -5, 1 }, {}, instance, 1, {}, {});
lights.push_back({}, {}, {}, {}, {}, {}, {}, {}); // first one is always skipped
lights.push_back(float4{ 0, 0, -5, 1 }, {}, {}, {}, instance, 1, {}, {});
{
froxelData.froxelizeLights(*engine, {}, lights);
+79
View File
@@ -0,0 +1,79 @@
;;; c-filament-style.el --- Filament C++ style -*- lexical-binding: t -*-
;; Copyright (C) 2023 Google LLC
;; Author: Eliza Velasquez
;; Version: 0.1.0
;; Created: 2023-10-31
;; Package-Requires: ((emacs "24.1"))
;; Keywords: c
;; URL: https://github.com/google/filament
;; SPDX-License-Identifier: Apache-2
;;; Commentary:
;; Defines a basic Filament style for C++ code in Emacs.
;;; Code:
(require 'cc-mode)
(defvar c-syntactic-context)
(defun c-filament-style-lineup-brace-list-intro (langelem)
"Indent first line of braced lists in the Filament style.
This properly indents doubled-up arglists + lists, e.g. ({. It
also properly indents enums at 1x and other lists at 2x.
LANGELEM is the cons of the syntactic symbol and the anchor
position (or nil if there is none)."
(save-excursion
(let (case-fold-search)
(goto-char (c-langelem-pos langelem))
(if (looking-at "enum\\b")
c-basic-offset
(if (assq 'arglist-cont-nonempty c-syntactic-context)
(- c-basic-offset)
(* 2 c-basic-offset))))))
(defun c-filament-style-lineup-brace-list-entry (_langelem)
"Indent following lines in braced lists in the Filament style.
This properly indents doubled-up arglists + lists, e.g. ({."
(if (assq 'arglist-cont-nonempty c-syntactic-context)
(- (* c-basic-offset 2))
0))
(defun c-filament-style-lineup-arglist (langelem)
"Indent following lines in braced lists in the Filament style.
This properly indents arglists nested in if statements. LANGELEM
is the cons of the syntactic symbol and the anchor position (or
nil if there is none)."
(save-excursion
(let (case-fold-search)
(goto-char (c-langelem-pos langelem))
(if (and (cdr c-syntactic-context)
(looking-at "if\\b"))
c-basic-offset
(* 2 c-basic-offset)))))
(c-add-style "filament"
'((c-basic-offset . 4)
(c-offsets-alist
(innamespace . 0)
(inextern-lang . 0)
(arglist-intro . c-filament-style-lineup-arglist)
(arglist-cont . 0)
(arglist-cont-nonempty . c-filament-style-lineup-arglist)
(arglist-close . c-filament-style-lineup-arglist)
(statement-cont . ++)
(case-label . +)
(brace-list-intro . c-filament-style-lineup-brace-list-intro)
(brace-list-entry . c-filament-style-lineup-brace-list-entry)
(brace-list-close . c-filament-style-lineup-brace-list-entry)
(label . [0]))))
(provide 'c-filament-style)
;;; c-filament-style.el ends here
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.45.1"
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.45.1/filament-v1.45.1-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 = {
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_5" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -20,24 +20,24 @@
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="B3q-vU-ULy">
<rect key="frame" x="68" y="80" width="279" height="636"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<mutableString key="text">This is a native UITextView. The triangle is rendered by Filament.
<string key="text">This is a native UITextView. The triangle is rendered by Filament.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras tincidunt neque a metus laoreet imperdiet. In cursus arcu quis turpis viverra convallis. Sed eu laoreet metus. Nunc id est lacus. Sed hendrerit elementum velit a finibus. Mauris vitae scelerisque erat. Proin ultricies quam nec justo vehicula, sed porta nulla facilisis.
Phasellus orci lectus, ullamcorper efficitur urna non, sodales venenatis sem. Suspendisse auctor, enim non dapibus malesuada, est urna sollicitudin sapien, in sodales mi nulla quis odio. Suspendisse viverra vel orci vitae vestibulum. Fusce tristique ligula nisi, bibendum gravida est volutpat sed. Vivamus ut justo porttitor, feugiat lorem eget, rutrum orci. Donec nec nibh vitae ex consectetur pulvinar. Sed dignissim, metus a tincidunt finibus, justo massa auctor nunc, sit amet cursus ligula sapien ut enim. Quisque ut congue libero, in ultricies tortor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean ac justo in tortor commodo efficitur et ut leo.
</mutableString>
</string>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2KD-1D-oAh" customClass="FilamentView">
<view opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2KD-1D-oAh" customClass="FilamentView">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="cdP-0z-J5l"/>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
@@ -45,4 +45,12 @@ Phasellus orci lectus, ullamcorper efficitur urna non, sodales venenatis sem. Su
<point key="canvasLocation" x="140.57971014492756" y="133.25892857142856"/>
</scene>
</scenes>
<resources>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -89,6 +89,7 @@ static constexpr uint8_t BAKED_COLOR_PACKAGE[] = {
#elif FILAMENT_APP_USE_METAL
[self initializeMetalLayer];
#endif
self.opaque = NO;
[self initializeFilament];
self.contentScaleFactor = UIScreen.mainScreen.nativeScale;
}
@@ -160,6 +160,8 @@ public:
bool isEmpty() const noexcept { return mFieldInfoList.empty(); }
bool isEmptyForFeatureLevel(backend::FeatureLevel featureLevel) const noexcept;
Alignment getAlignment() const noexcept { return mAlignment; }
Target getTarget() const noexcept { return mTarget; }
@@ -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;
+8 -1
View File
@@ -171,6 +171,14 @@ BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
return &mFieldInfoList[pos->second];
}
bool BufferInterfaceBlock::isEmptyForFeatureLevel(
backend::FeatureLevel featureLevel) const noexcept {
return std::all_of(mFieldInfoList.begin(), mFieldInfoList.end(),
[featureLevel](auto const &info) {
return featureLevel < info.minFeatureLevel;
});
}
uint8_t UTILS_NOINLINE BufferInterfaceBlock::baseAlignmentForType(BufferInterfaceBlock::Type type) noexcept {
switch (type) {
case Type::BOOL:
@@ -230,4 +238,3 @@ uint8_t UTILS_NOINLINE BufferInterfaceBlock::strideForType(BufferInterfaceBlock:
}
} // namespace filament
@@ -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;
}
+3 -2
View File
@@ -13,7 +13,8 @@ material {
shadingModel : unlit,
culling : none,
depthCulling: false,
blending : transparent
blending : transparent,
featureLevel : 0
}
fragment {
@@ -21,7 +22,7 @@ fragment {
prepareMaterial(material);
vec2 uv = getUV0();
uv.y = 1.0 - uv.y;
vec4 albedo = texture(materialParams_albedo, uv);
vec4 albedo = texture2D(materialParams_albedo, uv);
material.baseColor = getColor() * albedo;
material.baseColor.rgb *= material.baseColor.a;
}
+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 -9
View File
@@ -239,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;
@@ -307,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;
@@ -618,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;
@@ -636,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 }
+13 -11
View File
@@ -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,
@@ -587,11 +583,11 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
}
if (mBlendingMode == BlendingMode::MASKED) {
ibb.add({{ "_maskThreshold", 0, UniformType::FLOAT }});
ibb.add({{ "_maskThreshold", 0, UniformType::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});
}
if (mDoubleSidedCapability) {
ibb.add({{ "_doubleSided", 0, UniformType::BOOL }});
ibb.add({{ "_doubleSided", 0, UniformType::BOOL, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});
}
mRequiredAttributes.set(VertexAttribute::POSITION);
@@ -1098,6 +1094,12 @@ error:
return Package::invalidPackage();
}
// Force post process materials to be unlit. This prevents imposing a lot of extraneous
// data, code, and expectations for materials which do not need them.
if (mMaterialDomain == MaterialDomain::POST_PROCESS) {
mShading = Shading::UNLIT;
}
// Add a default color output.
if (mMaterialDomain == MaterialDomain::POST_PROCESS && mOutputs.empty()) {
output(VariableQualifier::OUT,
@@ -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));
}
}
+29 -8
View File
@@ -63,9 +63,6 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
out << "#extension GL_OES_EGL_image_external : require\n\n";
}
}
if (material.has3dSamplers && mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
out << "#extension GL_OES_texture_3D : require\n\n";
}
if (v.hasInstancedStereo() && stage == ShaderStage::VERTEX) {
// If we're not processing the shader through glslang (in the case of unoptimized
// OpenGL shaders), then we need to add the #extension string ourselves.
@@ -89,6 +86,10 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
break;
}
if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
out << "#extension GL_OES_standard_derivatives : require\n\n";
}
// This allows our includer system to use the #line directive to denote the source file for
// #included code. This way, glslang reports errors more accurately.
out << "#extension GL_GOOGLE_cpp_style_line_directive : enable\n\n";
@@ -163,11 +164,13 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "VARYING", "out");
generateDefine(out, "ATTRIBUTE", "in");
} else if (stage == ShaderStage::FRAGMENT) {
generateDefine(out, "VARYING", "in");
}
} else {
generateDefine(out, "VARYING", "varying");
generateDefine(out, "ATTRIBUTE", "attribute");
}
auto getShadingDefine = [](Shading shading) -> const char* {
@@ -421,6 +424,13 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type,
return out;
}
// Feature level 0 only supports one output.
if (index > 0 && mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
slog.w << "Discarding an output in the generated ESSL 1.0 shader: index = " << index
<< ", name = " << name.c_str() << io::endl;
return out;
}
// TODO: add and support additional variable qualifiers
(void) qualifier;
assert(qualifier == MaterialBuilder::VariableQualifier::OUT);
@@ -434,7 +444,9 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type,
// formats behind the scenes. It's an error to output fewer components than the attachment
// needs, so we always output a float4 instead of a float3. It's never an error to output extra
// components.
if (mTargetApi == TargetApi::METAL) {
//
// Meanwhile, ESSL 1.0 must always write to gl_FragColor, a vec4.
if (mTargetApi == TargetApi::METAL || mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
if (outputType == MaterialBuilder::OutputType::FLOAT3) {
outputType = MaterialBuilder::OutputType::FLOAT4;
swizzleString = ".rgb";
@@ -446,13 +458,21 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type,
const char* typeString = getOutputTypeName(outputType);
out << "\n#define FRAG_OUTPUT" << index << " " << name.c_str();
out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str();
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;
out << "\n#define FRAG_OUTPUT_TYPE" << index << " " << typeString;
out << "\n#define FRAG_OUTPUT_SWIZZLE" << index << " " << swizzleString;
out << "\nlayout(location=" << index << ") out " << precisionString << " "
out << "\n";
if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
out << "\nlayout(location=" << index << ") out " << precisionString << " "
<< typeString << " output_" << name.c_str() << ";\n";
}
return out;
}
@@ -554,11 +574,12 @@ io::sstream& CodeGenerator::generateUboAsPlainUniforms(io::sstream& out, ShaderS
io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, ShaderStage stage,
uint32_t binding, const BufferInterfaceBlock& uib) const {
auto const& infos = uib.getFieldInfoList();
if (infos.empty()) {
if (uib.isEmptyForFeatureLevel(mFeatureLevel)) {
return out;
}
auto const& infos = uib.getFieldInfoList();
if (mTargetLanguage == TargetLanguage::GLSL &&
mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
// we need to generate a structure instead
-18
View File
@@ -872,24 +872,6 @@ TEST_F(MaterialCompiler, FeatureLevel0Sampler2D) {
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, FeatureLevel0Sampler3D) {
std::string shaderCode(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture3D(materialParams_sampler, vec3(0.0, 0.0, 0.0));
}
)");
filamat::MaterialBuilder builder;
builder.parameter("sampler", SamplerType::SAMPLER_3D);
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_0);
builder.shading(filament::Shading::UNLIT);
builder.material(shaderCode.c_str());
builder.printShaders(true);
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, FeatureLevel0Ess3CallFails) {
std::string shaderCode(R"(
void material(inout MaterialInputs material) {
@@ -9,7 +9,8 @@ material {
blending : transparent,
culling : none,
depthCulling : false,
shadingModel : unlit
shadingModel : unlit,
featureLevel : 0
}
fragment {
+7 -5
View File
@@ -603,10 +603,14 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
return Engine::Builder()
.backend(backend)
.platform(mFilamentApp->mVulkanPlatform)
.featureLevel(config.featureLevel)
.build();
#endif
}
return Engine::Builder().backend(backend).build();
return Engine::Builder()
.backend(backend)
.featureLevel(config.featureLevel)
.build();
};
if (config.headless) {
@@ -647,10 +651,8 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
#endif
// Select the feature level to use
config.featureLevel = std::min(config.featureLevel,
mFilamentApp->mEngine->getSupportedFeatureLevel());
mFilamentApp->mEngine->setActiveFeatureLevel(config.featureLevel);
// Write back the active feature level.
config.featureLevel = mFilamentApp->mEngine->getActiveFeatureLevel();
mSwapChain = mFilamentApp->mEngine->createSwapChain(
nativeSwapChain, filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
@@ -217,7 +217,7 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()(
"equirect must be a 2D texture.");
UTILS_UNUSED_IN_RELEASE
const uint8_t maxLevelCount = uint8_t(std::log2(equirect->getWidth()) + 0.5f) + 1u;
const uint8_t maxLevelCount = std::max(1, std::ilogbf(float(equirect->getWidth())) + 1);
ASSERT_PRECONDITION(equirect->getLevels() == maxLevelCount,
"equirect must have %u mipmap levels allocated.", +maxLevelCount);
+2 -2
View File
@@ -41,8 +41,8 @@ set(SRCS
set(RESOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(RESOURCE_BINS
${CMAKE_CURRENT_SOURCE_DIR}/web/style.css
${CMAKE_CURRENT_SOURCE_DIR}/web/script.js
${CMAKE_CURRENT_SOURCE_DIR}/web/api.js
${CMAKE_CURRENT_SOURCE_DIR}/web/app.js
${CMAKE_CURRENT_SOURCE_DIR}/web/index.html
)
+42 -57
View File
@@ -50,13 +50,28 @@ using utils::FixedCapacityVector;
// serves files directly from the source code tree.
#define SERVE_FROM_SOURCE_TREE 0
// When set to 1, we will serve an experimental frontend, which will potentially replace the current
// frontend when ready.
#define EXPERIMENTAL_WEB_FRAMEWORK 0
#if SERVE_FROM_SOURCE_TREE
namespace {
std::string const BASE_URL = "libs/matdbg/web";
} // anonymous
#else
#if !SERVE_FROM_SOURCE_TREE
#include "matdbg_resources.h"
#endif
#include <unordered_map>
namespace {
struct Asset {
std::string_view mime;
std::string_view data;
};
std::unordered_map<std::string_view, Asset> ASSET_MAP;
} // anonymous
#endif // SERVE_FROM_SOURCE_TREE
namespace filament::matdbg {
@@ -74,14 +89,6 @@ std::string_view const DebugServer::kErrorHeader =
"HTTP/1.1 404 Not Found\r\nContent-Type: %s\r\n"
"Connection: close\r\n\r\n";
#if EXPERIMENTAL_WEB_FRAMEWORK
namespace {
std::string const BASE_URL = "libs/matdbg/web/experiment";
} // anonymous
class FileRequestHandler : public CivetHandler {
public:
FileRequestHandler(DebugServer* server) : mServer(server) {}
@@ -92,66 +99,44 @@ public:
if (uri == "/") {
uri = "/index.html";
}
#if SERVE_FROM_SOURCE_TREE
if (uri == "/index.html" || uri == "/app.js" || uri == "/api.js") {
mg_send_file(conn, (BASE_URL + uri).c_str());
return true;
}
slog.e << "DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
return false;
}
private:
DebugServer* mServer;
};
#else
class FileRequestHandler : public CivetHandler {
public:
FileRequestHandler(DebugServer* server) : mServer(server) {}
bool handleGet(CivetServer *server, struct mg_connection *conn) {
auto const& kSuccessHeader = DebugServer::kSuccessHeader;
const struct mg_request_info* request = mg_get_request_info(conn);
std::string uri(request->request_uri);
if (uri == "/" || uri == "/index.html") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/index.html");
#else
mg_printf(conn, kSuccessHeader.data(), "text/html");
mg_write(conn, mServer->mHtml.c_str(), mServer->mHtml.size());
#endif
return true;
}
if (uri == "/style.css") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/style.css");
#else
mg_printf(conn, kSuccessHeader.data(), "text/css");
mg_write(conn, mServer->mCss.c_str(), mServer->mCss.size());
#endif
return true;
}
if (uri == "/script.js") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/script.js");
#else
mg_printf(conn, kSuccessHeader.data(), "text/javascript");
mg_write(conn, mServer->mJavascript.c_str(), mServer->mJavascript.size());
#endif
auto const& asset_itr = ASSET_MAP.find(uri);
if (asset_itr != ASSET_MAP.end()) {
auto const& mime = asset_itr->second.mime;
auto const& data = asset_itr->second.data;
mg_printf(conn, kSuccessHeader.data(), mime.data());
mg_write(conn, data.data(), data.size());
return true;
}
#endif
slog.e << "DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
return false;
}
private:
DebugServer* mServer;
};
#endif
DebugServer::DebugServer(Backend backend, int port) : mBackend(backend) {
#if !SERVE_FROM_SOURCE_TREE
mHtml = CString((const char*) MATDBG_RESOURCES_INDEX_DATA, MATDBG_RESOURCES_INDEX_SIZE - 1);
mJavascript = CString((const char*) MATDBG_RESOURCES_SCRIPT_DATA, MATDBG_RESOURCES_SCRIPT_SIZE - 1);
mCss = CString((const char*) MATDBG_RESOURCES_STYLE_DATA, MATDBG_RESOURCES_STYLE_SIZE - 1);
ASSET_MAP["/index.html"] = {
.mime = "text/html",
.data = {(char const*) MATDBG_RESOURCES_INDEX_DATA},
};
ASSET_MAP["/app.js"] = {
.mime = "text/javascript",
.data = {(char const*) MATDBG_RESOURCES_APP_DATA},
};
ASSET_MAP["/api.js"] = {
.mime = "text/javascript",
.data = {(char const*) MATDBG_RESOURCES_API_DATA},
};
#endif
// By default the server spawns 50 threads so we override this to 10. According to the civetweb
@@ -242,6 +242,10 @@ class CodeViewer extends LitElement {
}
_rebuild() {
if (!this.active || !this.modified) {
console.log('Called rebuild while variant is inactive or unmodified');
return;
}
this.dispatchEvent(new CustomEvent(
'rebuild-shader',
{detail: this.editor.getValue(), bubbles: true, composed: true}
-23
View File
@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Filament Debugger</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<style>
html, body {
height: 100%;
margin: 0;
font-family: "Open Sans";
}
</style>
<script src="api.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.js"></script>
</head>
<body>
<script src="app.js" type="module"></script>
<matdbg-viewer></matdbg-viewer>
</body>
</html>
+18 -91
View File
@@ -1,96 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<title>Filament Debugger</title>
<meta charset="utf-8">
<link rel="icon" type="image/png" href="https://google.github.io/filament/favicon.png" />
<link href="https://fonts.googleapis.com/css?family=Lexend Deca" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body class="vbox viewport">
<header>matdbg</header>
<section class="main hbox space-between">
<nav class="vbox">
<div id="material-list" class="scrollable squishy">
</div>
<div id="material-detail" class="scrollable stretchy">
</div>
</nav>
<article id="shader-source">
</article>
</section>
<footer>&nbsp;</footer>
<template id="material-list-template">
<div style="white-space: nowrap">
{{#item}}
{{#is_label}}
<br/><b>{{label}}</b>
{{/is_label}}
{{#is_material}}
<a class="material {{classes}}" data-matid="{{matid}}">{{name}}</a>
{{/is_material}}
<br/>
{{/item}}
</div>
</template>
<template id="material-detail-template">
<div style="white-space: pre">
name = {{ name }}
version = {{ version }}
{{#required_attributes.length}}
<b>Required attributes</b>
{{#required_attributes}}
{{.}}
{{/required_attributes}}
{{/required_attributes.length}}
<b>OpenGL shaders</b>
{{#opengl}}
<a class="shader {{classes}}" data-glindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/opengl}}
<b>Vulkan shaders</b>
{{#vulkan}}
<a class="shader {{classes}}" data-vkindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/vulkan}}
<b>Metal shaders</b>
{{#metal}}
<a class="shader {{classes}}" data-metalindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/metal}}
<b>Material details</b>
{{#shading}}
shading model = {{model}}
vertex domain = {{vertex_domain}}
interpolation = {{interpolation}}
shadow multiply = {{shadow_multiply}}
specular antialiasing = {{specular_antialiasing}}
variance = {{variance}}
threshold = {{threshold}}
clear coat IOR change = {{clear_coat_IOR_change}}
{{/shading}}
{{#raster}}
blending = {{blending}}
mask threshold = {{mask_threshold}}
color write = {{color_write}}
depth write = {{depth_write}}
depth test = {{depth_test}}
double sided = {{double_sided}}
culling = {{culling}}
transparency = {{transparency}}
{{/raster}}
</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.17.1/min/vs/loader.js"></script>
<script src="script.js"></script>
</body>
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<style>
html, body {
height: 100%;
margin: 0;
font-family: "Open Sans";
}
</style>
<script src="api.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.js"></script>
</head>
<body>
<script src="app.js" type="module"></script>
<matdbg-viewer></matdbg-viewer>
</body>
</html>
-516
View File
@@ -1,516 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
const kMonacoBaseUrl = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/';
const kUntitledPlaceholder = "untitled";
const materialList = document.getElementById("material-list");
const materialDetail = document.getElementById("material-detail");
const header = document.querySelector("header");
const footer = document.querySelector("footer");
const shaderSource = document.getElementById("shader-source");
const matDetailTemplate = document.getElementById("material-detail-template");
const matListTemplate = document.getElementById("material-list-template");
const STATUS_LOOP_TIMEOUT = 3000;
const gMaterialDatabase = {};
let gEditor = null;
let gCurrentMaterial = "00000000";
let gCurrentLanguage = "glsl";
let gCurrentShader = { matid: "00000000", glindex: 0 };
let gEditorIsLoading = false;
require.config({ paths: { "vs": `${kMonacoBaseUrl}vs` }});
window.MonacoEnvironment = {
getWorkerUrl: function() {
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
self.MonacoEnvironment = {
baseUrl: '${kMonacoBaseUrl}'
};
importScripts('${kMonacoBaseUrl}vs/base/worker/workerMain.js');`
)}`;
}
};
function getShaderAPI(selection) {
if (!selection) {
selection = gCurrentShader;
}
if ("glindex" in selection) return "opengl";
if ("vkindex" in selection) return "vulkan";
if ("metalindex" in selection) return "metal";
return "error";
}
function rebuildMaterial() {
let api = 0, index = -1;
const shader = getShaderRecord(gCurrentShader);
const shaderApi = getShaderAPI();
switch (shaderApi) {
case "opengl": api = 1; index = gCurrentShader.glindex; break;
case "vulkan": api = 2; index = gCurrentShader.vkindex; break;
case "metal": api = 3; index = gCurrentShader.metalindex; break;
}
if (shaderApi === "vulkan") {
if (gCurrentLanguage === "glsl") {
delete shader["spirv"];
} else if (gCurrentLanguage === "spirv") {
delete shader["glsl"];
}
}
const editedText = shader[gCurrentLanguage];
const req = new XMLHttpRequest();
req.open('POST', '/api/edit');
req.send(`${gCurrentShader.matid} ${api} ${index} ${editedText}`);
}
document.querySelector("body").addEventListener("click", (evt) => {
const anchor = evt.target.closest("a");
if (!anchor) {
return;
}
// Handle selection of a material.
if (anchor.classList.contains("material")) {
selectMaterial(anchor.dataset.matid, true);
return;
}
// Handle selection of a shader.
if (anchor.classList.contains("shader")) {
selectShader(anchor.dataset);
return;
}
// Handle a rebuild.
if (anchor.classList.contains("rebuild")) {
rebuildMaterial();
return;
}
// Handle language selection.
for (const lang of "glsl spirv msl".split(" ")) {
if (anchor.classList.contains(lang)) {
gCurrentLanguage = lang;
selectShader(gCurrentShader);
return;
}
}
});
// Handle Ctrl+Arrow for fast keyboard navigation between shader variants and materials. Either the
// materialStep or shaderStep argument can be non-zero (not both) and they must be -1, 0, or +1.
// TODO: this function could be vastly simplified by changing the format of the shader selector.
function selectNextShader(materialStep, shaderStep) {
if (materialStep !== 0) {
const matids = getDisplayedMaterials().map(m => m.matid).filter(m => m);
const currentIndex = matids.indexOf(gCurrentMaterial);
const nextIndex = currentIndex + materialStep;
if (nextIndex >= 0 && nextIndex < matids.length) {
selectMaterial(matids[nextIndex], true);
}
return;
}
const material = gMaterialDatabase[gCurrentMaterial];
const variants = [];
let currentIndex = 0;
for (const [index, shader] of material.opengl.entries()) {
if (index === gCurrentShader.glindex) currentIndex = variants.length;
variants.push({ matid, glindex: index });
}
for (const [index, shader] of material.vulkan.entries()) {
if (index === gCurrentShader.vkindex) currentIndex = variants.length;
variants.push({ matid, vkindex: index });
}
for (const [index, shader] of material.metal.entries()) {
if (index === gCurrentShader.metalindex) currentIndex = variants.length;
variants.push({ matid, metalindex: index });
}
const nextIndex = currentIndex + shaderStep;
if (nextIndex >= 0 && nextIndex < variants.length) {
selectShader(variants[nextIndex]);
}
}
function fetchMaterial(matid) {
fetch(`api/material?matid=${matid}`).then(function(response) {
return response.json();
}).then(function(matInfo) {
if (matid in gMaterialDatabase) {
return;
}
matInfo.matid = matid;
gMaterialDatabase[matid] = matInfo;
renderMaterialList();
});
}
function queryActiveShaders() {
if (!isConnected()) {
return;
}
fetch("api/active").then(function(response) {
return response.json();
}).then(function(activeMaterials) {
// The only active materials are the ones with active variants.
for (matid in gMaterialDatabase) {
const material = gMaterialDatabase[matid];
material.active = false;
}
for (matid in activeMaterials) {
const material = gMaterialDatabase[matid];
const activeBackend = activeMaterials[matid][0];
const activeShaders = activeMaterials[matid].slice(1);
for (const shader of material[activeBackend]) {
shader.active = activeShaders.indexOf(shader.variant) > -1;
material.active = material.active || shader.active;
}
}
renderMaterialList();
renderMaterialDetail();
})
.catch(error => {
// This can occur if the JSON is invalid.
console.error(error);
});
}
function isConnected() {
return footer.innerText == 'connected';
}
function onConnected() {
footer.innerText = 'connected';
fetch("api/matids").then(function(response) {
return response.json();
}).then(function(matInfo) {
for (matid of matInfo) {
if (!(matid in gMaterialDatabase)) {
fetchMaterial(matid);
}
}
});
}
function onDisconnected() {
footer.innerText = 'not connected';
for (matid in gMaterialDatabase) {
const material = gMaterialDatabase[matid];
material.active = false;
for (const shader of material.opengl) shader.active = false;
for (const shader of material.vulkan) shader.active = false;
for (const shader of material.metal) shader.active = false;
}
renderMaterialList();
renderMaterialDetail();
}
function statusLoop() {
// This is a hanging get except for when transition from disconnected to connected, which
// should return immediately.
fetch("api/status" + (isConnected() ? '' : '?firstTime'))
.then(async (response) => {
const matid = await response.text();
// A first-time request returned successfully
if (matid === '0') {
onConnected();
} else if (matid != '1') {
fetchMaterial(matid);
} // else matid == '1' and it's a no-op, we just loop again.
statusLoop();
})
.catch(err => {
onDisconnected();
setTimeout(statusLoop, STATUS_LOOP_TIMEOUT)
});
}
function fetchMaterials() {
fetch("api/materials").then(function(response) {
return response.json();
}).then(function(matJson) {
for (const matInfo of matJson) {
if (matInfo.matid in gMaterialDatabase) {
continue;
}
gMaterialDatabase[matInfo.matid] = matInfo;
}
selectMaterial(matJson[0].matid, true);
});
}
function fetchShader(selection, matinfo, onDone) {
let query, target, index;
switch (getShaderAPI(selection)) {
case "opengl":
index = parseInt(selection.glindex);
query = `type=${gCurrentLanguage}&glindex=${index}`;
target = matinfo.opengl[index];
break;
case "vulkan":
index = parseInt(selection.vkindex);
query = `type=${gCurrentLanguage}&vkindex=${index}`;
target = matinfo.vulkan[index];
break;
case "metal":
index = parseInt(selection.metalindex);
query = `type=${gCurrentLanguage}&metalindex=${index}`;
target = matinfo.metal[index];
break;
}
fetch(`api/shader?matid=${matinfo.matid}&${query}`).then(function(response) {
return response.text();
}).then(function(shaderText) {
target[gCurrentLanguage] = shaderText;
onDone();
});
}
function getDisplayedMaterials() {
const items = [];
// Names need not be unique, so we display a numeric suffix for non-unique names.
// To achieve stable ordering of anonymous materials, we first sort by matid.
const labels = new Set();
const matids = Object.keys(gMaterialDatabase).sort();
const duplicatedLabels = {};
for (const matid of matids) {
const name = gMaterialDatabase[matid].name || kUntitledPlaceholder;
if (labels.has(name)) {
duplicatedLabels[name] = 0;
} else {
labels.add(name);
}
}
// Build a list of objects to pass into the template string.
for (const matid of matids) {
const item = Object.assign({}, gMaterialDatabase[matid]);
item.classes = matid === gCurrentMaterial ? "current " : "";
if (!item.active) {
item.classes += "inactive "
}
item.domain = item.shading.material_domain === "surface" ? "surface" : "postpro";
item.is_material = true;
const name = item.name || kUntitledPlaceholder;
if (name in duplicatedLabels) {
const index = duplicatedLabels[name];
item.name = `${name} (${index})`;
duplicatedLabels[name] = index + 1;
} else {
item.name = name;
}
items.push(item);
}
// The template takes a flat list of items, so here we insert items for section headers using
// blank names, which causes them to sort to the top of their respective sections.
const sectionLabel = {"is_label": true, "name": ""};
items.push(Object.assign({"label": "Surface materials", "domain": "surface"}, sectionLabel));
items.push(Object.assign({"label": "PostProcess materials", "domain": "postpro"}, sectionLabel));
// Next, sort all materials and section headers.
items.sort((a, b) => {
if (a.domain > b.domain) return -1;
if (a.domain < b.domain) return +1;
if (a.name < b.name) return -1;
if (a.name > b.name) return +1;
return 0;
});
return items;
}
function renderMaterialList() {
const items = getDisplayedMaterials();
materialList.innerHTML = Mustache.render(matListTemplate.innerHTML, { "item": items } );
}
function updateClassList(array, indexProperty, selectedIndex) {
for (let item of array) {
const current = parseInt(item[indexProperty]) === selectedIndex;
item.classes = current ? "current " : "";
if (!item.active) {
item.classes += "inactive "
}
}
}
function renderMaterialDetail() {
const mat = gMaterialDatabase[gCurrentMaterial];
const ok = mat.matid === gCurrentShader.matid;
updateClassList(mat.opengl, "index", ok ? parseInt(gCurrentShader.glindex) : -1);
updateClassList(mat.vulkan, "index", ok ? parseInt(gCurrentShader.vkindex) : -1);
updateClassList(mat.metal, "index", ok ? parseInt(gCurrentShader.metalindex) : -1);
const item = Object.assign({}, mat);
if (item.shading.material_domain !== "surface") {
delete item.shading;
}
materialDetail.innerHTML = Mustache.render(matDetailTemplate.innerHTML, item);
}
function getShaderRecord(selection) {
const mat = gMaterialDatabase[gCurrentMaterial];
if (selection.glindex >= 0) return mat.opengl[parseInt(selection.glindex)];
if (selection.vkindex >= 0) return mat.vulkan[parseInt(selection.vkindex)];
if (selection.metalindex >= 0) return mat.metal[parseInt(selection.metalindex)];
return null;
}
function renderShaderStatus() {
const shader = getShaderRecord(gCurrentShader);
let statusString = "";
if (shader) {
const glsl = "glsl " + (gCurrentLanguage === "glsl" ? "active" : "");
const msl = "msl " + (gCurrentLanguage === "msl" ? "active" : "");
const spirv = "spirv " + (gCurrentLanguage === "spirv" ? "active" : "");
switch (getShaderAPI()) {
case "opengl":
statusString += ` &nbsp; <a class='status_button ${glsl}'>[GLSL]</a>`;
break;
case "metal":
statusString += ` &nbsp; <a class='status_button ${msl}'>[MSL]</a>`;
break;
case "vulkan":
statusString += ` &nbsp; <a class='status_button ${glsl}'>[GLSL]</a>`;
statusString += ` &nbsp; <a class='status_button ${spirv}'>[SPIRV]</a>`;
break;
}
if (shader.modified && gCurrentLanguage !== "spirv") {
statusString += " &nbsp; <a class='status_button rebuild'>[rebuild]</a>";
}
if (!shader.active) {
statusString += " &nbsp; <span class='warning'> selected variant is inactive </span>";
}
}
header.innerHTML = "matdbg" + statusString;
}
function selectShader(selection) {
const shader = getShaderRecord(selection);
if (!shader) {
console.error("Shader not yet available.")
return;
}
// Change the current language selection if necessary.
switch (getShaderAPI(selection)) {
case "opengl":
if (gCurrentLanguage !== "glsl") {
gCurrentLanguage = "glsl";
}
break;
case "vulkan":
if (gCurrentLanguage !== "spirv" && gCurrentLanguage !== "glsl") {
gCurrentLanguage = "spirv";
}
break;
case "metal":
if (gCurrentLanguage !== "msl") {
gCurrentLanguage = "msl";
}
break;
}
const showShaderSource = () => {
gCurrentShader = selection;
gCurrentShader.matid = gCurrentMaterial;
renderMaterialDetail();
gEditorIsLoading = true;
gEditor.setValue(shader[gCurrentLanguage]);
gEditorIsLoading = false;
shaderSource.style.visibility = "visible";
renderShaderStatus();
};
if (!shader[gCurrentLanguage]) {
const matInfo = gMaterialDatabase[gCurrentMaterial];
fetchShader(selection, matInfo, showShaderSource);
} else {
showShaderSource();
}
}
function onEdit(changes) {
if (gEditorIsLoading) {
return;
}
const shader = getShaderRecord(gCurrentShader);
if (!shader) {
return;
}
if (!shader.modified) {
shader.modified = true;
renderShaderStatus();
}
shader[gCurrentLanguage] = gEditor.getValue();
}
function selectMaterial(matid, selectFirstShader) {
gCurrentMaterial = matid;
renderMaterialList();
renderMaterialDetail();
if (selectFirstShader) {
const mat = gMaterialDatabase[gCurrentMaterial];
const selection = { matid };
if (mat.opengl.length > 0) selection.glindex = 0;
else if (mat.vulkan.length > 0) selection.vkindex = 0;
else if (mat.metal.length > 0) selection.metalindex = 0;
selectShader(selection);
}
}
function init() {
require(["vs/editor/editor.main"], function () {
const KeyMod = monaco.KeyMod, KeyCode = monaco.KeyCode;
gEditor = monaco.editor.create(shaderSource, {
value: "",
language: "cpp",
scrollBeyondLastLine: false,
readOnly: false,
minimap: { enabled: false }
});
gEditor.onDidChangeModelContent((e) => { onEdit(e.changes); });
gEditor.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, () => rebuildMaterial());
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.UpArrow, () => selectNextShader(-1, 0));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.DownArrow, () => selectNextShader(+1, 0));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.LeftArrow, () => selectNextShader(0, -1));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.RightArrow, () => selectNextShader(0, +1));
fetchMaterials();
});
Mustache.parse(matDetailTemplate.innerHTML);
Mustache.parse(matListTemplate.innerHTML);
statusLoop();
// Poll for active shaders once every second.
// Take care not to poll more frequently than the frame rate. Active variants are determined
// by the list of variants that were fetched between this query and the previous query.
setInterval(queryActiveShaders, 1000);
}
init();
-110
View File
@@ -1,110 +0,0 @@
html, body, .viewport {
width: 100%;
height: 100%;
margin: 0;
}
body {
font-family: 'Lexend Deca', sans-serif;
overflow: hidden;
}
a, a:visited {
text-decoration: none;
color: #567;
cursor: pointer;
}
a:hover, a.current {
font-weight: bold;
color: #07f;
}
a.status_button {
color: #e4e682;
}
a.status_button.active {
color: #fff;
}
span.warning {
color: black;
background: orange;
font-size: small;
}
a.inactive {
color: #aaa;
}
a.inactive.current {
color: #5af;
}
pre {
font-family: Menlo, Monaco, "Courier New", monospace;
}
.vbox {
display: flex;
flex-direction: column;
}
.scrollable {
position: relative;
overflow-y: scroll;
}
.scrollable > * {
position: absolute;
}
.squishy {
flex-grow: 1;
}
.stretchy {
flex-grow: 1;
}
.hbox {
display: flex;
flex-direction: row;
}
.space-between {
justify-content: space-between;
}
header, footer {
height: 26px;
background: cornflowerblue;
padding: 5px 0 0 5px;
}
.main {
flex: 1;
}
article {
flex: 5;
border-top: solid 2px;
border-bottom: solid 2px;
visibility: hidden;
}
nav {
border: solid 2px;
font-size: 12px;
flex: 1;
}
nav > *:first-child {
border-bottom: solid 2px;
}
nav > * {
padding-left: 5px;
padding-right: 5px;
}
+9 -9
View File
@@ -58,7 +58,7 @@ public:
/* compound assignment products by a scalar
*/
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
constexpr QUATERNION<T>& operator*=(U v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
@@ -67,7 +67,7 @@ public:
return lhs;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
constexpr QUATERNION<T>& operator/=(U v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
@@ -125,25 +125,25 @@ public:
* q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
*
*/
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(QUATERNION<T> q, U scalar) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
return q *= scalar;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(U scalar, QUATERNION<T> q) {
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(T scalar, QUATERNION<U> q) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
return q *= scalar;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator/(QUATERNION<T> q, U scalar) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q /= scalar);
return q /= scalar;
}
};
+31
View File
@@ -289,6 +289,14 @@ public:
return matrix::cof(m);
}
/*
* Returns a matrix representing the pose of a virtual camera looking towards -Z in its
* local Y-up coordinate system. "up" defines where the Y axis of the camera's local coordinate
* system is.
*/
template<typename A, typename B>
static TMat33 lookTo(const TVec3<A>& direction, const TVec3<B>& up) noexcept;
/**
* Packs the tangent frame represented by the specified matrix into a quaternion.
* Reflection is preserved by encoding it as the sign of the w component in the
@@ -406,6 +414,29 @@ constexpr TMat33<T>::TMat33(const TQuaternion<U>& q) noexcept : m_value{} {
m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy); // NOLINT
}
template<typename T>
constexpr T dot_tolerance() noexcept;
template<>
constexpr float dot_tolerance<float>() noexcept { return 0.999f; }
template<>
constexpr double dot_tolerance<double>() noexcept { return 0.9999; }
template<typename T>
template<typename A, typename B>
TMat33<T> TMat33<T>::lookTo(const TVec3<A>& direction, const TVec3<B>& up) noexcept {
auto const z_axis = direction;
auto norm_up = up;
if (std::abs(dot(z_axis, norm_up)) > dot_tolerance< arithmetic_result_t<A, B> >()) {
// Fix up vector if we're degenerate (looking straight up, basically)
norm_up = { norm_up.z, norm_up.x, norm_up.y };
}
auto const x_axis = normalize(cross(z_axis, norm_up));
auto const y_axis = cross(x_axis, z_axis);
return { x_axis, y_axis, -z_axis };
}
//------------------------------------------------------------------------------
template<typename T>
constexpr TQuaternion<T> TMat33<T>::packTangentFrame(const TMat33<T>& m, size_t storageSize) noexcept {
+16 -13
View File
@@ -285,6 +285,9 @@ public:
template<typename A, typename B, typename C>
static TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) noexcept;
template<typename A, typename B, typename C>
static TMat44 lookTo(const TVec3<A>& direction, const TVec3<B>& position, const TVec3<C>& up) noexcept;
template<typename A>
static constexpr TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) noexcept{
TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
@@ -517,19 +520,19 @@ template<typename T>
template<typename A, typename B, typename C>
TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center,
const TVec3<C>& up) noexcept {
TVec3<T> z_axis(normalize(center - eye));
TVec3<T> norm_up(normalize(up));
if (std::abs(dot(z_axis, norm_up)) > T(0.999)) {
// Fix up vector if we're degenerate (looking straight up, basically)
norm_up = { norm_up.z, norm_up.x, norm_up.y };
}
TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
TVec3<T> y_axis(cross(x_axis, z_axis));
return TMat44<T>(
TVec4<T>(x_axis, 0),
TVec4<T>(y_axis, 0),
TVec4<T>(-z_axis, 0),
TVec4<T>(eye, 1));
return lookTo(normalize(center - eye), eye, normalize(up));
}
template<typename T>
template<typename A, typename B, typename C>
TMat44<T> TMat44<T>::lookTo(const TVec3<A>& direction, const TVec3<B>& position,
const TVec3<C>& up) noexcept {
auto r = TMat33<T>::lookTo(direction, up);
return TMat44<T>{
TVec4<T>{ r[0], 0 },
TVec4<T>{ r[1], 0 },
TVec4<T>{ r[2], 0 },
TVec4<T>{ position, 1 } };
}
// ----------------------------------------------------------------------------------------
+65 -47
View File
@@ -32,6 +32,71 @@ class MatTest : public testing::Test {
protected:
};
//------------------------------------------------------------------------------
// A macro to help with vector comparisons within floating point range.
#define EXPECT_VEC_EQ(VEC1, VEC2) \
do { \
const decltype(VEC1) v1 = VEC1; \
const decltype(VEC2) v2 = VEC2; \
if (std::is_same<TypeParam,float>::value) { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_FLOAT_EQ(v1[i], v2[i]); \
} \
} else if (std::is_same<TypeParam,double>::value) { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
} \
} else { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_EQ(v1[i], v2[i]); \
} \
} \
} while(0)
//------------------------------------------------------------------------------
// A macro to help with vector comparisons within a range.
#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \
do { \
const decltype(VEC1) v1 = VEC1; \
const decltype(VEC2) v2 = VEC2; \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_NEAR(v1[i], v2[i], eps); \
} \
} while(0)
//------------------------------------------------------------------------------
// A macro to help with type comparisons within floating point range.
#define ASSERT_TYPE_EQ(T1, T2) \
do { \
const decltype(T1) t1 = T1; \
const decltype(T2) t2 = T2; \
if (std::is_same<TypeParam,float>::value) { \
ASSERT_FLOAT_EQ(t1, t2); \
} else if (std::is_same<TypeParam,double>::value) { \
ASSERT_DOUBLE_EQ(t1, t2); \
} else { \
ASSERT_EQ(t1, t2); \
} \
} while(0)
TEST_F(MatTest, LargeFloatRotationsWithOrthogonalization) {
double3 const t = { 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 };
mat4 const T = mat4::translation(t);
for (float d = 0; d < 90; d = d + 1.0) {
mat3f const R = mat3f::rotation(d * f::DEG_TO_RAD, float3{ 0, 1, 0 });
mat3 RR = orthogonalize(mat3{ R });
ASSERT_NEAR(dot(RR[0], RR[0]), 1.0, 1e-12);
ASSERT_NEAR(dot(RR[1], RR[1]), 1.0, 1e-12);
ASSERT_NEAR(dot(RR[2], RR[2]), 1.0, 1e-12);
mat4 M = mat4{ RR } * T;
double3 const t2 = transpose(M.upperLeft()) * M[3].xyz;
EXPECT_VEC_NEAR(t, t2, 0.0001); // 0.1mm
}
}
TEST_F(MatTest, ConstexprMat2) {
constexpr float a = F_PI;
constexpr mat2f M;
@@ -552,53 +617,6 @@ TYPED_TEST(MatTestT, Inverse2) {
TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
}
//------------------------------------------------------------------------------
// A macro to help with vector comparisons within floating point range.
#define EXPECT_VEC_EQ(VEC1, VEC2) \
do { \
const decltype(VEC1) v1 = VEC1; \
const decltype(VEC2) v2 = VEC2; \
if (std::is_same<TypeParam,float>::value) { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_FLOAT_EQ(v1[i], v2[i]); \
} \
} else if (std::is_same<TypeParam,double>::value) { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
} \
} else { \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_EQ(v1[i], v2[i]); \
} \
} \
} while(0)
//------------------------------------------------------------------------------
// A macro to help with vector comparisons within a range.
#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \
do { \
const decltype(VEC1) v1 = VEC1; \
const decltype(VEC2) v2 = VEC2; \
for (int i = 0; i < v1.size(); ++i) { \
EXPECT_NEAR(v1[i], v2[i], eps); \
} \
} while(0)
//------------------------------------------------------------------------------
// A macro to help with type comparisons within floating point range.
#define ASSERT_TYPE_EQ(T1, T2) \
do { \
const decltype(T1) t1 = T1; \
const decltype(T2) t2 = T2; \
if (std::is_same<TypeParam,float>::value) { \
ASSERT_FLOAT_EQ(t1, t2); \
} else if (std::is_same<TypeParam,double>::value) { \
ASSERT_DOUBLE_EQ(t1, t2); \
} else { \
ASSERT_EQ(t1, t2); \
} \
} while(0)
TYPED_TEST(MatTestT, NormalsNegativeScale) {
typedef filament::math::details::TMat33<TypeParam> M33T;
-101
View File
@@ -17,7 +17,6 @@
#include <math.h>
#include <random>
#include <functional>
#include <type_traits>
#include <gtest/gtest.h>
@@ -325,103 +324,3 @@ TEST_F(QuatTest, NaN) {
EXPECT_NEAR(qs[2], 0.5, 0.1);
EXPECT_NEAR(qs[3], 0.5, 0.1);
}
TEST_F(QuatTest, Conversions) {
quat qd;
quatf qf;
float3 vf;
double3 vd;
double d = 0.0;
float f = 0.0f;
static_assert(std::is_same<details::arithmetic_result_t<float, float>, float>::value);
static_assert(std::is_same<details::arithmetic_result_t<float, double>, double>::value);
static_assert(std::is_same<details::arithmetic_result_t<double, float>, double>::value);
static_assert(std::is_same<details::arithmetic_result_t<double, double>, double>::value);
{
auto r1 = qd * d;
auto r2 = qd * f;
auto r3 = qf * d;
auto r4 = qf * f;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = qd / d;
auto r2 = qd / f;
auto r3 = qf / d;
auto r4 = qf / f;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = d * qd;
auto r2 = f * qd;
auto r3 = d * qf;
auto r4 = f * qf;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = qd * vd;
auto r2 = qf * vd;
auto r3 = qd * vf;
auto r4 = qf * vf;
static_assert(std::is_same<decltype(r1), double3>::value);
static_assert(std::is_same<decltype(r2), double3>::value);
static_assert(std::is_same<decltype(r3), double3>::value);
static_assert(std::is_same<decltype(r4), float3>::value);
}
{
auto r1 = qd * qd;
auto r2 = qf * qd;
auto r3 = qd * qf;
auto r4 = qf * qf;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = dot(qd, qd);
auto r2 = dot(qf, qd);
auto r3 = dot(qd, qf);
auto r4 = dot(qf, qf);
static_assert(std::is_same<decltype(r1), double>::value);
static_assert(std::is_same<decltype(r2), double>::value);
static_assert(std::is_same<decltype(r3), double>::value);
static_assert(std::is_same<decltype(r4), float>::value);
}
{
auto r1 = cross(qd, qd);
auto r2 = cross(qf, qd);
auto r3 = cross(qd, qf);
auto r4 = cross(qf, qf);
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
}
template <typename L, typename R, typename = void>
struct has_divide_assign : std::false_type {};
template <typename L, typename R>
struct has_divide_assign<L, R,
decltype(std::declval<L&>() /= std::declval<R>(), void())> : std::true_type {};
// Static assertions to validate the availability of the /= operator for specific type
// combinations. The first static_assert checks that the quat does not have a /= operator with Foo.
// This ensures that quat does not provide an inappropriate overload that could be erroneously
// selected.
struct Foo {};
static_assert(!has_divide_assign<quat, Foo>::value);
static_assert(has_divide_assign<quat, float>::value);
+8 -3
View File
@@ -101,7 +101,7 @@ struct App {
bool actualSize = false;
bool originIsFarAway = false;
float originDistance = 6378137; // Earth's radius in [m]
float originDistance = 1.0f;
struct Scene {
Entity groundPlane;
@@ -762,7 +762,7 @@ int main(int argc, char** argv) {
ImGui::Checkbox("Camera at origin",
debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
ImGui::Checkbox("Far Origin", &app.originIsFarAway);
ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000);
ImGui::SliderFloat("Origin", &app.originDistance, 0, 1);
ImGui::Checkbox("Far uses shadow casters",
debug.getPropertyAddress<bool>("d.shadowmap.far_uses_shadowcasters"));
ImGui::Checkbox("Focus shadow casters",
@@ -981,7 +981,12 @@ int main(int argc, char** argv) {
tcm.setParent(tcm.getInstance(camera.getEntity()), root);
tcm.setParent(tcm.getInstance(app.asset->getRoot()), root);
tcm.setParent(tcm.getInstance(view->getFogEntity()), root);
tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? app.originDistance : 0.0f }));
// these values represent a point somewhere on Earth's surface
float const d = app.originIsFarAway ? app.originDistance : 0.0f;
// tcm.setTransform(root, mat4::translation(double3{ 67.0, -6366759.0, -21552.0 } * d));
tcm.setTransform(root, mat4::translation(
double3{ 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 } * d));
// Check if color grading has changed.
ColorGradingSettings const& options = app.viewer->getSettings().view.colorGrading;
+1
View File
@@ -122,6 +122,7 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
int main(int argc, char** argv) {
App app{};
app.config.title = "hellotriangle";
app.config.featureLevel = backend::FeatureLevel::FEATURE_LEVEL_0;
handleCommandLineArguments(argc, argv, &app);
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
+2 -1
View File
@@ -4,7 +4,8 @@ material {
color
],
shadingModel : unlit,
culling : none
culling : none,
featureLevel : 0
}
fragment {
+1 -1
View File
@@ -1,4 +1,4 @@
LAYOUT_LOCATION(LOCATION_POSITION) in vec4 position;
LAYOUT_LOCATION(LOCATION_POSITION) ATTRIBUTE vec4 position;
struct PostProcessVertexInputs {
+28 -15
View File
@@ -180,6 +180,19 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
}
}
const JsonishValue* transferFunctionValue = jsonObject.getValue("transferFunction");
if (transferFunctionValue) {
if (transferFunctionValue->getType() != JsonishValue::STRING) {
std::cerr << "parameters: transferFunction must be a STRING." << std::endl;
return false;
}
auto transferFunctionString = formatValue->toJsonString();
if (!Enums::isValid<SamplerTransferFunction>(transferFunctionString->getString())){
return logEnumIssue("parameters", *transferFunctionString, Enums::map<SamplerTransferFunction>());
}
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
@@ -205,22 +218,22 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
return false;
}
MaterialBuilder::SamplerType type = Enums::toEnum<SamplerType>(typeString);
if (precisionValue && formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format, precision);
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format);
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, precision);
} else {
builder.parameter(nameString.c_str(), type);
SamplerType type = Enums::toEnum<SamplerType>(typeString);
SamplerFormat format = SamplerFormat::FLOAT;
if (formatValue) {
format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
}
ParameterPrecision precision = ParameterPrecision::DEFAULT;
if (precisionValue) {
precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
}
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED;
if (transferFunctionValue) {
transferFunction =
Enums::toEnum<SamplerTransferFunction>(transferFunctionValue->toJsonString()->getString());
}
builder.parameter(nameString.c_str(), type, format, precision, transferFunction);
} else {
std::cerr << "parameters: the type '" << typeString
<< "' for parameter with name '" << nameString << "' is neither a valid uniform "
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.45.1",
"version": "1.45.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",