Compare commits

..

66 Commits

Author SHA1 Message Date
Benjamin Doherty
41a809368b Merge branch 'rc/1.14.0' into release 2021-11-15 10:07:56 -08:00
Romain Guy
ea53eb9290 Skip task incompatible with configuration caching (#4831) 2021-11-09 15:56:00 -08:00
Benjamin Doherty
05875057c9 Update RELEASE_NOTES for 1.14.0 2021-11-09 15:51:13 -08:00
Benjamin Doherty
44125926d1 Disable configuration-cache 2021-11-08 17:05:34 -08:00
Benjamin Doherty
60734349de Bump version to 1.14.0 2021-11-08 11:52:50 -08:00
Benjamin Doherty
fbfd5ec0ec Merge branch 'rc/1.13.0' into release 2021-11-08 11:50:19 -08:00
Benjamin Doherty
157c0264af Release Filament 1.13.0 2021-11-08 11:49:39 -08:00
Mathias Agopian
9bacfdb390 simplify shadowing code
- remove PCF "low" quality, we only use "HARD" now, when using PCF.
  Higher quality levels are achieved by using VSM.

- added a version of PCF that doesn't use a shadowSampler for future
  use.
2021-11-04 23:01:08 -07:00
Mathias Agopian
f9aaf5c42e Fix normal bias for spotlights.
The normal bias is now computed correctly, this requires to compute
the z in lightspace in the shader.
Note that this would not work as well if we used LISPSM, but we'll
cross that bridge when we get there.
2021-11-04 14:56:00 -07:00
Ben Doherty
a74a95cc65 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-11-04 13:28:17 -07:00
Ben Doherty
21db695e79 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-11-04 13:28:08 -07:00
Mathias Agopian
cf917f1093 Add a (crude) way to have structs in our UBOs
The struct must be declared in common_type.fs, so custom
structures are not supported.
2021-11-04 12:43:50 -07:00
Ben Doherty
a8d3a61c25 Enforce readPixels is called within frame (#4802) 2021-11-03 10:59:25 -07:00
Mathias Agopian
80f13a8149 fix typo in comments 2021-11-03 10:54:05 -07:00
Mathias Agopian
e172f3a67f Fix java shadow biases + minor cleanups
- constant bias and normal bias default values in java didn't match
  C++ or the documentation

- stable shadows were enabled by default in java

- polygon offset biases were missing from the java API

- document and don't apply polygon offset to VSM

- remove unused code
2021-11-03 00:20:48 -07:00
Mathias Agopian
c382c0a9cc Tighten spotlights near/far further
We now cull the shadow casters before computing the near/far plane
for spotlights -- we can do that because we know the light's frustum.
So only these casters that contribute to the shadow are accounted for
when calculating the near/far plane.

This PR also include more cleanup and simplifications.
2021-11-02 14:05:09 -07:00
Benjamin Doherty
bc0ea16ff0 Update RELEASE_NOTES for 1.13.0 2021-11-02 13:15:53 -07:00
Mathias Agopian
b2dc8aa84c Fix typo that broke the directional shadowmap 2021-11-02 13:11:44 -07:00
Mathias Agopian
78b29fe967 Fix typo that broke the directional shadowmap 2021-11-01 16:41:30 -07:00
Benjamin Doherty
9987e8b6ab Bump version to 1.13.0 2021-11-01 14:59:11 -07:00
Benjamin Doherty
0d9bdcc008 Merge branch 'rc/1.12.11' into release 2021-11-01 14:55:58 -07:00
Benjamin Doherty
81fa33abeb Release Filament 1.12.11 2021-11-01 14:55:54 -07:00
Mathias Agopian
af7f87e19b make sure the near plane for spot shadows is not negative 2021-11-01 14:19:13 -07:00
Mathias Agopian
3073b03d56 Improve computation of spotlight near/far plane
Near/far plane is now computed dynamically, this significantly
improves the shadowmap depth resolution.

Quite a bit of refactoring was necessary to get all the right data 
in the right place, in particular SceneInfo is now passed around for
both directional and spot shadows.
2021-10-29 18:57:26 -07:00
Mathias Agopian
23dab0a013 Fix very narrow spotlight lighting and other minor issues
The froxel code was failing when a spotlight cone was too narrow. This
is fixed here by never using a cone smaller than 0.5 degrees during
forxelization. 

Additionally we now silently clamp the cone angles to 0.5 degrees at
the API level because, the falloff was also failing due to floating
point precision in the shader.

Finally, we clamp the inner cone upper value to the outer cone's
instead of the other way around. i.e. the outer cone defines the 
spotlight while the inner cone just controls the falloff.
2021-10-29 15:08:05 -07:00
Mathias Agopian
fd68144ba2 fix depth precision with VSM
When calculating the linear depth for VSM, we were using the whole
range between 0 and the far plane, the near plane wasn't taken into
account.

This can be a problem is the light is very far, but it's near plane is
closer to the camera/scene, in this case the depth precision wasn't
used optimally.

Note that we don't hit this problem currently, because the directional
light is constructed such that its origin is at the near plane, and the
spotlights have a fixed near plane (which is a problem and will be 
fixed at a later time).
2021-10-29 12:03:41 -07:00
Mathias Agopian
51e92cd142 Fix spotlight direction and falloff
Direction and falloff were recently changed to fp16 int the shader,
which is not enough (far from it) when a spotlight is over 100m away.
2021-10-28 21:59:01 -07:00
Mathias Agopian
50f33f7196 fix some warnings and typos 2021-10-28 21:58:45 -07:00
Benjamin Doherty
b5c634045e Update RELEASE_NOTES for 1.12.11 2021-10-28 16:13:50 -07:00
Ben Doherty
1f05531d53 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-10-28 16:02:23 -07:00
Benjamin Doherty
88f382f0e3 Revert "refactor colorgrading materials"
This reverts commit fb86a77cf8.
2021-10-28 15:56:50 -07:00
Benjamin Doherty
3e59925900 Remove problematic configuration-cache setting for release build 2021-10-28 15:56:26 -07:00
Mathias Agopian
1182d30fb9 MSAA VSMs seem to now work on Qualcomm h/w
The bug that prevented them to work has been worked around, but we 
don't know when or how. The original bug still exists as demonstrated
by our standalone test apk.

For now, we reenable MSAA VSM on Adreno.
2021-10-26 16:06:20 -07:00
Romain Guy
095179eeb3 Enabled Gradle configuration caching and rename properties (#4769)
* Fix configurationg caching problems

* Enable Gradle configuration caching

* Update BUILDING.md

* Update RELEASE_NOTES.md

Co-authored-by: Mathias Agopian <mathias@google.com>

Co-authored-by: Benjamin Doherty <bendoherty@google.com>
2021-10-26 12:47:35 -07:00
Romain Guy
69b48eaadb Fix erroenous warning 2021-10-26 12:05:49 -07:00
Timo Röhling
4031c6f0d3 Simplify devendoring (#4765)
* Perform sanity check when combining static libraries

This is a small modification that simplifies Debian packaging when one
or more vendored dependencies are replaced by system shared libraries
and no longer need to be combined with a Filament library.

* Verify vendoring before including license texts

This is another small modification to simplify Debian packaging when one
or more vendored dependencies are removed and replaced by system
libraries, so they no longer need to be considered when compiling the
license texts.
2021-10-26 11:31:35 -07:00
Mathias Agopian
7a22f7dfc5 move some VSM computations into the vertex shader 2021-10-26 11:26:21 -07:00
Mathias Agopian
728ebf5023 cleanup shadowing shader code
- remove PCF MEDIUM and HIGH quality -- these were never used in
practice, and "quality" really meant "blurriness", now we have VSM
for doing that with much more control and more efficiently.

PCF_HARD is now the default. PCF_LOW is still available at compile time
for now. It shouldn't be needed though because VSM can achieve the same.

Moved the divide-by-w of light space into the shadow() method -- which
known if it should be done or not.
2021-10-26 11:26:21 -07:00
Timo Röhling
62476d2f06 Fix compatibility with libstdc++ (#4763)
The codebase needs only a few additional standard #includes to compile
against libstdc++ on Linux; presumably those headers are implicitly
included with MSVC's and Clang's standard C++ library, but even if
libstdc++ compatibility is not a goal, it is advisable to have them
included directly.
2021-10-26 11:07:39 -07:00
Benjamin Doherty
ea404f8d4f Remove problematic configuration-cache setting for release build 2021-10-26 10:09:51 -07:00
Benjamin Doherty
602a550d93 Bump version to 1.12.11 2021-10-25 12:30:37 -07:00
Benjamin Doherty
12abbe2d23 Merge branch 'rc/1.12.10' into release 2021-10-25 11:06:18 -07:00
Benjamin Doherty
5fea428243 Release Filament 1.12.10 2021-10-25 11:06:12 -07:00
Benjamin Doherty
fb0ee97588 Update RELEASE_NOTES for 1.12.10 2021-10-25 11:00:06 -07:00
Ben Doherty
56ef48c9c3 Fix, call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (#4749) 2021-10-25 10:39:24 -07:00
Benjamin Doherty
47c3dd3dd1 Revert "refactor colorgrading materials"
This reverts commit fb86a77cf8.
2021-10-25 10:37:52 -07:00
clayly
9a3c9ccbf3 android-samples-gradle-plugin-id (#4740) 2021-10-24 11:12:15 -07:00
Ben Doherty
ef4dfcecd6 Metal: support framebuffer fetch on M1 devices (#4735) 2021-10-21 11:27:05 -07:00
Ben Doherty
5943382d23 iOS: avoid precision issues with CACurrentMediaTime (#4753) 2021-10-21 11:13:51 -07:00
Benjamin Doherty
c181648bfa Bump version to 1.12.10 2021-10-20 12:15:51 -07:00
Benjamin Doherty
1dcf347b87 Release Filament 1.12.9 2021-10-20 12:11:46 -07:00
Ben Doherty
47714007f7 Fix VirtualMachineEnv.cpp with older JNI versions (#4752) 2021-10-20 11:18:03 -07:00
Mathias Agopian
54277572d2 code health: cleanup FrameInfo and FrameSkipper a bit
in particular add a terminate() method to FrameSkipper instead of
destroying driver objects in the destructor -- this matches all other
objects.

also remove dependency on FEngine which wasn't needed.
2021-10-18 18:41:59 -07:00
Mathias Agopian
52f2c0e107 don't use static initialization when not needed
static init/destruction is always dangerous
2021-10-18 15:36:17 -07:00
Mathias Agopian
59abc8cc20 use NEAREST filtering for the final blit
the final blit, when used, is never upscaling, so always use the 
NEAREST filter.
2021-10-18 12:18:57 -07:00
Mathias Agopian
e15796b348 Rewrite controller for dynamic resolution scaling
- move the control code out of FrameInfo and into
  View itself. FrameInfo now just gathers information about the 
  frames instead of also doing part of the control.

- the control code has been refactored into a more formal PIDController.

- a few bugs were fixed in the control loop and default parameters
  tuned. 


The control loop itself now outputs a relative scale factor instead
of an absolute one. The relative scale factor seems easier to
control and is less jittery, and doesn't have to rely on the "integral"
term of the PID.

It's possible that more tuning is needed, but the scaling is now 
more stable.
2021-10-18 10:06:44 -07:00
Mathias Agopian
1711eaa4d6 Minor improvements to DebugRegistry
- remove getProperties() we'll put it back if we need it some day

- use unordered_map because it generates less code than robin_map and
  here performance is not critical
2021-10-18 10:06:44 -07:00
Mathias Agopian
acbd6a5ca8 Improve dynamic resolution scaling
- defaults to 31 history points instead of 3, this gives a much better 
  estimate of the frame rate.

- make sure the calculations work for any definition of our internal
  duration<> (it's useful to use milliseconds when dealing with
  refresh periods).

- don't round to 8 pixels if the scale factor is exactly 1

- fix a few comments and warnings
2021-10-18 10:06:44 -07:00
Romain Guy
775090fdda Re-fix tests 2021-10-15 17:30:06 -07:00
Romain Guy
0915b86927 Fix tests 2021-10-15 16:23:45 -07:00
BStringhamVRSK
c10f7b01f4 Fixes normal map issues in mipgen and exposes a couple of SurfaceOrientation functions for Web
* mipgen: Linearized PNG bitmaps (such as normal maps) no longer perform gamma transform on read.

* mipgen: Using "-k normals" argument now works for all file types, not just KTX

* Web: Exposed SurfaceOrientation functions getQuatsHalf4() and getQuatsFloat()
2021-10-15 14:23:41 -07:00
Mathias Agopian
84142ac506 move platform related files into a platforms directory
this is just to improve source code readability.
2021-10-14 22:48:16 -07:00
Mathias Agopian
ff190847a1 ExternalStreamManagerAndroid actually depends on GLES
Since ExternalStreamManagerAndroid depends on GLES, move it to the 
opengl directory.
2021-10-14 22:48:16 -07:00
Mathias Agopian
3ceec28189 added a way to check for adreno driver version
not used yet, but good too have.
2021-10-14 19:35:17 -07:00
Romain Guy
481038152f Cleanup of Android projects (#4730) 2021-10-13 18:17:12 -07:00
Rupert Rawnsley
60bd72730d ASTC texture enum missing in JNI interface 2021-10-13 10:18:54 -07:00
128 changed files with 1855 additions and 1181 deletions

View File

@@ -291,11 +291,11 @@ Alternatively you can build the AAR from the command line by executing the follo
`android/` directory:
```
$ ./gradlew -Pfilament_dist_dir=../../out/android-release/filament assembleRelease
$ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease
```
The `-Pfilament_dist_dir` can be used to specify a different installation directory (it must match
the CMake install prefix used in the previous steps).
The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation
directory (it must match the CMake install prefix used in the previous steps).
#### Using Filament's AAR

View File

@@ -502,11 +502,15 @@ function(list_licenses OUTPUT MODULES)
foreach(module ${_MODULES})
set(license_path "../../third_party/${module}/LICENSE")
get_filename_component(fullname "${license_path}" ABSOLUTE)
string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n")
file(READ ${license_path} license_long)
string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long})
string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},)
string(APPEND CONTENT "\n\n")
if(EXISTS ${fullname})
string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n")
file(READ ${license_path} license_long)
string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long})
string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},)
string(APPEND CONTENT "\n\n")
else()
message(AUTHOR_WARNING "${license_path} not found. You can ignore this warning if you have devendored ${module}.")
endif()
endforeach()
configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT})
endfunction(list_licenses)
@@ -522,7 +526,12 @@ function(combine_static_libs TARGET OUTPUT DEPS)
# Loop through the dependent libraries and query their location on disk.
set(DEPS_FILES )
foreach(DEPENDENCY ${DEPS})
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
if(TARGET ${DEPENDENCY})
get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE)
if(dep_type STREQUAL "STATIC_LIBRARY")
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
endif()
endif()
endforeach()
add_custom_command(

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.12.9'
implementation 'com.google.android.filament:filament-android:1.14.0'
}
```
@@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.12.9'
pod 'Filament', '~> 1.14.0'
```
### Snapshots

View File

@@ -3,7 +3,40 @@
This file contains one line summaries of commits that are worthy of mentioning in release notes.
A new header is inserted each time a *tag* is created.
## v1.12.9 (currently main branch)
## v1.14.1 (currently main branch)
## v1.14.0
- engine: Internal materials can use structures as parameters [⚠️ **Material breakage**].
- engine: `readPixels` on a `SwapChain` must be called within `beginFrame` / `endFrame` [⚠️ **API
Change**].
- engine: Fix normal bias and improve spotlight quality.
- Java: Fix shadow biases.
## v1.13.0
- Android: Gradle configuration caching is now enabled.
- Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx`
where `xxx` is the property name. See `android/build.gradle` for a complete list [⚠️]
- Android: The Gradle property `filament_tools_dir` (now called
`com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify one
in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️]
- engine: Fix spotlights direction and falloff [⚠️ **Material breakage**].
- engine: Improvements to VSM and spotlight shadows.
## v1.12.11
- Metal: Color grading performance improvement on M1 devices.
- samples: Fix glitchy animation seen in gltf-viewer iOS sample.
## v1.12.10
- engine: rewrite dynamic resolution scaling controller for better accuracy and less jittering.
- Java: fix missing ASTC texture enum.
- tools: Fix normal map issues in mipgen.
- WebGL: expose some `SurfaceOrientation` functions.
## v1.12.9
- engine: New API: `MultiSampleAntiAliasingOptions` and HDR-aware MSAA resolve. When `customResolve`
is enabled, improves anti-aliasing quality [**NEW API**].

View File

@@ -122,20 +122,20 @@ The Gradle project used to generate the AAR is located at `<filament>\android`.
```
cd android
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease
copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
```
If you're only interested in building for a single ABI, you'll need to pass a `filament_abis` parameter:
If you're only interested in building for a single ABI, you'll need to pass a `com.google.android.filament.abis` parameter:
```
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_abis=x86
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pcom.google.android.filament.abis=x86
```
If you're only interested in building SDK, you may skip samples build by passing a `filament_skip_samples` flag:
If you're only interested in building SDK, you may skip samples build by passing a `com.google.android.filament.skip-samples` flag:
```
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples
```

View File

@@ -1,26 +1,30 @@
import java.nio.file.Paths
// This script accepts the following parameters:
//
// filament_dist_dir
// com.google.android.filament.dist-dir
// Path to the Filament distribution/install directory for Android
// (produced by make/ninja install). This directory must contain lib/arm64-v8a/ etc.
//
// filament_tools_dir
// com.google.android.filament.tools-dir
// Path to the Filament distribution/install directory for desktop.
// This directory must contain bin/matc.
//
// filament_exclude_vulkan
// com.google.android.filament.exclude-vulkan
// When set, support for Vulkan will be excluded.
//
// filament_skip_samples
// com.google.android.filament.skip-samples
// Exclude samples from the project. Useful to speed up compilation.
//
// filament_abis
// com.google.android.filament.abis
// List of supported ABIs to build as a comma separated list. Available options are:
// arm64-v8a, armeabi-v7a, x86_64, x86, all
// Defaults to all.
//
// Example:
// ./gradlew -Pfilament_dist_dir=../dist-android-release assembleRelease -Pfilament_abis=x86
// ./gradlew -Pcom.google.android.filament.dist-dir=../dist-android-release \
// -Pcom.google.android.filament.abis=x86 \
// assembleRelease
// Publishing to Maven Central:
// - Build and upload artifacts with ./gradlew publish
@@ -40,44 +44,52 @@
//
buildscript {
def filamentPath = file("../out/android-release/filament").absolutePath
if (project.hasProperty("filament_dist_dir")) {
filamentPath = file(project.property("filament_dist_dir")).absolutePath
}
def path = providers
.gradleProperty("com.google.android.filament.dist-dir")
.forUseAtConfigurationTime().get()
def directory = objects.fileProperty().fileValue(new File(path)).getAsFile().get()
def filamentPath = directory.absolutePath
// Our CMake scripts require a forward-slash path for the FILAMENT_DIST_DIR
// variable, so here we convert the native path to a forward-slash path.
filamentPath = filamentPath.replace(File.separator, '/')
// Warning: changing this property does not work well with incremental builds.
def excludeVulkan = project.hasProperty("filament_exclude_vulkan")
def excludeVulkan = providers
.gradleProperty("com.google.android.filament.exclude-vulkan")
.forUseAtConfigurationTime()
.isPresent()
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
if (project.hasProperty("filament_abis")) {
def newAbis = project.property("filament_abis").split(',')
if (!newAbis.contains("all")) {
abis = newAbis
}
def newAbis = providers
.gradleProperty("com.google.android.filament.abis")
.forUseAtConfigurationTime()
.get()
.split(',')
if (!newAbis.contains("all")) {
abis = newAbis
}
ext.versions = [
'minSdk': 19,
'targetSdk': 30,
'compileSdk': 30,
'kotlin': '1.5.30',
'kotlin': '1.5.31',
'buildTools': '30.0.3',
'ndk': '22.1.7171670'
]
ext.deps = [
'androidx': [
'annotations': "androidx.annotation:annotation:1.1.0",
'annotations': "androidx.annotation:annotation:1.3.0",
'core': "androidx.core:core:1.3.0",
],
'kotlin': "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
]
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
@@ -118,7 +130,7 @@ buildscript {
}
plugins {
id 'io.codearte.nexus-staging' version '0.22.0'
id 'io.codearte.nexus-staging' version '0.30.0'
}
// Nexus Staging configuration
@@ -187,7 +199,8 @@ subprojects {
gradle.taskGraph.whenReady {
gradle.taskGraph.allTasks.each {
it.onlyIf {
!it.project.ext.has('isSample') || !project.hasProperty('filament_skip_samples')
!it.project.ext.has('isSample') ||
!project.hasProperty('com.google.android.filament.skip-samples')
}
}
}

View File

@@ -1,3 +1,17 @@
plugins {
id 'groovy-gradle-plugin'
}
gradlePlugin {
plugins {
create("filament-tools-plugin") {
id = "filament-tools-plugin"
implementationClass = "FilamentToolsPlugin"
}
}
}
repositories {
mavenCentral()
}

View File

@@ -1,20 +1,22 @@
// This plugin accepts the following parameters:
//
// filament_tools_dir
// com.google.android.filament.tools-dir
// Path to the Filament distribution/install directory for desktop.
// This directory must contain bin/matc.
//
// filament_exclude_vulkan
// com.google.android.filament.exclude-vulkan
// When set, support for Vulkan will be excluded.
//
// Example:
// ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
// ./gradlew -Pcom.google.android.filament.tools-dir=../../dist-release assembleDebug
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.FileType
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.LogLevel
@@ -27,14 +29,18 @@ import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.incremental.InputFileDetails
import org.gradle.api.model.ObjectFactory
import org.gradle.internal.os.OperatingSystem
import org.gradle.process.ExecOperations
import org.gradle.work.ChangeType
import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import java.nio.file.Paths
class TaskWithBinary extends DefaultTask {
import javax.inject.Inject
abstract class TaskWithBinary extends DefaultTask {
private final String binaryName
private Property<String> binaryPath = null
@@ -42,15 +48,23 @@ class TaskWithBinary extends DefaultTask {
binaryName = name
}
@Inject abstract ObjectFactory getObjects()
@Inject abstract ProviderFactory getProviders()
@Input
Property<String> getBinary() {
if (binaryPath == null) {
def tool = ["/bin/${binaryName}.exe", "/bin/${binaryName}"]
def fullPath = tool.collect { path ->
Paths.get(project.ext.filamentToolsPath.absolutePath, path).toFile()
def filamentToolsPath = providers
.gradleProperty("com.google.android.filament.tools-dir")
.forUseAtConfigurationTime().get()
def directory = objects.fileProperty()
.fileValue(new File(filamentToolsPath)).getAsFile().get()
Paths.get(directory.absolutePath, path).toFile()
}
binaryPath = project.objects.property(String.class)
binaryPath = objects.property(String.class)
binaryPath.set(
(OperatingSystem.current().isWindows() ? fullPath[0] : fullPath[1]).toString())
}
@@ -84,6 +98,11 @@ abstract class MaterialCompiler extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
@Inject abstract ObjectFactory getObjects()
@Inject abstract ProviderFactory getProviders()
MaterialCompiler() {
super("matc")
}
@@ -91,7 +110,9 @@ abstract class MaterialCompiler extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamat' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*.filamat' })
})
}
inputs.getFileChanges(inputDir).each { InputFileDetails change ->
@@ -115,12 +136,15 @@ abstract class MaterialCompiler extends TaskWithBinary {
}
def matcArgs = []
if (!project.hasProperty("filament_exclude_vulkan")) {
def exclude_vulkan = providers
.gradleProperty("com.google.android.filament.exclude-vulkan")
.forUseAtConfigurationTime().present
if (!exclude_vulkan) {
matcArgs += ['-a', 'vulkan']
}
matcArgs += ['-a', 'opengl', '-p', 'mobile', '-o', getOutputFile(file), file]
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -149,6 +173,10 @@ abstract class IblGenerator extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
@Inject abstract ObjectFactory getObjects()
IblGenerator() {
super("cmgen")
}
@@ -156,7 +184,9 @@ abstract class IblGenerator extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*' })
})
}
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
@@ -188,7 +218,7 @@ abstract class IblGenerator extends TaskWithBinary {
}
commandArgs = commandArgs + " " + file
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -213,6 +243,9 @@ abstract class MeshCompiler extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
MeshCompiler() {
super("filamesh")
}
@@ -220,7 +253,9 @@ abstract class MeshCompiler extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamesh' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*.filamesh' })
})
}
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
@@ -243,7 +278,7 @@ abstract class MeshCompiler extends TaskWithBinary {
" Ensure Filament has been built/installed before building this app.")
}
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -280,11 +315,6 @@ class FilamentToolsPlugin implements Plugin<Project> {
extension.meshInputFile = project.objects.fileProperty()
extension.meshOutputDir = project.objects.directoryProperty()
project.ext.filamentToolsPath = project.file("../../../out/release/filament")
if (project.hasProperty("filament_tools_dir")) {
project.ext.filamentToolsPath = project.file(project.property("filament_tools_dir"))
}
project.tasks.register("filamentCompileMaterials", MaterialCompiler) {
enabled =
extension.materialInputDir.isPresent() &&

View File

@@ -18,21 +18,15 @@
#include "private/backend/VirtualMachineEnv.h"
namespace filament {
extern jint JNI_OnLoad(JavaVM* vm, void* reserved);
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
#if ANDROID
::filament::JNI_OnLoad(vm, reserved);
#else
// This must be called when the library is loaded. We need this to get a reference to the
// global VM
::filament::VirtualMachineEnv::JNI_OnLoad(vm);
#endif
return JNI_VERSION_1_6;
}

View File

@@ -76,7 +76,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass,
jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions,
jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint,
jfloat shadowFarHint, jboolean stable, jboolean screenSpaceContactShadows, jint stepCount,
jfloat shadowFarHint, jboolean stable,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
@@ -88,6 +90,8 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.shadowNearHint = shadowNearHint,
.shadowFarHint = shadowFarHint,
.stable = (bool)stable,
.polygonOffsetConstant = polygonOffsetConstant,
.polygonOffsetSlope = polygonOffsetConstant,
.screenSpaceContactShadows = (bool)screenSpaceContactShadows,
.stepCount = uint8_t(stepCount),
.maxShadowDistance = maxShadowDistance,

View File

@@ -244,13 +244,13 @@ public class LightManager {
* light. 1mm by default.
* This is ignored when the View's ShadowType is set to VSM.
*/
public float constantBias = 0.05f;
public float constantBias = 0.001f;
/** Amount by which the maximum sampling error is scaled. The resulting value is used
* to move the shadow away from the fragment normal. Should be 1.0.
* This is ignored when the View's ShadowType is set to VSM.
*/
public float normalBias = 0.4f;
public float normalBias = 1.0f;
/** Distance from the camera after which shadows are clipped. This is used to clip
* shadows that are too far and wouldn't contribute to the scene much, improving
@@ -279,7 +279,24 @@ public class LightManager {
* When set to true, all resolution enhancing features that can affect stability are
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
*/
public boolean stable = true;
public boolean stable = false;
/**
* Constant bias in depth-resolution units by which shadows are moved away from the
* light. The default value of 0.5 is used to round depth values up.
* Generally this value shouldn't be changed or at least be small and positive.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetConstant = 0.5f;
/**
* Bias based on the change in depth in depth-resolution units by which shadows are moved
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
* Generally this value is between 0.5 and the size in texel of the PCF filter.
* Setting this value correctly is essential for LISPSM shadow-maps.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetSlope = 2.0f;
/**
* Whether screen-space contact shadows are used. This applies regardless of whether a
@@ -471,7 +488,9 @@ public class LightManager {
nBuilderShadowOptions(mNativeBuilder,
options.mapSize, options.shadowCascades, options.cascadeSplitPositions,
options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint,
options.shadowFarHint, options.stable, options.screenSpaceContactShadows,
options.shadowFarHint, options.stable,
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
options.blurWidth);
return this;
@@ -1131,7 +1150,7 @@ public class LightManager {
private static native void nDestroyBuilder(long nativeBuilder);
private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity);
private static native void nBuilderCastShadows(long nativeBuilder, boolean enable);
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);

View File

@@ -107,12 +107,12 @@ public class Renderer {
/**
* Rate at which the scale will change to reach the target frame rate.
*/
public float scaleRate = 0.125f;
public float scaleRate = 1.0f / 15.0f;
/**
* History size. higher values, tend to filter more (clamped to 30).
* History size. higher values, tend to filter more (clamped to 31).
*/
public int history = 9;
public int history = 15;
}
/**
@@ -437,8 +437,9 @@ public class Renderer {
*</pre>
*
*
* <p>Typically <code>readPixels</code> will be called after {@link #render} and before
* {@link #endFrame}.</p>
* <p><code>readPixels</code> must be called within a frame, meaning after {@link #beginFrame}
* and before {@link #endFrame}. Typically, <code>readPixels</code> will be called after
* {@link #render}.</p>
* <br>
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.

View File

@@ -216,7 +216,38 @@ public class Texture {
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8,
// Available everywhere except Android/iOS
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA,
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA,
// ASTC formats are available with a GLES extension
RGBA_ASTC_4x4,
RGBA_ASTC_5x4,
RGBA_ASTC_5x5,
RGBA_ASTC_6x5,
RGBA_ASTC_6x6,
RGBA_ASTC_8x5,
RGBA_ASTC_8x6,
RGBA_ASTC_8x8,
RGBA_ASTC_10x5,
RGBA_ASTC_10x6,
RGBA_ASTC_10x8,
RGBA_ASTC_10x10,
RGBA_ASTC_12x10,
RGBA_ASTC_12x12,
SRGB8_ALPHA8_ASTC_4x4,
SRGB8_ALPHA8_ASTC_5x4,
SRGB8_ALPHA8_ASTC_5x5,
SRGB8_ALPHA8_ASTC_6x5,
SRGB8_ALPHA8_ASTC_6x6,
SRGB8_ALPHA8_ASTC_8x5,
SRGB8_ALPHA8_ASTC_8x6,
SRGB8_ALPHA8_ASTC_8x8,
SRGB8_ALPHA8_ASTC_10x5,
SRGB8_ALPHA8_ASTC_10x6,
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12
}
/**
@@ -231,7 +262,38 @@ public class Texture {
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8,
// Available everywhere except Android/iOS
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA,
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA,
// ASTC formats are available with a GLES extension
RGBA_ASTC_4x4,
RGBA_ASTC_5x4,
RGBA_ASTC_5x5,
RGBA_ASTC_6x5,
RGBA_ASTC_6x6,
RGBA_ASTC_8x5,
RGBA_ASTC_8x6,
RGBA_ASTC_8x8,
RGBA_ASTC_10x5,
RGBA_ASTC_10x6,
RGBA_ASTC_10x8,
RGBA_ASTC_10x10,
RGBA_ASTC_12x10,
RGBA_ASTC_12x12,
SRGB8_ALPHA8_ASTC_4x4,
SRGB8_ALPHA8_ASTC_5x4,
SRGB8_ALPHA8_ASTC_5x5,
SRGB8_ALPHA8_ASTC_6x5,
SRGB8_ALPHA8_ASTC_6x6,
SRGB8_ALPHA8_ASTC_8x5,
SRGB8_ALPHA8_ASTC_8x6,
SRGB8_ALPHA8_ASTC_8x8,
SRGB8_ALPHA8_ASTC_10x5,
SRGB8_ALPHA8_ASTC_10x6,
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12
}
/**

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.12.9
VERSION_NAME=1.14.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -18,3 +18,9 @@ POM_DEVELOPER_NAME=Filament Team
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
org.gradle.unsafe.configuration-cache=true
com.google.android.filament.tools-dir=../../../out/release/filament
com.google.android.filament.dist-dir=../out/android-release/filament
com.google.android.filament.abis=all

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -30,6 +32,11 @@ android {
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
}
dependencies {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -17,10 +19,16 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.hellocamera"
minSdkVersion versions.minSdk
minSdkVersion 23
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -33,7 +33,9 @@ import android.graphics.ImageFormat
import android.hardware.HardwareBuffer
import android.media.ImageReader
import android.opengl.Matrix
import android.view.Display
import android.os.Build
import android.os.Looper
import androidx.annotation.RequiresApi
import com.google.android.filament.*
@@ -44,7 +46,7 @@ import java.util.concurrent.TimeUnit
* Toy class that handles all interaction with the Android camera2 API.
* Sets the "textureTransform" and "videoTexture" parameters on the given Filament material.
*/
class CameraHelper(val activity: Activity, private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance, private val display: Display) {
class CameraHelper(val activity: Activity, private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance) {
private lateinit var cameraId: String
private lateinit var captureRequest: CaptureRequest
@@ -63,6 +65,20 @@ class CameraHelper(val activity: Activity, private val filamentEngine: Engine, p
kImageReaderMaxImages,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
@Suppress("deprecation")
private val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(activity)
} else {
activity.windowManager.defaultDisplay!!
}
@RequiresApi(30)
class Api30Impl {
companion object {
fun getDisplay(context: Context) = context.display!!
}
}
private val cameraCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
@@ -87,7 +103,9 @@ class CameraHelper(val activity: Activity, private val filamentEngine: Engine, p
val stream = filamentStream
if (stream != null) {
imageReader.acquireLatestImage()?.also {
stream.setAcquiredImage(it.hardwareBuffer, Handler()) { it.close() }
stream.setAcquiredImage(it.hardwareBuffer, Handler(Looper.getMainLooper())) {
it.close()
}
}
}
}

View File

@@ -94,7 +94,7 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
setupView()
setupScene()
cameraHelper = CameraHelper(this, engine, materialInstance, windowManager.defaultDisplay)
cameraHelper = CameraHelper(this, engine, materialInstance)
cameraHelper.openCamera()
}

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -21,6 +23,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -27,6 +29,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -20,6 +22,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -10,9 +10,16 @@ android {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
}
dependencies {
implementation deps.kotlin
implementation deps.androidx.annotations
implementation project(':filament-android')
}

View File

@@ -18,19 +18,21 @@ package com.google.android.filament.livewallpaper
import android.animation.ValueAnimator
import android.app.Service
import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.os.Build
import android.service.wallpaper.WallpaperService
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceHolder
import android.view.WindowManager
import android.view.animation.LinearInterpolator
import androidx.annotation.RequiresApi
import com.google.android.filament.*
import com.google.android.filament.android.DisplayHelper
import com.google.android.filament.android.UiHelper
class FilamentLiveWallpaper : WallpaperService() {
// Make sure to initialize Filament first
// This loads the JNI library needed by most API calls
@@ -196,9 +198,14 @@ class FilamentLiveWallpaper : WallpaperService() {
override fun onNativeWindowChanged(surface: Surface) {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
val display =
(application.getSystemService(Service.WINDOW_SERVICE) as WindowManager)
.defaultDisplay
@Suppress("deprecation")
val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(displayContext!!)
} else {
(getSystemService(Service.WINDOW_SERVICE) as WindowManager).defaultDisplay
}
displayHelper.attach(renderer, display)
}
@@ -222,4 +229,11 @@ class FilamentLiveWallpaper : WallpaperService() {
}
}
}
@RequiresApi(30)
class Api30Impl {
companion object {
fun getDisplay(context: Context) = context.display!!
}
}
}

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -31,6 +33,12 @@ android {
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -20,6 +22,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,5 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -22,6 +25,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {
@@ -31,5 +40,5 @@ android {
dependencies {
implementation project(':filament-android')
implementation 'androidx.annotation:annotation:1.1.0'
implementation deps.androidx.annotations
}

View File

@@ -70,11 +70,11 @@ public class MainActivity extends Activity
private Page mPage;
private PageMaterials mPageMaterials;
private Scene mScene;
private Texture[] mTextures = new Texture[2];
private final Texture[] mTextures = new Texture[2];
private @Entity int mLight;
private IndirectLight mIndirectLight;
private float[] mTouchDownPoint = new float[2];
private final float[] mTouchDownPoint = new float[2];
private float mTouchDownValue = 0;
private float mPageAnimationRadians = 0;
private float mPageAnimationValue = 0;

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -17,10 +19,16 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.streamtest"
minSdkVersion versions.minSdk
minSdkVersion 23
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -18,6 +18,7 @@ package com.google.android.filament.streamtest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.view.Choreographer
import android.view.Surface
@@ -33,7 +34,9 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.Channels
import android.opengl.*
import android.os.Build
import android.view.MotionEvent
import androidx.annotation.RequiresApi
import com.google.android.filament.android.DisplayHelper
@@ -78,6 +81,13 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
private var externalTextureID: Int = 0
@RequiresApi(30)
class Api30Impl {
companion object {
fun getDisplay(context: Context) = context.display!!
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -94,7 +104,15 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
setupScene()
externalTextureID = createExternalTexture()
streamHelper = StreamHelper(engine, materialInstance, windowManager.defaultDisplay, externalTextureID)
@Suppress("deprecation")
val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(this)
} else {
windowManager.defaultDisplay!!
}
streamHelper = StreamHelper(engine, materialInstance, display, externalTextureID)
this.title = streamHelper.getTestName()
}

View File

@@ -24,6 +24,7 @@ import android.view.Surface
import android.graphics.*
import android.media.ImageReader
import android.opengl.Matrix
import android.os.Looper
import android.view.Display
import com.google.android.filament.*
@@ -32,7 +33,12 @@ import com.google.android.filament.*
/**
* Demonstrates Filament's various texture sharing mechanisms.
*/
class StreamHelper(private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance, private val display: Display, private val externalTextureId: Int) {
class StreamHelper(
private val filamentEngine: Engine,
private val filamentMaterial: MaterialInstance,
private val display: Display,
private val externalTextureId: Int
) {
/**
* The StreamSource configures the source data for the texture.
*
@@ -50,7 +56,7 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
}
private var streamSource = StreamSource.CANVAS_STREAM_NATIVE
private val directImageHandler = Handler()
private val directImageHandler = Handler(Looper.getMainLooper())
private var resolution = Size(640, 480)
private var surfaceTexture: SurfaceTexture? = null
private var imageReader: ImageReader? = null
@@ -93,11 +99,30 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
val canvas = surface.lockCanvas(null)
val movingPaint = Paint()
movingPaint.shader = LinearGradient(kGradientOffset, 0.0f, kGradientOffset + kGradientScale, 0.0f, kGradientColors, kGradientStops, Shader.TileMode.REPEAT)
canvas.drawRect(Rect(0, resolution.height / 2, resolution.width, resolution.height), movingPaint)
movingPaint.shader = LinearGradient(
kGradientOffset,
0.0f,
kGradientOffset + kGradientScale,
0.0f,
kGradientColors,
kGradientStops,
Shader.TileMode.REPEAT
)
canvas.drawRect(
Rect(0, resolution.height / 2, resolution.width, resolution.height),
movingPaint
)
val staticPaint = Paint()
staticPaint.shader = LinearGradient(0.0f, 0.0f, kGradientScale, 0.0f, kGradientColors, kGradientStops, Shader.TileMode.REPEAT)
staticPaint.shader = LinearGradient(
0.0f,
0.0f,
kGradientScale,
0.0f,
kGradientColors,
kGradientStops,
Shader.TileMode.REPEAT
)
canvas.drawRect(Rect(0, 0, resolution.width, resolution.height / 2), staticPaint)
surface.unlockCanvasAndPost(canvas)
@@ -108,7 +133,10 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
if (streamSource == StreamSource.CANVAS_STREAM_ACQUIRED) {
val image = imageReader!!.acquireLatestImage()
filamentStream!!.setAcquiredImage(image.hardwareBuffer!!, directImageHandler) { image.close() }
filamentStream!!.setAcquiredImage(
image.hardwareBuffer!!,
directImageHandler
) { image.close() }
}
}
@@ -136,7 +164,9 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
val filamentTexture = this.filamentTexture!!
val sampler = TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR, TextureSampler.WrapMode.REPEAT)
val sampler = TextureSampler(
TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR,
TextureSampler.WrapMode.REPEAT)
// We are texturing a front-facing square shape so we need to generate a matrix that transforms (u, v, 0, 1)
// into a new UV coordinate according to the screen rotation and the aspect ratio of the camera image.
@@ -164,7 +194,8 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
// Connect the Stream to the Texture and the Texture to the MaterialInstance.
filamentMaterial.setParameter("videoTexture", filamentTexture, sampler)
filamentMaterial.setParameter("textureTransform", MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
filamentMaterial.setParameter("textureTransform",
MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
if (streamSource == StreamSource.CANVAS_STREAM_NATIVE) {
@@ -207,7 +238,9 @@ class StreamHelper(private val filamentEngine: Engine, private val filamentMater
filamentTexture.setExternalStream(filamentEngine, filamentStream!!)
this.imageReader = ImageReader.newInstance(resolution.width, resolution.height, ImageFormat.RGB_565, kImageReaderMaxImages).apply {
this.imageReader = ImageReader.newInstance(
resolution.width, resolution.height, ImageFormat.RGB_565, kImageReaderMaxImages
).apply {
canvasSurface = surface
}
}

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -21,6 +23,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -28,6 +30,12 @@ android {
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -21,6 +23,12 @@ android {
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {

View File

@@ -453,23 +453,23 @@ function build_android {
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug \
:filament-utils-android:assembleDebug
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:filamat-android:assembleDebug
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
for sample in ${ANDROID_SAMPLES}; do
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:samples:${sample}:assembleDebug
done
fi
@@ -502,23 +502,23 @@ function build_android {
if [[ "${ISSUE_RELEASE_BUILD}" == "true" ]]; then
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease \
:filament-utils-android:assembleRelease
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:filamat-android:assembleRelease
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
for sample in ${ANDROID_SAMPLES}; do
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:samples:${sample}:assembleRelease
done
fi
@@ -831,7 +831,7 @@ while getopts ":hacCfijmp:q:uvslwtdk:" opt; do
;;
v)
VULKAN_ANDROID_OPTION="-DFILAMENT_SUPPORTS_VULKAN=OFF"
VULKAN_ANDROID_GRADLE_OPTION="-Pfilament_exclude_vulkan"
VULKAN_ANDROID_GRADLE_OPTION="-Pcom.google.android.filament.exclude-vulkan"
echo "Disabling support for Vulkan in the core Filament library."
echo "Consider using -c after changing this option to clear the Gradle cache."
;;

View File

@@ -116,6 +116,7 @@ set(PRIVATE_HDRS
src/Intersections.h
src/MaterialParser.h
src/PerViewUniforms.h
src/PIDController.h
src/PostProcessManager.h
src/RenderPass.h
src/RenderPrimitive.h
@@ -274,6 +275,7 @@ add_custom_command(
OUTPUT "${MATERIAL_DIR}/colorGrading.filamat"
DEPENDS ../shaders/src/dithering.fs
DEPENDS ../shaders/src/vignette.fs
DEPENDS src/materials/colorGrading/colorGrading.fs
APPEND
)
@@ -281,6 +283,7 @@ add_custom_command(
OUTPUT "${MATERIAL_DIR}/colorGradingAsSubpass.filamat"
DEPENDS ../shaders/src/dithering.fs
DEPENDS ../shaders/src/vignette.fs
DEPENDS src/materials/colorGrading/colorGrading.fs
APPEND
)

View File

@@ -79,7 +79,7 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
include/private/backend/OpenGLPlatform.h
)
if (EGL)
list(APPEND SRCS src/opengl/PlatformEGL.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformEGL.cpp)
endif()
if (ANDROID)
# FIXME: this should be included when we build for JAVA (which is implied by ANDROID)
@@ -87,22 +87,22 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
list(APPEND SRCS src/VirtualMachineEnv.cpp)
endif ()
if (ANDROID)
list(APPEND SRCS src/android/ExternalStreamManagerAndroid.cpp)
list(APPEND SRCS src/opengl/platforms/ExternalStreamManagerAndroid.cpp)
list(APPEND SRCS src/android/ExternalTextureManagerAndroid.cpp)
list(APPEND SRCS src/opengl/PlatformEGLAndroid.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformEGLAndroid.cpp)
elseif (IOS)
list(APPEND SRCS src/opengl/PlatformCocoaTouchGL.mm)
list(APPEND SRCS src/opengl/CocoaTouchExternalImage.mm)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
elseif (APPLE)
list(APPEND SRCS src/opengl/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
elseif (WEBGL)
list(APPEND SRCS src/opengl/PlatformWebGL.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
elseif (LINUX)
list(APPEND SRCS src/opengl/PlatformGLX.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformGLX.cpp)
elseif (WIN32)
list(APPEND SRCS src/opengl/PlatformWGL.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformWGL.cpp)
else()
list(APPEND SRCS src/opengl/PlatformDummyGL.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformDummyGL.cpp)
endif()
endif()
@@ -133,9 +133,9 @@ if (FILAMENT_SUPPORTS_METAL)
# Objective-C++ sources need an additional compiler flag on iOS to disable exceptions.
set_property(SOURCE
${METAL_SRCS}
src/opengl/PlatformCocoaTouchGL.mm
src/opengl/CocoaTouchExternalImage.mm
src/opengl/PlatformCocoaGL.mm
src/opengl/platforms/PlatformCocoaTouchGL.mm
src/opengl/platforms/CocoaTouchExternalImage.mm
src/opengl/platforms/PlatformCocoaGL.mm
PROPERTY COMPILE_FLAGS -fno-objc-exceptions)
endif()

View File

@@ -220,7 +220,8 @@ enum class UniformType : uint8_t {
UINT3,
UINT4,
MAT3, //!< a 3x3 float matrix
MAT4 //!< a 4x4 float matrix
MAT4, //!< a 4x4 float matrix
STRUCT
};
enum class Precision : uint8_t {

View File

@@ -21,6 +21,8 @@
#include <utils/Log.h>
#include <utils/debug.h>
#include <limits>
namespace filament {
namespace backend {

View File

@@ -43,18 +43,6 @@ public:
return env;
}
VirtualMachineEnv() noexcept : mVirtualMachine(sVirtualMachine) {
// We're not initializing the JVM here -- but we could -- because most of the time
// we don't need the jvm. Instead we do the initialization on first use. This means we could get
// a nasty slow down the very first time, but we'll live with it for now.
}
~VirtualMachineEnv() {
if (mVirtualMachine) {
mVirtualMachine->DetachCurrentThread();
}
}
inline JNIEnv* getEnvironment() noexcept {
assert_invariant(mVirtualMachine);
JNIEnv* env = mJniEnv;
@@ -67,6 +55,18 @@ public:
static void handleException(JNIEnv* env) noexcept;
private:
VirtualMachineEnv() noexcept : mVirtualMachine(sVirtualMachine) {
// We're not initializing the JVM here -- but we could -- because most of the time
// we don't need the jvm. Instead we do the initialization on first use. This means we could get
// a nasty slow down the very first time, but we'll live with it for now.
}
~VirtualMachineEnv() {
if (mVirtualMachine) {
mVirtualMachine->DetachCurrentThread();
}
}
JNIEnv* getEnvironmentSlow() noexcept;
static JavaVM* sVirtualMachine;
JNIEnv* mJniEnv = nullptr;

View File

@@ -22,44 +22,44 @@
#if defined(ANDROID)
#include <sys/system_properties.h>
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#include "opengl/PlatformEGLAndroid.h"
#include "opengl/platforms/PlatformEGLAndroid.h"
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkAndroid.h"
#endif
#elif defined(IOS)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#include "opengl/PlatformCocoaTouchGL.h"
#include "opengl/platforms/PlatformCocoaTouchGL.h"
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkCocoaTouch.h"
#endif
#elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "opengl/PlatformCocoaGL.h"
#include "opengl/platforms/PlatformCocoaGL.h"
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkCocoa.h"
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "opengl/PlatformGLX.h"
#include "opengl/platforms/PlatformGLX.h"
#endif
#if defined (FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkLinux.h"
#endif
#elif defined(WIN32)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "opengl/PlatformWGL.h"
#include "opengl/platforms/PlatformWGL.h"
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkWindows.h"
#endif
#elif defined(__EMSCRIPTEN__)
#include "opengl/PlatformWebGL.h"
#include "opengl/platforms/PlatformWebGL.h"
#else
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#include "opengl/PlatformDummyGL.h"
#include "opengl/platforms/PlatformDummyGL.h"
#endif
#endif

View File

@@ -84,9 +84,12 @@ struct EGLExternalTexture : public ExternalTextureManagerAndroid::ExternalTextur
GraphicBufferWrapper* graphicBufferWrapper = nullptr;
};
ExternalTextureManagerAndroid& ExternalTextureManagerAndroid::get() noexcept {
static ExternalTextureManagerAndroid instance;
return instance;
ExternalTextureManagerAndroid& ExternalTextureManagerAndroid::create() noexcept {
return *(new ExternalTextureManagerAndroid{});
}
void ExternalTextureManagerAndroid::destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept {
delete pExternalTextureManager;
}
// called on gl thread
@@ -116,7 +119,7 @@ ExternalTextureManagerAndroid::~ExternalTextureManagerAndroid() noexcept {
}
// called on gl thread
backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::create() noexcept {
backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::createExternalTexture() noexcept {
#ifndef PLATFORM_HAS_HARDWAREBUFFER
if (!AHardwareBuffer_allocate) {
// initialize java stuff on-demand

View File

@@ -31,6 +31,15 @@
namespace filament {
/*
* ExternalTextureManagerAndroid::ExternalTexture is basically a wrapper for AHardwareBuffer.
*
* This class doesn't rely on GL or EGL, and could be used for other Android platform if needed
* (e.g. Vulkan).
*
* ExternalTextureManagerAndroid handle allocation/destruction using either Java or the NDK,
* whichever is available.
*/
class ExternalTextureManagerAndroid {
public:
@@ -39,26 +48,27 @@ public:
AHardwareBuffer* hardwareBuffer = nullptr;
};
static ExternalTextureManagerAndroid& get() noexcept;
// must be called on backend thread
static ExternalTextureManagerAndroid& create() noexcept;
// called on gl thread
ExternalTextureManagerAndroid() noexcept;
// must be called on backend thread
static void destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept;
// not quite sure on which thread this is going to be called
~ExternalTextureManagerAndroid() noexcept;
// called on gl thread
backend::Platform::ExternalTexture* create() noexcept;
// must be called on backend thread (only because we don't synchronize
backend::Platform::ExternalTexture* createExternalTexture() noexcept;
// called on app thread
void reallocate(
backend::Platform::ExternalTexture* ets, uint32_t w, uint32_t h,
backend::TextureFormat format, uint64_t usage) noexcept;
// called on gl thread
// must be called on backend thread
void destroy(backend::Platform::ExternalTexture* ets) noexcept;
private:
ExternalTextureManagerAndroid() noexcept;
~ExternalTextureManagerAndroid() noexcept;
// called on app thread
void alloc(backend::Platform::ExternalTexture* ets,
uint32_t w, uint32_t h, backend::TextureFormat format, uint64_t usage) noexcept;

View File

@@ -64,16 +64,19 @@ MetalDriver::MetalDriver(backend::MetalPlatform* platform) noexcept
initializeSupportedGpuFamilies(mContext);
utils::slog.d << "Supported GPU families: " << utils::io::endl;
utils::slog.v << "Supported GPU families: " << utils::io::endl;
if (mContext->highestSupportedGpuFamily.common > 0) {
utils::slog.d << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
utils::slog.v << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.apple > 0) {
utils::slog.d << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
utils::slog.v << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.mac > 0) {
utils::slog.d << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
utils::slog.v << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
}
utils::slog.v << "Features:" << utils::io::endl;
utils::slog.v << " readWriteTextureSupport: " <<
(bool) mContext->device.readWriteTextureSupport << utils::io::endl;
// In order to support texture swizzling, the GPU needs to support it and the system be running
// iOS 13+.
@@ -650,11 +653,10 @@ bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
}
bool MetalDriver::isFrameBufferFetchSupported() {
#if defined(IOS) && !defined(FILAMENT_IOS_SIMULATOR)
return true;
#else
return false;
#endif
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
// Apple GPUs with readWriteTextureSupport.
return mContext->highestSupportedGpuFamily.apple >= 1 &&
mContext->device.readWriteTextureSupport;
}
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {

View File

@@ -352,8 +352,7 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
length:source.size()
encoding:NSUTF8StringEncoding];
NSError* error = nil;
MTLCompileOptions* options = [MTLCompileOptions new];
options.languageVersion = MTLLanguageVersion1_1;
// When options is nil, Metal uses the most recent language version available.
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:nil
error:&error];

View File

@@ -75,10 +75,26 @@ OpenGLContext::OpenGLContext() noexcept {
// Figure out which driver bugs we need to workaround
if (strstr(renderer, "Adreno")) {
int maj, min, driverMajor, driverMinor;
int c = sscanf(version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
&maj, &min, &driverMajor, &driverMinor);
if (c == 4) {
// workarounds based on version here.
// notes:
// bugs.invalidate_end_only_if_invalidate_start
// - appeared at least in "OpenGL ES 3.2 V@0490.0 (GIT@85da404, I46ff5fc46f, 1606794520) (Date:11/30/20)"
// - wasn't present in "OpenGL ES 3.2 V@0490.0 (GIT@0905e9f, Ia11ce2d146, 1599072951) (Date:09/02/20)"
}
// On Adreno (As of 3/20) timer query seem to return the CPU time, not the GPU time.
bugs.dont_use_timer_query = true;
// Blits to texture arrays are failing
bugs.disable_sidecar_blit_into_texture_array = true;
// This bug doesn't happen anymore, but we don't know why. The standalone sample
// app that was written to show this problem still does. We have tested this on
// several V@0490.0 on several devices and the problem appears to have gone away.
// The working hypthesis is that some other state affects this behavior.
bugs.disable_sidecar_blit_into_texture_array = false;
// early exit condition is flattened in EASU code
bugs.split_easu = true;
bugs.invalidate_end_only_if_invalidate_start = true;

View File

@@ -17,7 +17,7 @@
#ifndef FILAMENT_DRIVER_OPENGL_COCOA_TOUCH_EXTERNAL_IMAGE
#define FILAMENT_DRIVER_OPENGL_COCOA_TOUCH_EXTERNAL_IMAGE
#include "gl_headers.h"
#include "../gl_headers.h"
#include <CoreVideo/CoreVideo.h>

View File

@@ -21,7 +21,7 @@
#include <OpenGLES/ES3/gl.h>
#include <OpenGLES/ES3/glext.h>
#include "GLUtils.h"
#include "../GLUtils.h"
#include <math/vec2.h>

View File

@@ -37,10 +37,12 @@ static void loadSymbol(T*& pfn, const char *symbol) noexcept {
pfn = (T*)dlsym(RTLD_DEFAULT, symbol);
}
ExternalStreamManagerAndroid& ExternalStreamManagerAndroid::get() noexcept {
// declaring this thread local, will ensure it's destroyed with the calling thread
static thread_local ExternalStreamManagerAndroid instance;
return instance;
ExternalStreamManagerAndroid& ExternalStreamManagerAndroid::create() noexcept {
return *(new ExternalStreamManagerAndroid{});
}
void ExternalStreamManagerAndroid::destroy(ExternalStreamManagerAndroid* pExternalStreamManagerAndroid) noexcept {
delete pExternalStreamManagerAndroid;
}
ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept
@@ -61,6 +63,8 @@ ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept
}
}
ExternalStreamManagerAndroid::~ExternalStreamManagerAndroid() noexcept = default;
UTILS_NOINLINE
JNIEnv* ExternalStreamManagerAndroid::getEnvironmentSlow() noexcept {
JNIEnv * env = mVm.getEnvironment();

View File

@@ -31,20 +31,37 @@ typedef struct ASurfaceTexture ASurfaceTexture;
namespace filament {
/*
* ExternalStreamManagerAndroid::Stream is basically a wrapper for SurfaceTexture.
*
* This class DOES DEPEND on having a GLES context, because that's how SurfaceTexture works.
*/
class ExternalStreamManagerAndroid {
public:
using Stream = backend::Platform::Stream;
ExternalStreamManagerAndroid() noexcept;
static ExternalStreamManagerAndroid& get() noexcept;
// must be called on GLES thread
static ExternalStreamManagerAndroid& create() noexcept;
// must be called on GLES thread
static void destroy(ExternalStreamManagerAndroid* pExternalStreamManagerAndroid) noexcept;
Stream* acquire(jobject surfaceTexture) noexcept;
void release(Stream* stream) noexcept;
// attach Stream to current GLES context
void attach(Stream* stream, intptr_t tname) noexcept;
// detach Stream to current GLES context
void detach(Stream* stream) noexcept;
// must be called on GLES context thread, updates the stream content
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept;
private:
ExternalStreamManagerAndroid() noexcept;
~ExternalStreamManagerAndroid() noexcept;
VirtualMachineEnv& mVm;
JNIEnv* mJniEnv = nullptr;
@@ -63,17 +80,17 @@ private:
JNIEnv* getEnvironmentSlow() noexcept;
jmethodID mSurfaceTextureClass_updateTexImage;
jmethodID mSurfaceTextureClass_getTimestamp;
jmethodID mSurfaceTextureClass_attachToGLContext;
jmethodID mSurfaceTextureClass_detachFromGLContext;
jmethodID mSurfaceTextureClass_updateTexImage{};
jmethodID mSurfaceTextureClass_getTimestamp{};
jmethodID mSurfaceTextureClass_attachToGLContext{};
jmethodID mSurfaceTextureClass_detachFromGLContext{};
ASurfaceTexture* (*ASurfaceTexture_fromSurfaceTexture)(JNIEnv*, jobject);
void (*ASurfaceTexture_release)(ASurfaceTexture*);
int (*ASurfaceTexture_attachToGLContext)(ASurfaceTexture*, uint32_t);
int (*ASurfaceTexture_detachFromGLContext)(ASurfaceTexture*);
int (*ASurfaceTexture_updateTexImage)(ASurfaceTexture*);
int64_t (*ASurfaceTexture_getTimestamp)(ASurfaceTexture*); // available since api 28
ASurfaceTexture* (*ASurfaceTexture_fromSurfaceTexture)(JNIEnv*, jobject){};
void (*ASurfaceTexture_release)(ASurfaceTexture*){};
int (*ASurfaceTexture_attachToGLContext)(ASurfaceTexture*, uint32_t){};
int (*ASurfaceTexture_detachFromGLContext)(ASurfaceTexture*){};
int (*ASurfaceTexture_updateTexImage)(ASurfaceTexture*){};
int64_t (*ASurfaceTexture_getTimestamp)(ASurfaceTexture*){}; // available since api 28
};
} // namespace filament

View File

@@ -19,8 +19,8 @@
#include "PlatformCocoaGL.h"
#include "OpenGLDriverFactory.h"
#include "gl_headers.h"
#include "opengl/OpenGLDriverFactory.h"
#include "opengl/gl_headers.h"
#include <utils/compiler.h>
#include <utils/Panic.h>

View File

@@ -30,9 +30,9 @@
#include <utils/Panic.h>
#include "OpenGLDriverFactory.h"
#include "../OpenGLDriverFactory.h"
#include "OpenGLDriver.h"
#include "../OpenGLDriver.h"
#include "CocoaTouchExternalImage.h"
namespace filament {

View File

@@ -16,9 +16,9 @@
#include "PlatformEGL.h"
#include "OpenGLDriver.h"
#include "OpenGLContext.h"
#include "OpenGLDriverFactory.h"
#include "opengl/OpenGLDriver.h"
#include "opengl/OpenGLContext.h"
#include "opengl/OpenGLDriverFactory.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>

View File

@@ -16,11 +16,11 @@
#include "PlatformEGLAndroid.h"
#include "OpenGLDriver.h"
#include "OpenGLContext.h"
#include "opengl/OpenGLDriver.h"
#include "opengl/OpenGLContext.h"
#include "android/ExternalTextureManagerAndroid.h"
#include "android/ExternalStreamManagerAndroid.h"
#include "ExternalStreamManagerAndroid.h"
#include "private/backend/VirtualMachineEnv.h"
#include <android/api-level.h>
@@ -73,8 +73,8 @@ using EGLStream = Platform::Stream;
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
: PlatformEGL(),
mExternalStreamManager(ExternalStreamManagerAndroid::get()),
mExternalTextureManager(ExternalTextureManagerAndroid::get()) {
mExternalStreamManager(ExternalStreamManagerAndroid::create()),
mExternalTextureManager(ExternalTextureManagerAndroid::create()) {
char scratch[PROP_VALUE_MAX + 1];
int length = __system_property_get("ro.build.version.release", scratch);
@@ -87,6 +87,15 @@ PlatformEGLAndroid::PlatformEGLAndroid() noexcept
}
}
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
void PlatformEGLAndroid::terminate() noexcept {
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
ExternalTextureManagerAndroid::destroy(&mExternalTextureManager);
PlatformEGL::terminate();
}
Driver* PlatformEGLAndroid::createDriver(void* sharedContext) noexcept {
Driver* driver = PlatformEGL::createDriver(sharedContext);
auto extensions = GLUtils::split(eglQueryString(mEGLDisplay, EGL_EXTENSIONS));
@@ -146,7 +155,7 @@ void PlatformEGLAndroid::updateTexImage(Stream* stream, int64_t* timestamp) noex
}
Platform::ExternalTexture* PlatformEGLAndroid::createExternalTextureStorage() noexcept {
return mExternalTextureManager.create();
return mExternalTextureManager.createExternalTexture();
}
void PlatformEGLAndroid::reallocateExternalStorage(
@@ -236,11 +245,6 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
return { eglImage, patchedCallback, closure, source.handler };
}
// This must be called when the library is loaded. We need this to get a reference to the global VM
void JNI_OnLoad(JavaVM* vm, void* reserved) {
::filament::VirtualMachineEnv::JNI_OnLoad(vm);
}
} // namespace filament
// ---------------------------------------------------------------------------------------------

View File

@@ -28,6 +28,9 @@ class PlatformEGLAndroid final : public PlatformEGL {
public:
PlatformEGLAndroid() noexcept;
~PlatformEGLAndroid() noexcept override;
void terminate() noexcept override;
backend::Driver* createDriver(void* sharedContext) noexcept final;

View File

@@ -23,7 +23,7 @@
#include <GL/glx.h>
#include <GL/glxext.h>
#include "OpenGLDriverFactory.h"
#include "../OpenGLDriverFactory.h"
#include <dlfcn.h>

View File

@@ -18,7 +18,7 @@
#include <Wingdi.h>
#include "OpenGLDriverFactory.h"
#include "../OpenGLDriverFactory.h"
#ifdef _MSC_VER
// this variable is checked in BlueGL.h (included from "gl_headers.h" right after this),
@@ -27,7 +27,7 @@
#define FILAMENT_PLATFORM_WGL
#endif
#include "gl_headers.h"
#include "../gl_headers.h"
#include "Windows.h"
#include <GL/gl.h>

View File

@@ -15,7 +15,7 @@
*/
#include "PlatformWebGL.h"
#include "OpenGLDriverFactory.h"
#include "../OpenGLDriverFactory.h"
namespace filament {

View File

@@ -54,18 +54,6 @@ public:
Type type; //!< property type
};
struct PropertyArray {
Property const* array;
size_t size;
};
/**
* Queries the list of all available properties.
*
* @return A pair containing a pointer to a Property array and the size of this array.
*/
PropertyArray getProperties() const noexcept;
/**
* Queries whether a property exists
* @param name The name of the property to query
@@ -123,6 +111,24 @@ public:
bool getProperty(const char* name, math::float4* v) const noexcept;
/** @}*/
struct DataSource {
void const* data;
size_t count;
};
DataSource getDataSource(const char* name) const noexcept;
struct FrameHistory {
using duration_ms = float;
duration_ms target{};
duration_ms targetWithHeadroom{};
duration_ms frameTime{};
duration_ms frameTimeDenoised{};
float scale = 1.0f;
float pid_e = 0.0f;
float pid_i = 0.0f;
float pid_d = 0.0f;
};
};

View File

@@ -273,6 +273,7 @@ public:
* Constant bias in depth-resolution units by which shadows are moved away from the
* light. The default value of 0.5 is used to round depth values up.
* Generally this value shouldn't be changed or at least be small and positive.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetConstant = 0.5f;
@@ -281,6 +282,7 @@ public:
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
* Generally this value is between 0.5 and the size in texel of the PCF filter.
* Setting this value correctly is essential for LISPSM shadow-maps.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetSlope = 2.0f;
@@ -558,10 +560,11 @@ public:
* and are defined by the angle from the center axis to where the falloff begins (i.e.
* cones are defined by their half-angle).
*
* @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$
*
* @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$
* Both inner and outer are silently clamped to a minimum value of 0.5 degrees
* (~0.00873 radians) to avoid floating-point precision issues during rendering.
*
* @param inner inner cone angle in *radians* between 0.00873 and \p outer
* @param outer outer cone angle in *radians* between 0.00873 inner and @f$ \pi/2 @f$
* @return This Builder, for chaining calls.
*
* @note
@@ -812,8 +815,8 @@ public:
* Dynamically updates a spot light's cone as angles
*
* @param i Instance of the component obtained from getInstance().
* @param inner inner cone angle in *radians* between 0 and pi/2
* @param outer outer cone angle in *radians* between inner and pi/2
* @param inner inner cone angle in *radians* between 0.00873 and outer
* @param outer outer cone angle in *radians* between 0.00873 and pi/2
*
* @see Builder.spotLightCone()
*/

View File

@@ -99,7 +99,7 @@ public:
* headRoomRatio: additional headroom for the GPU as a ratio of the targetFrameTime.
* Useful for taking into account constant costs like post-processing or
* GPU drivers on different platforms.
* history: History size. higher values, tend to filter more (clamped to 30)
* history: History size. higher values, tend to filter more (clamped to 31)
* scaleRate: rate at which the gpu load is adjusted to reach the target frame rate
* This value can be computed as 1 / N, where N is the number of frames
* needed to reach 64% of the target scale factor.
@@ -110,10 +110,10 @@ public:
*
*/
struct FrameRateOptions {
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
float scaleRate = 0.125f; //!< rate at which the system reacts to load changes
uint8_t history = 3; //!< history size
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
float scaleRate = 1.0f / 8.0f; //!< rate at which the system reacts to load changes
uint8_t history = 15; //!< history size
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
};
/**
@@ -341,7 +341,7 @@ public:
*
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
* screen
*
*
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
@@ -359,7 +359,8 @@ public:
* O------------+-------+
*
*
* Typically readPixels() will be called after render() and before endFrame().
* readPixels() must be called within a frame, meaning after beginFrame() and before endFrame().
* Typically, readPixels() will be called after render().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen

View File

@@ -31,9 +31,7 @@ using namespace utils;
namespace filament {
FDebugRegistry::FDebugRegistry() noexcept {
mProperties.reserve(8);
}
FDebugRegistry::FDebugRegistry() noexcept = default;
UTILS_NOINLINE
void *FDebugRegistry::getPropertyAddress(const char *name) noexcept {
@@ -48,15 +46,10 @@ void *FDebugRegistry::getPropertyAddress(const char *name) noexcept {
void FDebugRegistry::registerProperty(utils::StaticString name, void *p, Type type) noexcept {
auto& propertyMap = mPropertyMap;
if (propertyMap.find(name) == propertyMap.end()) {
mProperties.push_back({ name.c_str(), type });
propertyMap[name] = p;
}
}
DebugRegistry::PropertyArray FDebugRegistry::getProperties() const noexcept {
return {mProperties.data(), mProperties.size()};
}
inline bool FDebugRegistry::hasProperty(const char *name) const noexcept {
return const_cast<FDebugRegistry *>(this)->getPropertyAddress(name) != nullptr;
}
@@ -83,14 +76,27 @@ inline bool FDebugRegistry::getProperty(const char* name, T* UTILS_RESTRICT p) c
return false;
}
void FDebugRegistry::registerDataSource(StaticString name, void const* data, size_t count) noexcept {
auto& dataSourceMap = mDataSourceMap;
if (dataSourceMap.find(name) == dataSourceMap.end()) {
dataSourceMap[name] = { data, count };
}
}
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
StaticString key = StaticString::make(name, strlen(name));
auto &dataSourceMap = mDataSourceMap;
auto const& it = dataSourceMap.find(key);
if (it == dataSourceMap.end()) {
return { nullptr, 0u };
}
return it->second;
}
// ------------------------------------------------------------------------------------------------
// Trampoline calling into private implementation
// ------------------------------------------------------------------------------------------------
DebugRegistry::PropertyArray DebugRegistry::getProperties() const noexcept {
return upcast(this)->getProperties();
}
bool DebugRegistry::hasProperty(const char* name) const noexcept {
return upcast(this)->hasProperty(name);
}
@@ -148,5 +154,10 @@ void *DebugRegistry::getPropertyAddress(const char *name) noexcept {
return upcast(this)->getPropertyAddress(name);
}
DebugRegistry::DataSource DebugRegistry::getDataSource(const char* name) const noexcept {
return upcast(this)->getDataSource(name);
}
} // namespace filament

View File

@@ -24,7 +24,9 @@
#include <cmath>
namespace filament {
using namespace utils;
using namespace backend;
// this is to avoid a call to memmove
template<class InputIterator, class OutputIterator>
@@ -35,8 +37,7 @@ void move_backward(InputIterator first, InputIterator last, OutputIterator resul
}
}
FrameInfoManager::FrameInfoManager(FEngine& engine) : mEngine(engine) {
backend::DriverApi& driver = mEngine.getDriverApi();
FrameInfoManager::FrameInfoManager(DriverApi& driver) noexcept {
for (auto& query : mQueries) {
query = driver.createTimerQuery();
}
@@ -44,15 +45,13 @@ FrameInfoManager::FrameInfoManager(FEngine& engine) : mEngine(engine) {
FrameInfoManager::~FrameInfoManager() noexcept = default;
void FrameInfoManager::terminate() {
backend::DriverApi& driver = mEngine.getDriverApi();
void FrameInfoManager::terminate(DriverApi& driver) noexcept {
for (auto& query : mQueries) {
driver.destroyTimerQuery(query);
}
}
void FrameInfoManager::beginFrame(Config const& config, uint32_t frameId) {
backend::DriverApi& driver = mEngine.getDriverApi();
void FrameInfoManager::beginFrame(DriverApi& driver,Config const& config, uint32_t frameId) noexcept {
driver.beginTimerQuery(mQueries[mIndex]);
uint64_t elapsed = 0;
if (driver.getTimerQueryValue(mQueries[mLast], &elapsed)) {
@@ -60,16 +59,15 @@ void FrameInfoManager::beginFrame(Config const& config, uint32_t frameId) {
// conversion to our duration happens here
mFrameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
}
update(config,mFrameTime);
update(config, mFrameTime);
}
void FrameInfoManager::endFrame() {
backend::DriverApi& driver = mEngine.getDriverApi();
void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
driver.endTimerQuery(mQueries[mIndex]);
mIndex = (mIndex + 1) % POOL_COUNT;
}
void FrameInfoManager::update(Config const& config, FrameInfoManager::duration lastFrameTime) {
void FrameInfoManager::update(Config const& config, FrameInfoManager::duration lastFrameTime) noexcept {
// keep an history of frame times
auto& history = mFrameTimeHistory;
@@ -95,34 +93,7 @@ void FrameInfoManager::update(Config const& config, FrameInfoManager::duration l
duration denoisedFrameTime = median[size / 2];
history[0].denoisedFrameTime = denoisedFrameTime;
// how much we need to scale the current workload to fit in our target, at this instant
const duration targetWithHeadroom = config.targetFrameTime * (1.0f - config.headRoomRatio);
const duration measured = denoisedFrameTime;
// We use a P.I.D. controller below to figure out the scaling factor to apply. In practice, we
// don't use the Derivative gain (so it's really a PI controller).
const float Kp = (1.0f - std::exp(-config.oneOverTau));
const float Ki = Kp / 10.0f;
const float Kd = 0.0f;
history[0].pid.error = (targetWithHeadroom - measured) / targetWithHeadroom;
history[0].pid.integral = history[1].pid.integral + Ki * history[0].pid.error;
history[0].pid.integral = math::clamp(history[0].pid.integral, -6.0f, 2.0f);
const float derivative = Kd * (history[0].pid.error - history[1].pid.error);
const float out = Kp * history[0].pid.error + history[0].pid.integral + derivative;
// maps the command to a ratio, it really doesn't matter much how the conversion is done
// the system will find the right value automatically
const float scale = std::exp2(out);
history[0].scale = scale;
history[0].valid = true;
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("info_e", history[0].pid.error * 100);
SYSTRACE_VALUE32("info_s", scale * 100);
// slog.d << history[0].pid.error * 100 << "%, " << scale << io::endl;
}

View File

@@ -17,9 +17,8 @@
#ifndef TNT_FILAMENT_FRAMEINFO_H
#define TNT_FILAMENT_FRAMEINFO_H
#include "details/Engine.h"
#include "backend/Handle.h"
#include <private/backend/DriverApi.h>
#include <array>
#include <chrono>
@@ -30,38 +29,35 @@ namespace filament {
class FEngine;
struct FrameInfo {
using duration = std::chrono::duration<float>;
using duration = std::chrono::duration<float, std::milli>;
duration frameTime{}; // frame period
duration denoisedFrameTime{}; // frame period (median filter)
bool valid = false;
float scale = 1.0f;
struct {
float integral{};
float error{};
} pid;
};
class FrameInfoManager {
static constexpr size_t POOL_COUNT = 8;
static constexpr size_t MAX_FRAMETIME_HISTORY = 32u;
static constexpr size_t POOL_COUNT = 4;
static constexpr size_t MAX_FRAMETIME_HISTORY = 31u;
public:
using duration = FrameInfo::duration;
struct Config {
duration targetFrameTime;
float headRoomRatio;
float oneOverTau;
uint32_t historySize;
};
explicit FrameInfoManager(FEngine& engine);
~FrameInfoManager() noexcept;
void terminate();
void beginFrame(Config const& config, uint32_t frameId); // call this immediately after "make current"
void endFrame(); // call this immediately before "swap buffers"
explicit FrameInfoManager(backend::DriverApi& driver) noexcept;
FrameInfo const& getLastFrameInfo() const {
~FrameInfoManager() noexcept;
void terminate(backend::DriverApi& driver) noexcept;
// call this immediately after "make current"
void beginFrame(backend::DriverApi& driver, Config const& config, uint32_t frameId) noexcept;
// call this immediately before "swap buffers"
void endFrame(backend::DriverApi& driver) noexcept;
FrameInfo const& getLastFrameInfo() const noexcept {
return mFrameTimeHistory[0];
}
@@ -69,10 +65,8 @@ public:
return getLastFrameInfo().frameTime;
}
private:
void update(Config const& config, duration lastFrameTime);
FEngine& mEngine;
void update(Config const& config, duration lastFrameTime) noexcept;
backend::Handle<backend::HwTimerQuery> mQueries[POOL_COUNT];
duration mFrameTime{};
uint32_t mIndex = 0;

View File

@@ -26,13 +26,14 @@ namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(FEngine& engine, size_t latency) noexcept
: mEngine(engine), mLast(latency) {
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(latency) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
FrameSkipper::~FrameSkipper() noexcept {
auto& driver = mEngine.getDriverApi();
FrameSkipper::~FrameSkipper() noexcept = default;
void FrameSkipper::terminate(DriverApi& driver) noexcept {
for (auto sync : mDelayedSyncs) {
if (sync) {
driver.destroySync(sync);
@@ -40,8 +41,7 @@ FrameSkipper::~FrameSkipper() noexcept {
}
}
bool FrameSkipper::beginFrame() noexcept {
auto& driver = mEngine.getDriverApi();
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& syncs = mDelayedSyncs;
auto sync = syncs.front();
if (sync) {
@@ -58,11 +58,10 @@ bool FrameSkipper::beginFrame() noexcept {
return true;
}
void FrameSkipper::endFrame() noexcept {
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
// if the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& driver = mEngine.getDriverApi();
auto& sync = mDelayedSyncs[mLast];
if (sync) {
driver.destroySync(sync);

View File

@@ -18,28 +18,28 @@
#define TNT_FILAMENT_DETAILS_FRAMESKIPPER_H
#include <backend/Handle.h>
#include <private/backend/DriverApi.h>
#include <array>
namespace filament {
class FEngine;
class FrameSkipper {
static constexpr size_t MAX_FRAME_LATENCY = 4;
public:
explicit FrameSkipper(FEngine& engine, size_t latency = 2) noexcept;
explicit FrameSkipper(size_t latency = 2) noexcept;
~FrameSkipper() noexcept;
void terminate(backend::DriverApi& driver) noexcept;
// returns false if we need to skip this frame, because the gpu is running behind the cpu.
// in that case, don't call endFrame().
// returns true if rendering can proceed. Always call endFrame() when done.
bool beginFrame() noexcept;
bool beginFrame(backend::DriverApi& driver) noexcept;
void endFrame() noexcept;
void endFrame(backend::DriverApi& driver) noexcept;
private:
FEngine& mEngine;
using Container = std::array<backend::Handle<backend::HwSync>, MAX_FRAME_LATENCY>;
mutable Container mDelayedSyncs{};
size_t mLast;

View File

@@ -560,14 +560,19 @@ void Froxelizer::froxelizeLoop(FEngine& engine,
const mat4f& projection = mProjection;
const mat3f& vn = camera.view.upperLeft();
// We use minimum cone angle of 0.5 degrees because too small angles cause issues in the
// sphere/cone intersection test, due to floating-point precision.
constexpr float maxInvSin = 114.59301f; // 1 / sin(0.5 degrees)
constexpr float maxCosSquared = 0.99992385f; // cos(0.5 degrees)^2
for (size_t i = offset; i < count; i += stride) {
const size_t j = i + FScene::DIRECTIONAL_LIGHTS_COUNT;
FLightManager::Instance li = instances[j];
LightParams light = {
.position = (camera.view * float4{ spheres[j].xyz, 1 }).xyz, // to view-space
.cosSqr = lcm.getCosOuterSquared(li), // spot only
.axis = vn * directions[j], // spot only
.invSin = lcm.getSinInverse(li), // spot only
.cosSqr = std::min(maxCosSquared, lcm.getCosOuterSquared(li)), // spot only
.axis = vn * directions[j], // spot only
.invSin = std::min(maxInvSin, lcm.getSinInverse(li)), // spot only
.radius = spheres[j].w,
};

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_PIDCONTROLLER_H
#define TNT_FILAMENT_PIDCONTROLLER_H
#include <math/scalar.h>
#include <limits>
namespace filament {
class PIDController {
public:
PIDController() noexcept = default;
void setStandardGains(float Kp, float Ti, float Td) noexcept {
mKp = Kp;
mKi = Kp / Ti;
mKd = Kp * Td;
}
void setParallelGains(float Kp, float Ki, float Kd) noexcept {
mKp = Kp;
mKi = Ki;
mKd = Kd;
}
// output is kept steady in the dead band
void setOutputDeadBand(float low, float high) noexcept {
mDeadBandLow = low;
mDeadBandHigh = high;
}
// integral bounds to prevent windup
void setIntegralLimits(float low, float high) noexcept {
mIntegralLimitLow = low;
mIntegralLimitHigh = high;
}
// output bounds
void setOutputLimits(float low, float high) noexcept {
mOutputLimitLow = low;
mOutputLimitHigh = high;
}
// disable integral term to prevent windup
void setIntegralInhibitionEnabled(bool enabled) noexcept {
mIntegralInhibition = enabled ? 0.0f : 1.0f;
}
// update PID output
float update(float measure, float target, float dt) const noexcept {
// compute error
const float error = target - measure;
// compute error integration
float integral = mIntegral + error * mIntegralInhibition * dt;
// compute derivative
const float derivative = (error - mLastError) / dt;
// prevent integral windup
integral = math::clamp(integral, mIntegralLimitLow, mIntegralLimitHigh);
// PID controller output
float out = mKp * error + mKi * integral + mKd * derivative;
// Apply dead band
if (out > mDeadBandLow && out < mDeadBandHigh) {
out = 0.0f;
}
// Apply output limits
out = math::clamp(out, mOutputLimitLow, mOutputLimitHigh);
// save state for next round
mIntegral = integral;
mLastError = error;
mDerivative = derivative;
return out;
}
float getError() const noexcept {
return mLastError;
}
float getIntegral() const noexcept {
return mIntegral;
}
float getDerivative() const noexcept {
return mDerivative;
}
private:
float mKp = 0.1f;
float mKi = 0.0f;
float mKd = 0.0f;
float mIntegralInhibition = 1.0f;
float mIntegralLimitLow = -std::numeric_limits<float>::infinity();
float mIntegralLimitHigh = std::numeric_limits<float>::infinity();
float mOutputLimitLow = -std::numeric_limits<float>::infinity();
float mOutputLimitHigh = std::numeric_limits<float>::infinity();
float mDeadBandLow = 0.0f;
float mDeadBandHigh = 0.0f;
mutable float mLastError = 0.0f;
mutable float mIntegral = 0.0f;
mutable float mDerivative = 0.0f;
};
} // namespace filament
#endif // TNT_FILAMENT_PIDCONTROLLER_H

View File

@@ -84,6 +84,8 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
s.cameraPosition = float3{ camera.getPosition() };
s.worldOffset = camera.worldOffset;
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
s.clipControl = mClipControl;
}

View File

@@ -16,6 +16,7 @@
#include "details/Renderer.h"
#include "PostProcessManager.h"
#include "RenderPass.h"
#include "ResourceAllocator.h"
@@ -53,8 +54,8 @@ using namespace backend;
FRenderer::FRenderer(FEngine& engine) :
mEngine(engine),
mFrameSkipper(engine, 1u),
mFrameInfoManager(engine),
mFrameSkipper(1u),
mFrameInfoManager(engine.getDriverApi()),
mIsRGB8Supported(false),
mPerRenderPassArena(engine.getPerRenderPassAllocator())
{
@@ -129,7 +130,8 @@ void FRenderer::terminate(FEngine& engine) {
// to initialize themselves, otherwise the engine tries to destroy invalid handles.
engine.execute();
}
mFrameInfoManager.terminate();
mFrameInfoManager.terminate(driver);
mFrameSkipper.terminate(driver);
}
void FRenderer::resetUserTime() {
@@ -229,7 +231,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
bool hasColorGrading = hasPostProcess;
bool hasDithering = view.getDithering() == Dithering::TEMPORAL;
bool hasFXAA = view.getAntiAliasing() == AntiAliasing::FXAA;
float2 scale = view.updateScale(mFrameInfoManager.getLastFrameInfo());
float2 scale = view.updateScale(engine, mFrameInfoManager.getLastFrameInfo(), mFrameRateOptions, mDisplayInfo);
auto msaaOptions = view.getMultiSampleAntiAliasingOptions();
auto dsrOptions = view.getDynamicResolutionOptions();
auto bloomOptions = view.getBloomOptions();
@@ -632,7 +634,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// * This is because the default render target is not multi-sampled, so we need an
// intermediate buffer when MSAA is enabled.
// * We also need an extra buffer for blending the result to the framebuffer if the view
// is translucent.
// is translucent AND we've not already done it as part of upscaling.
// * And we can't use the default rendertarget if MRT is required (e.g. with color grading
// as a subpass)
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
@@ -645,7 +647,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (UTILS_LIKELY(!blending)) {
input = ppm.opaqueBlit(fg, input, {
.width = vp.width, .height = vp.height,
.format = colorGradingConfig.ldrFormat }, SamplerMagFilter::LINEAR);
.format = colorGradingConfig.ldrFormat }, SamplerMagFilter::NEAREST);
} else {
input = ppm.blendBlit(fg, blending, {
.quality = QualityLevel::LOW
@@ -1058,12 +1060,8 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
// This need to occur after the backend beginFrame() because some backends need to start
// a command buffer before creating a fence.
mFrameInfoManager.beginFrame({
.targetFrameTime = FrameInfo::duration{
float(mFrameRateOptions.interval) / mDisplayInfo.refreshRate
},
.headRoomRatio = mFrameRateOptions.headRoomRatio,
.oneOverTau = mFrameRateOptions.scaleRate,
mFrameInfoManager.beginFrame(driver, {
.historySize = mFrameRateOptions.history
}, mFrameId);
@@ -1103,7 +1101,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
engine.prepare();
};
if (mFrameSkipper.beginFrame()) {
if (mFrameSkipper.beginFrame(driver)) {
// if beginFrame() returns true, we are expecting a call to endFrame(),
// so do the beginFrame work right now, instead of requiring a call to render()
beginFrameInternal();
@@ -1137,8 +1135,8 @@ void FRenderer::endFrame() {
driver.debugThreading();
}
mFrameInfoManager.endFrame();
mFrameSkipper.endFrame();
mFrameInfoManager.endFrame(driver);
mFrameSkipper.endFrame(driver);
if (mSwapChain) {
mSwapChain->commit(driver);
@@ -1173,6 +1171,11 @@ void FRenderer::endFrame() {
void FRenderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) {
#ifndef NDEBUG
const bool withinFrame = mSwapChain != nullptr;
ASSERT_PRECONDITION(withinFrame, "readPixels() on a SwapChain must be called after"
" beginFrame() and before endFrame().");
#endif
readPixels(mRenderTarget, xoffset, yoffset, width, height, std::move(buffer));
}

View File

@@ -316,9 +316,11 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
const size_t gpuIndex = i - DIRECTIONAL_LIGHTS_COUNT;
auto li = instances[i];
lp[gpuIndex].positionFalloff = { spheres[i].xyz, lcm.getSquaredFalloffInv(li) };
lp[gpuIndex].color = { lcm.getColor(li), 0.0f };
lp[gpuIndex].directionIES = { directions[i], 0.0f };
lp[gpuIndex].direction = directions[i];
lp[gpuIndex].reserved1 = {};
lp[gpuIndex].colorIES = { lcm.getColor(li), 0.0f };
lp[gpuIndex].spotScaleOffset = lcm.getSpotParams(li).scaleOffset;
lp[gpuIndex].reserved3 = {};
lp[gpuIndex].intensity = lcm.getIntensity(li);
lp[gpuIndex].typeShadow = LightsUib::packTypeShadow(
lcm.isPointLight(li) ? 0u : 1u,
@@ -326,7 +328,6 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
shadowInfo[i].index,
shadowInfo[i].layer);
lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows);
lp[gpuIndex].reserved = {};
}
driver.updateBufferObject(lightUbh, { lp, positionalLightCount * sizeof(LightsUib) }, 0);

View File

@@ -22,6 +22,7 @@
#include "details/Engine.h"
#include "details/Scene.h"
#include "details/View.h"
#include <backend/DriverEnums.h>
@@ -52,7 +53,7 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters);
debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters);
if (ENABLE_LISPSM) {
if constexpr (ENABLE_LISPSM) {
debugRegistry.registerProperty("d.shadowmap.lispsm", &engine.debug.shadowmap.lispsm);
debugRegistry.registerProperty("d.shadowmap.dzn", &engine.debug.shadowmap.dzn);
debugRegistry.registerProperty("d.shadowmap.dzf", &engine.debug.shadowmap.dzf);
@@ -74,140 +75,66 @@ void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
pass->setCamera(cameraInfo);
pass->setVisibilityMask(visibilityMask);
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
pass->overridePolygonOffset(&mPolygonOffset);
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
pass->appendCommands(RenderPass::SHADOW);
pass->sortCommands();
}
mat4f ShadowMap::getLightViewMatrix(float3 position, float3 direction) noexcept {
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
const mat4f M = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
return FCamera::rigidTransformInverse(M);
}
void ShadowMap::computeSceneInfo(float3 dir,
FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers,
SceneInfo& sceneInfo) {
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
const mat4f Mv = getLightViewMatrix({}, dir);
const mat4f V = camera.view;
void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept {
// Compute scene bounds in world space, as well as the light-space and view-space near/far planes
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.wsShadowCastersVolume = {};
sceneInfo.wsShadowReceiversVolume = {};
visitScene(scene, visibleLayers,
[&](Aabb caster) {
sceneInfo.wsShadowCastersVolume.min =
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
sceneInfo.wsShadowCastersVolume.max =
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
float2 nf = computeNearFar(Mv, caster);
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
},
[&](Aabb receiver) {
sceneInfo.wsShadowReceiversVolume.min =
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
sceneInfo.wsShadowReceiversVolume.max =
max(sceneInfo.wsShadowReceiversVolume.max, receiver.max);
float2 nf = computeNearFar(V, receiver);
sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x);
sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y);
}
);
}
void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo,
const SceneInfo& cascadeParams) noexcept {
// this is the hard part here, find a good frustum for our camera
auto& lcm = mEngine.getLightManager();
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
mShadowMapInfo = shadowMapInfo;
FLightManager::ShadowParams params = lcm.getShadowParams(li);
mPolygonOffset = {
// handle reversed Z
.slope = -params.options.polygonOffsetSlope,
.constant = -params.options.polygonOffsetConstant
};
// Note: we keep the polygon offset even with VSM as it seems to help.
auto& lcm = mEngine.getLightManager();
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
FLightManager::ShadowParams params = lcm.getShadowParams(li);
// Adjust the camera's projection for the light's shadowFar
mat4f cullingProjection(camera.cullingProjection);
if (params.options.shadowFar > 0.0f) {
float n = camera.zn;
float f = params.options.shadowFar;
if (std::abs(cullingProjection[2].w) > std::numeric_limits<float>::epsilon()) {
// perspective projection
cullingProjection[2].z = (f + n) / (n - f);
cullingProjection[3].z = (2 * f * n) / (n - f);
} else {
// ortho projection
cullingProjection[2].z = 2.0f / (n - f);
cullingProjection[3].z = (f + n) / (n - f);
}
}
const ShadowCameraInfo cameraInfo = {
.projection = cullingProjection,
.model = camera.model,
.view = camera.view,
.worldOrigin = camera.worldOrigin,
.zn = camera.zn,
.zf = camera.zf
};
// debugging...
const float dz = cameraInfo.zf - cameraInfo.zn;
#ifndef NDEBUG
// LISPSM debugging for directional light (works because we only have one)
const float dz = camera.zf - camera.zn;
float& dzn = mEngine.debug.shadowmap.dzn;
float& dzf = mEngine.debug.shadowmap.dzf;
if (dzn < 0) dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn) / dz;
else params.options.shadowNearHint = dzn * dz - camera.zn;
if (dzf > 0) dzf =-std::max(0.0f, camera.zf - params.options.shadowFarHint) / dz;
else params.options.shadowFarHint = dzf * dz + camera.zf;
#endif
using LightType = FLightManager::Type;
switch (lcm.getType(li)) {
case LightType::SUN:
case LightType::DIRECTIONAL:
computeShadowCameraDirectional(
lightData.elementAt<FScene::DIRECTION>(index),
cameraInfo, params, cascadeParams);
break;
case LightType::FOCUSED_SPOT:
case LightType::SPOT:
computeShadowCameraSpot(
lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz,
lightData.elementAt<FScene::DIRECTION>(index), lcm.getSpotLightOuterCone(li),
lightData.elementAt<FScene::POSITION_RADIUS>(index).w, cameraInfo, params);
break;
case LightType::POINT:
break;
// Adjust the camera's projection for the light's shadowFar
mat4f cullingProjection(camera.cullingProjection);
if (params.options.shadowFar > 0.0f) {
float n = camera.zn;
float f = params.options.shadowFar;
// orthographic projection
assert_invariant(std::abs(cullingProjection[2].w) <= std::numeric_limits<float>::epsilon());
cullingProjection[2].z = 2.0f / (n - f);
cullingProjection[3].z = (f + n) / (n - f);
}
}
void ShadowMap::computeShadowCameraDirectional(
float3 const& dir, ShadowCameraInfo const& camera,
FLightManager::ShadowParams const& params,
SceneInfo cascadeParams) noexcept {
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
/*
* Compute the light's model matrix
*/
// 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 mater for a directional light.
// 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 = getLightViewMatrix({}, dir);
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(direction);
const Aabb wsShadowCastersVolume = cascadeParams.wsShadowCastersVolume;
const Aabb wsShadowReceiversVolume = cascadeParams.wsShadowReceiversVolume;
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo);
const Aabb wsShadowCastersVolume = sceneInfo.wsShadowCastersVolume;
const Aabb wsShadowReceiversVolume = sceneInfo.wsShadowReceiversVolume;
if (wsShadowCastersVolume.isEmpty() || wsShadowReceiversVolume.isEmpty()) {
mHasVisibleShadows = false;
return;
@@ -216,14 +143,14 @@ void ShadowMap::computeShadowCameraDirectional(
// view frustum vertices in world-space
float3 wsViewFrustumVertices[8];
computeFrustumCorners(wsViewFrustumVertices,
camera.model * FCamera::inverseProjection(camera.projection),
cascadeParams.csNearFar);
camera.model * FCamera::inverseProjection(cullingProjection),
sceneInfo.csNearFar);
// we use aligned_storage<> here to avoid the default initialization of std::array<>
std::aligned_storage<sizeof(FrustumBoxIntersection)>::type localStorage;
std::aligned_storage<sizeof(FrustumBoxIntersection)>::type localStorage; // NOLINT(cppcoreguidelines-pro-type-member-init)
FrustumBoxIntersection& wsClippedShadowReceiverVolume{ reinterpret_cast<FrustumBoxIntersection&>(localStorage) };
// compute the intersection of the shadow receivers volume with the view volume
// compute the intersection of the shadow receivers' volume with the view volume
// in world space. This returns a set of points on the convex-hull of the intersection.
size_t vertexCount = intersectFrustumWithBox(wsClippedShadowReceiverVolume,
wsViewFrustumVertices, wsShadowReceiversVolume);
@@ -243,27 +170,27 @@ void ShadowMap::computeShadowCameraDirectional(
*/
Aabb lsLightFrustumBounds;
if (!USE_DEPTH_CLAMP) {
if constexpr (!USE_DEPTH_CLAMP) {
// near plane from shadow caster volume
lsLightFrustumBounds.max.z = cascadeParams.lsNearFar[0];
lsLightFrustumBounds.max.z = sceneInfo.lsNearFar[0];
}
for (size_t i = 0; i < vertexCount; ++i) {
// far: figure out farthest shadow receivers
// far: figure out the farthest shadow receivers
float3 v = mat4f::project(MvAtOrigin, wsClippedShadowReceiverVolume[i]);
lsLightFrustumBounds.min.z = std::min(lsLightFrustumBounds.min.z, v.z);
if (USE_DEPTH_CLAMP) {
// further tighten to the shadow receiver volume
if constexpr (USE_DEPTH_CLAMP) {
// tighten the shadow receiver volume further
lsLightFrustumBounds.max.z = std::max(lsLightFrustumBounds.max.z, v.z);
}
}
if (mEngine.debug.shadowmap.far_uses_shadowcasters) {
// far: closest of the farthest shadow casters and receivers
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, cascadeParams.lsNearFar[1]);
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, sceneInfo.lsNearFar[1]);
}
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
// that znear = 0, this is only need for VSM, but doesn't hurt PCF.
const mat4f Mv = getLightViewMatrix(dir * -lsLightFrustumBounds.max.z, dir);
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)
@@ -279,7 +206,7 @@ void ShadowMap::computeShadowCameraDirectional(
float4 viewVolumeBoundingSphere = {};
if (params.options.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
// 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
@@ -340,7 +267,7 @@ void ShadowMap::computeShadowCameraDirectional(
LMpMv = L * MpMv;
W = applyLISPSM(Wp, camera, params, LMpMv,
wsClippedShadowReceiverVolume, vertexCount, dir);
wsClippedShadowReceiverVolume, vertexCount, direction);
}
/*
@@ -393,7 +320,8 @@ void ShadowMap::computeShadowCameraDirectional(
if (params.options.stable) {
// Use the world origin as reference point, fixed w.r.t. the camera
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, 1.0f / mShadowMapInfo.shadowDimension);
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
1.0f / mShadowMapInfo.shadowDimension);
}
const mat4f F(mat4f::row_major_init {
@@ -411,7 +339,7 @@ void ShadowMap::computeShadowCameraDirectional(
const mat4f S = F * WLMpMv;
// Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transform a world-space vertex to a texture coordinate in the shadow-map
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const mat4 MbMt = getTextureCoordsMapping();
const mat4f St = mat4f(MbMt * S);
@@ -426,14 +354,14 @@ void ShadowMap::computeShadowCameraDirectional(
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, zfar);
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
}
// We apply the constant bias in world space (as opposed to light-space) to account
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
// by baking it in the shadow-map itself.
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(dir * constantBias);
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so
// that the cameraPosition uniform gets set correctly.
@@ -451,43 +379,68 @@ void ShadowMap::computeShadowCameraDirectional(
}
}
void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
FLightManager::ShadowParams const& params) noexcept {
void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo& sceneInfo) noexcept {
mShadowMapInfo = shadowMapInfo;
auto& lcm = mEngine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
// TODO: correctly compute if this spot light has any visible shadows.
mHasVisibleShadows = true;
/*
* Compute the light models matrix.
* Compute the light model matrix.
*/
// Choose a reasonable value for the near plane.
const float nearPlane = radius * (1.0f / 1024.0f);
const float farPlane = radius;
const mat4f Mv = getLightViewMatrix(position, dir);
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo, mShadowMapInfo.spotIndex);
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2, 1.0f, nearPlane, farPlane,
mat4f::Fov::HORIZONTAL);
const mat4f MpMv(Mp * Mv);
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
// Final shadow transform
const mat4f S = MpMv;
const mat4 MbMt = getTextureCoordsMapping();
const mat4f St = mat4f(MbMt * S);
mTexelSizeWs = texelSizeWorldSpace(Mp, mat4f(MbMt));
// TODO: focus projection
// 1) focus on the casters
// 2) additionally focus that on intersection of view & receivers
// Alternatively,
// Project receivers, casters and view onto near plane,
// compute intersection of that which gives the l,r,t,b planes
// For spotlights, we store the texel size at 1 world unit
// The size of a texel in world unit is given by: (near/dimension) / lightspace.z,
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// Note: this would not work with LISPSM, which warps the texture space.
mTexelSizeWs = 0.5f * nearPlane / float(mShadowMapInfo.shadowDimension);
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, farPlane);
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(dir * constantBias);
const mat4f b = mat4f::translation(direction * constantBias);
const mat4f Sb = S * b;
// It's important to set the light camera's model matrix separately from its projection, so that
@@ -505,8 +458,8 @@ void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::floa
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
}
mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
mat4f const& LMpMv,
FrustumBoxIntersection const& wsShadowReceiversVolume, size_t vertexCount,
float3 const& dir) {
@@ -514,7 +467,7 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
const float LoV = dot(camera.getForwardVector(), dir);
const float sinLV = std::sqrt(std::max(0.0f, 1.0f - LoV * LoV));
// Virtual near plane -- the default is 1m, can be changed by the user.
// Virtual near plane -- the default is 1 m, can be changed by the user.
// The virtual near plane prevents too much resolution to be wasted in the area near the eye
// where shadows might not be visible (e.g. a character standing won't see shadows at her feet).
const float dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn);
@@ -528,8 +481,8 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
// compute n and f, the near and far planes coordinates of Wp (warp space).
// It's found by looking down the Y axis in light space (i.e. -Z axis of Wp,
// i.e. the axis orthogonal to the light direction) and taking the min/max
// of the shadow receivers volume.
// Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y axis)
// of the shadow receivers' volume.
// Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y-axis)
const float2 nf = computeNearFarOfWarpSpace(LMpMv, wsShadowReceiversVolume.data(), vertexCount);
const float n = nf[0]; // near plane coordinate of Mp (light space)
const float f = nf[1]; // far plane coordinate of Mp (light space)
@@ -563,10 +516,10 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
const float3 lsCameraPosition = mat4f::project(LMpMv, camera.getPosition());
const float3 p = {
// Another option here is to use lsShadowReceiversCenter.x, which skews less the
// x axis. Doesn't seem to make a big difference in the end.
// x-axis. Doesn't seem to make a big difference in the end.
lsCameraPosition.x,
n - nopt,
// note: various papers suggest to use the shadow receiver's center z coordinate in light
// note: various papers suggest using the shadow receiver's center z coordinate in light
// space, i.e. to center "vertically" on the shadow receiver volume.
// e.g. (LMpMv * wsShadowReceiversVolume.center()).z
// However, simply using 0, guarantees to be centered on the light frustum, which itself
@@ -629,22 +582,22 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
return mat4(Mf * Mb * Mv * Mt);
}
math::mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float zfar) noexcept {
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
const mat4f& Mv, float znear, float zfar) noexcept {
// The lightSpacePcf matrix transforms coordinates from world space into (u, v, z) coordinates,
// where (u, v) are used to access the shadow map, and z is the (non linear) PCF comparison
// where (u, v) are used to access the shadow map, and z is the (non-linear) PCF comparison
// value [0, 1].
//
// For VSM, we want to leave the z coordinate in linear light space, normalized between [0, 1]
// (the normalization factor is therefore -1/zfar).
//
// For VSM, we want to leave the z coordinate in linear light-space, normalized between [0, 1],
// i.e. remapping [near, far] to [0, 1].
// When sampling a VSM shadow map, the shader follows suit, and doesn't divide by w for the z
// coordinate. See getters.fs.
math::mat4f lightSpaceVsm{ lightSpacePcf };
lightSpaceVsm[0].z = Mv[0].z * (-1.0f / zfar);
lightSpaceVsm[1].z = Mv[1].z * (-1.0f / zfar);
lightSpaceVsm[2].z = Mv[2].z * (-1.0f / zfar);
lightSpaceVsm[3].z = Mv[3].z * (-1.0f / zfar);
// coordinate. See shadowing.fs.
// compute z' = -(Mv * position).z / (far - near) - (near / (far - near))
const float scale = 1.0f / (zfar - znear);
mat4f lightSpaceVsm{ lightSpacePcf };
lightSpaceVsm[0].z = -Mv[0].z * scale;
lightSpaceVsm[1].z = -Mv[1].z * scale;
lightSpaceVsm[2].z = -Mv[2].z * scale;
lightSpaceVsm[3].z = (-Mv[3].z - znear) * scale;
return lightSpaceVsm;
}
@@ -663,7 +616,7 @@ mat4f ShadowMap::warpFrustum(float n, float f) noexcept {
return Wp;
}
math::mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept {
mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept {
const float d = far - near;
mat4f m;
m[2][2] = -2 / d;
@@ -694,7 +647,7 @@ float2 ShadowMap::computeNearFarOfWarpSpace(mat4f const& lightView,
float2 nearFar = { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
#pragma nounroll
for (size_t i = 0; i < count; i++) {
// we're on the y axis in light space (looking down to +y)
// we're on the y-axis in light space (looking down to +y)
float c = mat4f::project(lightView, wsVertices[i]).y;
nearFar.x = std::min(nearFar.x, c);
nearFar.y = std::max(nearFar.y, c);
@@ -805,7 +758,7 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
const float2 r = 2.0f * shadowMapResolution;
o -= fmod(o, r);
// This offsets the texture coordinates so it has a fixed offset w.r.t the world
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world
const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s;
o -= fmod(lsOrigin, r);
}
@@ -854,7 +807,7 @@ size_t ShadowMap::intersectFrustumWithBox(
// b) add the scene's vertices that are known to be inside the view frustum
//
// We need to handle the case where a corner of the box lies exactly on a plane of
// the frustum. This actually happens often due to fitting light-space
// the frustum. This actually often happens due to fitting light-space
// We fudge the distance to the plane by a small amount.
#pragma nounroll
for (float3 p : wsSceneReceiversCorners) {
@@ -988,7 +941,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
// The Jacobian of the transformation from texture-to-world is the matrix itself for
// orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic.
// The two first columns give us the how a texel maps in world-space.
// The two first columns give us how a texel maps in world-space.
const float ures = 1.0f / mShadowMapInfo.shadowDimension;
const float vres = 1.0f / mShadowMapInfo.shadowDimension;
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
@@ -1001,7 +954,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const noexcept {
// Here we compute the Jacobian of inverse(MbMtF * Wp).
// The expression below has been computed with Mathematica. However, it's not very hard,
// albeit error prone, to do it by hand because MbMtF is a linear transform.
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
//
// Because we're only interested in the length of the columns of the Jacobian, we can use
@@ -1015,10 +968,10 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
const float ures = 1.0f / mShadowMapInfo.shadowDimension;
const float vres = 1.0f / mShadowMapInfo.shadowDimension;
const float dres = mShadowMapInfo.zResolution;
const float dres = 1.0f / 65536.0f;
constexpr bool JACOBIAN_ESTIMATE = false;
if (JACOBIAN_ESTIMATE) {
if constexpr (JACOBIAN_ESTIMATE) {
// this estimates the Jacobian -- this is a lot heavier. This is mostly for reference
// and testing.
const mat4f Si(inverse(MbMtF * Wp));
@@ -1063,7 +1016,6 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
return s;
}
template<typename Casters, typename Receivers>
void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
Casters casters, Receivers receivers) noexcept {
@@ -1075,19 +1027,81 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
float3 const* const UTILS_RESTRICT worldAABBExtent = soa.data<FScene::WORLD_AABB_EXTENT>();
uint8_t const* const UTILS_RESTRICT layers = soa.data<FScene::LAYERS>();
State const* const UTILS_RESTRICT visibility = soa.data<FScene::VISIBILITY_STATE>();
auto const* const UTILS_RESTRICT visibleMasks = soa.data<FScene::VISIBLE_MASK>();
size_t c = soa.size();
for (size_t i = 0; i < c; i++) {
if (layers[i] & visibleLayers) {
const Aabb aabb{ worldAABBCenter[i] - worldAABBExtent[i],
worldAABBCenter[i] + worldAABBExtent[i] };
if (visibility[i].castShadows) {
casters(aabb);
casters(aabb, visibleMasks[i]);
}
if (visibility[i].receiveShadows) {
receivers(aabb);
receivers(aabb, visibleMasks[i]);
}
}
}
}
void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
ShadowMap::SceneInfo& sceneInfo) {
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
const mat4f V = camera.view;
// Compute scene bounds in world space, as well as the light-space and view-space near/far planes
sceneInfo.wsShadowCastersVolume = {};
sceneInfo.wsShadowReceiversVolume = {};
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster, Culler::result_type) {
sceneInfo.wsShadowCastersVolume.min =
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
sceneInfo.wsShadowCastersVolume.max =
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
},
[&](Aabb receiver, Culler::result_type) {
sceneInfo.wsShadowReceiversVolume.min =
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
sceneInfo.wsShadowReceiversVolume.max =
max(sceneInfo.wsShadowReceiversVolume.max, receiver.max);
float2 nf = ShadowMap::computeNearFar(V, receiver);
sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x);
sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y);
}
);
}
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo) {
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster, Culler::result_type) {
float2 nf = ShadowMap::computeNearFar(Mv, caster);
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
},
[&](Aabb receiver, Culler::result_type) {
}
);
}
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo, uint16_t index) {
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster, Culler::result_type mask) {
if (mask & VISIBLE_SPOT_SHADOW_RENDERABLE_N(index)) {
float2 nf = ShadowMap::computeNearFar(Mv, caster);
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
}
},
[&](Aabb receiver, Culler::result_type) {
}
);
}
} // namespace filament

View File

@@ -44,10 +44,6 @@ public:
~ShadowMap();
struct ShadowMapInfo {
// the smallest increment in depth precision
// e.g., for 16 bit depth textures, is this 1 / (2^16)
float zResolution = 0.0f;
// the dimension of the encompassing texture atlas
uint16_t atlasDimension = 0;
@@ -59,11 +55,19 @@ public:
// e.g., for a texture dimension of 512, shadowDimension would be 510
uint16_t shadowDimension = 0;
// This spot shadowmap index.
uint16_t spotIndex = 0;
// whether we're using vsm
bool vsm = false;
// polygon offset
backend::PolygonOffset polygonOffset{};
};
struct SceneInfo {
explicit SceneInfo(uint8_t visibleLayers) noexcept : visibleLayers(visibleLayers) { }
// The near and far planes, in clip space, to use for this shadow map
math::float2 csNearFar = { -1.0f, 1.0f };
@@ -71,32 +75,34 @@ public:
// light's near/far expressed in light-space, calculated from the scene's content
// assuming the light is at the origin.
math::float2 lsNearFar;
math::float2 lsNearFar{};
// Viewing camera's near/far expressed in view-space, calculated from the scene's content
math::float2 vsNearFar;
math::float2 vsNearFar{};
// World-space shadow-casters volume
Aabb wsShadowCastersVolume;
// World-space shadow-receivers volume
Aabb wsShadowReceiversVolume;
uint8_t visibleLayers;
};
static math::mat4f getLightViewMatrix(
math::float3 position, math::float3 direction) noexcept;
// Call once per frame to populate the CascadeParameters struct, then pass to update().
// This computes values constant across all cascades.
static void computeSceneInfo(math::float3 dir,
FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers,
SceneInfo& sceneInfo);
static math::mat4f getDirectionalLightViewMatrix(
math::float3 direction, math::float3 position = {}) noexcept;
// Call once per frame if the light, scene (or visible layers) or camera changes.
// This computes the light's camera.
void update(const FScene::LightSoa& lightData, size_t index,
void updateDirectional(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept;
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
void updateSpot(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
void render(FScene const& scene, utils::Range<uint32_t> range,
FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo,
@@ -118,22 +124,21 @@ public:
// use only for debugging
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; }
backend::PolygonOffset getPolygonOffset() const noexcept { return mShadowMapInfo.polygonOffset; }
// Call once per frame to populate the SceneInfo struct, then pass to update().
// This computes values constant across all shadow maps.
static void initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
ShadowMap::SceneInfo& sceneInfo);
// Update SceneInfo struct for a given light
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo);
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo, uint16_t index);
private:
struct ShadowCameraInfo {
math::mat4f projection;
math::mat4f model;
math::mat4f view;
math::mat4f worldOrigin;
float zn = 0;
float zf = 0;
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
math::float3 getForwardVector() const noexcept {
return -normalize(model[2].xyz); // the camera looks towards -z
}
};
struct Segment {
uint8_t v0, v1;
};
@@ -145,16 +150,8 @@ private:
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
using FrustumBoxIntersection = std::array<math::float3, 64>;
void computeShadowCameraDirectional(
math::float3 const& direction,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
SceneInfo cascadeParams) noexcept;
void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
FLightManager::ShadowParams const& params) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
const math::mat4f& LMpMv,
FrustumBoxIntersection const& wsShadowReceiverVolume, size_t vertexCount,
const math::float3& dir);
@@ -213,8 +210,8 @@ private:
math::mat4 getTextureCoordsMapping() const noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpace,
const math::mat4f& Mv, float zfar) noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float znear, float zfar) noexcept;
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept;
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept;
@@ -239,9 +236,8 @@ private:
float mTexelSizeWs = 0.0f; // 4
// set-up in update()
ShadowMapInfo mShadowMapInfo; // 12
ShadowMapInfo mShadowMapInfo; // 20
bool mHasVisibleShadows = false; // 1
backend::PolygonOffset mPolygonOffset{}; // 8
FEngine& mEngine; // 8
const bool mClipSpaceFlipped; // 1

View File

@@ -34,7 +34,7 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguidelines-pro-type-member-init)
// initialize our ShadowMap array in-place
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
@@ -57,10 +57,23 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
FEngine& engine, FView& view,
TypedUniformBuffer<ShadowUib>& shadowUb, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData) noexcept {
calculateTextureRequirements(engine, view, lightData);
ShadowTechnique shadowTechnique = {};
shadowTechnique |= updateCascadeShadowMaps(engine, view, renderableData, lightData);
shadowTechnique |= updateSpotShadowMaps(engine, view, shadowUb, renderableData, lightData);
calculateTextureRequirements(engine, view, lightData);
ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers());
// Compute scene-dependent values shared across all shadow maps
ShadowMap::initSceneInfo(
*view.getScene(), view.getCameraInfo(),
sceneInfo);
shadowTechnique |= updateCascadeShadowMaps(
engine, view, renderableData, lightData, sceneInfo);
shadowTechnique |= updateSpotShadowMaps(
engine, view, renderableData, lightData, sceneInfo, shadowUb);
return shadowTechnique;
}
@@ -95,7 +108,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
const TextureFormat vsmTextureFormat = TextureFormat::RG16F;
// make a copy here, because it's a very small structure
const TextureRequirements textureRequirements = mTextureRequirements;
const TextureAtlasRequirements textureRequirements = mTextureAtlasRequirements;
assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS);
struct ShadowPass {
@@ -163,9 +176,9 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f };
struct ShadowPassData {
FrameGraphId<FrameGraphTexture> tempBlurSrc; // temporary shadowmap when blurring
uint32_t blurRt;
uint32_t shadowRt;
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
uint32_t blurRt{};
uint32_t shadowRt{};
};
auto shadows = prepareShadowPass.getData().shadows;
@@ -240,7 +253,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
renderTargetDesc.clearFlags = TargetBufferFlags::DEPTH;
}
// finally create the shadowmap render target -- one per layer.
// finally, create the shadowmap render target -- one per layer.
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
},
[=, &engine, &view](FrameGraphResources const& resources,
@@ -261,7 +274,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
view.prepareCamera(cameraInfo);
// We set a viewport with a 1-texel border for when we index outside of the
// We set a viewport with a 1-texel border for when we index outside the
// texture.
// DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too.
// see: ShadowMap::getTextureCoordsMapping()
@@ -276,7 +289,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
view.prepareViewport(viewport);
// set uniforms needed to render this ShadowMap
// Currently these uniforms are owned by View and are global, but eventully
// Currently these uniforms are owned by View and are global, but eventually
// this will set a separate per shadowmap UBO
view.prepareShadowMap();
@@ -298,7 +311,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
const float sigma = (blurWidth + 1.0f) / 6.0f;
size_t kernelWidth = std::ceil((blurWidth - 5.0f) / 4.0f);
kernelWidth = kernelWidth * 4 + 5;
const float ratio = (kernelWidth + 1.0f) / sigma;
const float ratio = float(kernelWidth + 1) / sigma;
ppm.gaussianBlurPass(fg,
shadowPass->tempBlurSrc, 0,
shadows, 0, layer,
@@ -321,49 +334,44 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
fg.getBlackboard().put("shadows", shadows);
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
FEngine& engine, FView& view, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData) noexcept {
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo) noexcept {
FScene* scene = view.getScene();
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
uint8_t visibleLayers = view.getVisibleLayers();
const uint16_t textureSize = mTextureRequirements.size;
auto& lcm = engine.getLightManager();
FLightManager::Instance directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
LightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
ShadowMap::SceneInfo sceneInfo;
const ShadowMap::ShadowMapInfo shadowMapInfo{
.atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options.mapSize),
.shadowDimension = uint16_t(options.mapSize - 2u),
.vsm = view.hasVsm(),
.polygonOffset = { // handle reversed Z
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
}
};
if (!mCascadeShadowMaps.empty()) {
// Compute scene-dependent values shared across all cascades
const float3 dir = lightData.elementAt<FScene::DIRECTION>(0);
ShadowMap::computeSceneInfo(dir,
*scene, viewingCameraInfo, visibleLayers, sceneInfo);
// 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.
ShadowMapEntry& entry = mCascadeShadowMaps[0];
ShadowMap& map = entry.getShadowMap();
const size_t textureDimension = entry.getShadowOptions()->mapSize;
const ShadowMap::ShadowMapInfo shadowMapInfo {
.zResolution = mTextureZResolution,
.atlasDimension = textureSize,
.textureDimension = (uint16_t)textureDimension,
.shadowDimension = (uint16_t)(textureDimension - 2),
.vsm = view.hasVsm()
};
ShadowMap& shadowMap = entry.getShadowMap();
map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
shadowMap.updateDirectional(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
Frustum const& frustum = map.getCamera().getCullingFrustum();
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
// note: normalBias is ignored for VSM
const float normalBias = lcm.getShadowNormalBias(0);
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(0);
// Set shadowBias, using the first directional cascade.
const float texelSizeWorldSpace = map.getTexelSizeWorldSpace();
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * texelSizeWorldSpace, 0 };
}
@@ -414,7 +422,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
ShadowTechnique shadowTechnique{};
uint32_t directionalShadowsMask = 0;
uint32_t cascadeHasVisibleShadows = 0;
float screenSpaceShadowDistance = 0.0f;
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
auto& entry = mCascadeShadowMaps[i];
@@ -422,16 +429,12 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
ShadowMap& shadowMap = entry.getShadowMap();
assert_invariant(entry.getLightIndex() == 0);
const size_t textureDimension = entry.getShadowOptions()->mapSize;
const ShadowMap::ShadowMapInfo shadowMapInfo{
.zResolution = mTextureZResolution,
.atlasDimension = textureSize,
.textureDimension = (uint16_t)textureDimension,
.shadowDimension = (uint16_t)(textureDimension - 2),
.vsm = view.hasVsm()
};
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
shadowMap.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
shadowMap.updateDirectional(lightData, 0,
viewingCameraInfo, shadowMapInfo,
*scene, sceneInfo);
if (shadowMap.hasVisibleShadows()) {
mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
@@ -440,7 +443,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
}
// screen-space contact shadows for the directional light
screenSpaceShadowDistance = options.maxShadowDistance;
float screenSpaceShadowDistance = options.maxShadowDistance;
if (options.screenSpaceContactShadows) {
shadowTechnique |= ShadowTechnique::SCREEN_SPACE;
}
@@ -467,63 +470,83 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
return shadowTechnique;
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(
FEngine& engine, FView& view, TypedUniformBuffer<ShadowUib>& shadowUb,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept {
ShadowTechnique shadowTechnique{};
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
const uint16_t textureSize = mTextureRequirements.size;
// shadow-map shadows for point/spot lights
auto& lcm = engine.getLightManager();
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
// shadow-map shadows for point/spotlights
ShadowTechnique shadowTechnique{};
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
auto& entry = mSpotShadowMaps[i];
// compute the frustum for this light
ShadowMap& shadowMap = entry.getShadowMap();
size_t l = entry.getLightIndex();
const size_t lightIndex = entry.getLightIndex();
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
FLightManager::ShadowParams params = lcm.getShadowParams(li);
const size_t textureDimension = entry.getShadowOptions()->mapSize;
const ShadowMap::ShadowMapInfo layout{
.zResolution = mTextureZResolution,
.atlasDimension = textureSize,
.textureDimension = (uint16_t)textureDimension,
.shadowDimension = (uint16_t)(textureDimension - 2),
.vsm = view.hasVsm()
FLightManager::ShadowOptions const* const options = entry.getShadowOptions();
const ShadowMap::ShadowMapInfo shadowMapInfo{
.atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options->mapSize),
.shadowDimension = uint16_t(options->mapSize - 2u),
.spotIndex = uint16_t(i),
.vsm = view.hasVsm(),
.polygonOffset = { // handle reversed Z
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
}
};
shadowMap.update(lightData, l, viewingCameraInfo, layout, {});
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
// for spotlights, we cull shadow casters first because we already know the frustum,
// this will help us find better near/far plane later
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
const auto direction = lightData.elementAt<FScene::DIRECTION>(lightIndex);
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, 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);
// Cull shadow casters
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
shadowMap.updateSpot(lightData, lightIndex,
viewingCameraInfo, shadowMapInfo,
*view.getScene(), sceneInfo);
if (shadowMap.hasVisibleShadows()) {
// Cull shadow casters
auto& s = shadowUb.edit();
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
shadowInfo[lightIndex].castsShadows = true;
shadowInfo[lightIndex].index = i;
shadowInfo[lightIndex].layer = entry.getLayer();
s.spotLightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
shadowInfo[l].castsShadows = true;
shadowInfo[l].index = i;
shadowInfo[l].layer = mSpotShadowMaps[i].getLayer();
// note: normalBias is ignored for VSM
const float3 dir = lightData.elementAt<FScene::DIRECTION>(l);
// note: normalBias is set to zero for VSM
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
const float normalBias = lcm.getShadowNormalBias(light);
s.directionShadowBias[i] = float4{ dir, normalBias * texelSizeWorldSpace };
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
auto& s = shadowUb.edit();
s.shadows[i].direction = direction;
s.shadows[i].normalBias = normalBias * texelSizeWorldSpace;
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
}
}
// screen-space contact shadows for point/spot lights
auto *pInstance = lightData.data<FScene::LIGHT_INSTANCE>();
// screen-space contact shadows for point/spotlights
auto *pLightInstances = lightData.data<FScene::LIGHT_INSTANCE>();
for (size_t i = 0, c = lightData.size(); i < c; i++) {
// screen-space contact shadows
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pInstance[i]);
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pLightInstances[i]);
if (shadowOptions.screenSpaceContactShadows) {
shadowTechnique |= ShadowTechnique::SCREEN_SPACE;
shadowInfo[i].contactShadows = true;
@@ -539,7 +562,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
// Lay out the shadow maps. For now, we take the largest requested dimension and allocate a
// texture of that size. Each cascade / shadow map gets its own layer in the array texture.
// The directional shadow cascades start on layer 0, followed by spot lights.
// The directional shadow cascades start on layer 0, followed by spotlights.
uint8_t layer = 0;
uint32_t maxDimension = 0;
for (auto& entry : mCascadeShadowMaps) {
@@ -569,7 +592,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
mipLevels = std::max(1, FTexture::maxLevelCount(maxDimension) - lowMipmapLevel);
}
mTextureRequirements = {
mTextureAtlasRequirements = {
(uint16_t)maxDimension,
layersNeeded,
mipLevels

View File

@@ -114,18 +114,21 @@ public:
private:
struct TextureRequirements {
// Atlas requirements, updated in ShadowMapManager::update(),
// consumed in ShadowMapManager::render()
struct TextureAtlasRequirements {
uint16_t size = 0;
uint8_t layers = 0;
uint8_t levels = 0;
} mTextureRequirements;
} mTextureAtlasRequirements;
ShadowTechnique updateCascadeShadowMaps(FEngine& engine, FView& view,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo) noexcept;
ShadowTechnique updateSpotShadowMaps(FEngine& engine, FView& view,
TypedUniformBuffer<ShadowUib>& shadowUb,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
ShadowTechnique updateSpotShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;
@@ -199,7 +202,6 @@ private:
// TODO: make it an option.
// TODO: iOS does not support the DEPTH16 texture format.
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
float mTextureZResolution = 1.0f / (1u << 16u);
ShadowMappingUniforms mShadowMappingUniforms;

View File

@@ -53,6 +53,9 @@ namespace filament {
using namespace backend;
using namespace math;
static constexpr float PID_CONTROLLER_Ki = 0.002f;
static constexpr float PID_CONTROLLER_Kd = 0.0f;
FView::FView(FEngine& engine)
: mFroxelizer(engine),
mPerViewUniforms(engine),
@@ -60,9 +63,30 @@ FView::FView(FEngine& engine)
DriverApi& driver = engine.getDriverApi();
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.view.camera_at_origin",
&engine.debug.view.camera_at_origin);
// Integral term is used to fight back the dead-band below, we limit how much it can act.
mPidController.setIntegralLimits(-100.0f, 100.0f);
// dead-band, 1% for scaling down, 5% for scaling up. This stabilizes all the jitters.
mPidController.setOutputDeadBand(-0.01f, 0.05f);
#ifndef NDEBUG
debugRegistry.registerDataSource("d.view.frame_info",
mDebugFrameHistory.data(), mDebugFrameHistory.size());
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
// default parameters for debugging UI
engine.debug.view.pid.kp = 1.0f - std::exp(-1.0f / 8.0f);
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
mPidController.setParallelGains(
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
#endif
// allocate ubos
mLightUbh = driver.createBufferObject(CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
@@ -131,7 +155,15 @@ void FView::setDynamicLightingOptions(float zLightNear, float zLightFar) noexcep
mFroxelizer.setOptions(zLightNear, zLightFar);
}
float2 FView::updateScale(FrameInfo const& info) noexcept {
float2 FView::updateScale(FEngine& engine,
FrameInfo const& info,
Renderer::FrameRateOptions const& frameRateOptions,
Renderer::DisplayInfo const& displayInfo) noexcept {
// scale factor returned to the caller is modified so the scaled viewport is rounded to
// 8 pixels. The internal scale factor, mScale, doesn't have this rounding.
float2 roundedScale = mScale;
DynamicResolutionOptions const& options = mDynamicResolution;
if (options.enabled) {
if (!UTILS_UNLIKELY(info.valid)) {
@@ -140,10 +172,42 @@ float2 FView::updateScale(FrameInfo const& info) noexcept {
return mScale;
}
// scaling factor we need to apply on the whole surface
const float scale = info.scale;
const float w = mViewport.width;
const float h = mViewport.height;
#ifndef NDEBUG
const float Kp = engine.debug.view.pid.kp;
const float Ki = engine.debug.view.pid.ki;
const float Kd = engine.debug.view.pid.kd;
#else
const float Kp = (1.0f - std::exp(-frameRateOptions.scaleRate));
const float Ki = PID_CONTROLLER_Ki;
const float Kd = PID_CONTROLLER_Kd;
#endif
mPidController.setParallelGains(Kp, Ki, Kd);
// all values in ms below
using std::chrono::duration;
const float dt = 1.0f; // we don't really need dt here, setting it to 1, means our parameters are in "frames"
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
float measured = duration<float, std::milli>{ info.denoisedFrameTime }.count();
float out = mPidController.update(measured / targetWithHeadroom, 1.0f, dt);
// maps pid command to a scale (absolute or relative, see below)
const float command = out < 0.0f ? (1.0f / (1.0f - out)) : (1.0f + out);
/*
* There is two ways we can control the scale factor, either by having the PID controller
* output a new scale factor directly (like a "position" control), or having it evaluate
* a relative scale factor (like a "velocity" control).
* More experimentation is needed to figure out which works better in more cases.
*/
// direct scaling ("position" control)
//const float scale = command;
// relative scaling ("velocity" control)
const float scale = mScale.x * mScale.y * command;
const float w = float(mViewport.width);
const float h = float(mViewport.height);
if (scale < 1.0f && !options.homogeneousScaling) {
// figure out the major and minor axis
const float major = std::max(w, h);
@@ -159,7 +223,7 @@ float2 FView::updateScale(FrameInfo const& info) noexcept {
// if we have some scaling capacity left, scale homogeneously
const float homogeneousScale = scale / (majorScale * minorScale);
// finally write the scale factors
// finally, write the scale factors
float& majorRef = w > h ? mScale.x : mScale.y;
float& minorRef = w > h ? mScale.y : mScale.x;
majorRef = std::sqrt(homogeneousScale) * majorScale;
@@ -169,32 +233,44 @@ float2 FView::updateScale(FrameInfo const& info) noexcept {
mScale = std::sqrt(scale);
}
// always clamp to the min/max scale range
const auto s = mScale;
mScale = clamp(s, options.minScale, options.maxScale);
// disable the integration term when we're outside the controllable range
// (i.e. we clamped). This help not to have to wait too long for the Integral term
// to kick in after a clamping event.
mPidController.setIntegralInhibitionEnabled(mScale != s);
// now tweak the scaling factor to get multiples of 8 (to help quad-shading)
// i.e. 8x8=64 fragments, to try to help with warp sizes.
mScale = (floor(mScale * float2{ w, h } / 8) * 8) / float2{ w, h };
// always clamp to the min/max scale range
mScale = clamp(mScale, options.minScale, options.maxScale);
//#define DEBUG_DYNAMIC_RESOLUTION
#if defined(DEBUG_DYNAMIC_RESOLUTION)
static int sLogCounter = 15;
if (!--sLogCounter) {
sLogCounter = 15;
slog.d << info.denoisedFrameTime.count() * 1000.0f << " ms"
<< ", " << info.smoothedWorkLoad
<< ", " << mScale.x
<< ", " << mScale.y
<< ", " << mViewport.width * mScale.x
<< ", " << mViewport.height * mScale.y
<< io::endl;
}
#endif
roundedScale.x = mScale.x == 1.0f ? 1.0f : (std::floor(mScale.x * float{ w } / 8) * 8) / float{ w };
roundedScale.y = mScale.y == 1.0f ? 1.0f : (std::floor(mScale.y * float{ h } / 8) * 8) / float{ h };
} else {
mScale = 1.0f;
roundedScale = 1.0f;
}
return mScale;
#ifndef NDEBUG
// only for debugging...
using duration_ms = std::chrono::duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(mDebugFrameHistory.begin() + 1,
mDebugFrameHistory.end(), mDebugFrameHistory.begin());
mDebugFrameHistory.back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = std::chrono::duration_cast<duration_ms>(info.frameTime).count(),
.frameTimeDenoised = std::chrono::duration_cast<duration_ms>(info.denoisedFrameTime).count(),
.scale = mScale.x * mScale.y,
.pid_e = mPidController.getError(),
.pid_i = mPidController.getIntegral(),
.pid_d = mPidController.getDerivative()
};
#endif
return roundedScale;
}
void FView::setVisibleLayers(uint8_t select, uint8_t values) noexcept {
@@ -240,21 +316,21 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
// when we get here all the lights should be visible
assert_invariant(lightData.elementAt<FScene::VISIBILITY>(l));
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
if (UTILS_LIKELY(!light)) {
if (UTILS_LIKELY(!li)) {
continue; // invalid instance
}
if (UTILS_LIKELY(!lcm.isShadowCaster(light))) {
if (UTILS_LIKELY(!lcm.isShadowCaster(li))) {
continue; // doesn't cast shadows
}
if (UTILS_LIKELY(!lcm.isSpotLight(light))) {
continue; // is not a spot-light (we're not supporting point-lights yet)
if (UTILS_LIKELY(!lcm.isSpotLight(li))) {
continue; // is not a spot-li (we're not supporting point-lights yet)
}
const auto& shadowOptions = lcm.getShadowOptions(light);
const auto& shadowOptions = lcm.getShadowOptions(li);
mShadowMapManager.addSpotShadowMap(l, &shadowOptions);
++shadowCastingSpotCount;
if (shadowCastingSpotCount > CONFIG_MAX_SHADOW_CASTING_SPOTS - 1) {

View File

@@ -24,8 +24,8 @@ Viewport Viewport::scale(math::float2 s) const noexcept {
// round to nearest to avoid overlapping viewports
// this could result to empty viewport, though.
float l = std::floor(0.5f + s.x * left);
float b = std::floor(0.5f + s.y * bottom);
float l = std::floor(0.5f + s.x * float(left));
float b = std::floor(0.5f + s.y * float(bottom));
float r = std::floor(0.5f + s.x * float(left + width));
float t = std::floor(0.5f + s.y * float(bottom + height));

View File

@@ -352,23 +352,23 @@ void FLightManager::setFalloff(Instance i, float falloff) noexcept {
void FLightManager::setSpotLightCone(Instance i, float inner, float outer) noexcept {
auto& manager = mManager;
if (i && isSpotLight(i)) {
// clamp the inner/outer angles to pi
float innerClamped = std::min(std::abs(inner), f::PI_2);
float outerClamped = std::min(std::abs(outer), f::PI_2);
// clamp the inner/outer angles to [0.5 degrees, 90 degrees]
float innerClamped = std::clamp(std::abs(inner), 0.5f * f::DEG_TO_RAD, f::PI_2);
float outerClamped = std::clamp(std::abs(outer), 0.5f * f::DEG_TO_RAD, f::PI_2);
// outer must always be bigger than inner
outerClamped = std::max(innerClamped, outerClamped);
// inner must always be smaller than outer
innerClamped = std::min(innerClamped, outerClamped);
float cosOuter = fast::cos(outerClamped);
float cosInner = fast::cos(innerClamped);
float cosOuterSquared = cosOuter * cosOuter;
float scale = 1 / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float scale = 1.0f / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float offset = -cosOuter * scale;
SpotParams& spotParams = manager[i].spotParams;
spotParams.outerClamped = outerClamped;
spotParams.cosOuterSquared = cosOuterSquared;
spotParams.sinInverse = 1 / std::sqrt(1 - cosOuterSquared);
spotParams.sinInverse = 1.0f / std::sin(outerClamped);
spotParams.scaleOffset = { scale, offset };
// we need to recompute the luminous intensity

View File

@@ -24,7 +24,7 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <tsl/robin_map.h>
#include <unordered_map>
namespace filament {
@@ -34,8 +34,6 @@ class FDebugRegistry : public DebugRegistry {
public:
FDebugRegistry() noexcept;
PropertyArray getProperties() const noexcept;
bool hasProperty(const char* name) const noexcept;
void* getPropertyAddress(const char* name) noexcept;
@@ -58,10 +56,14 @@ public:
registerProperty(name, p, type);
}
void registerDataSource(utils::StaticString name, void const* data, size_t count) noexcept;
DataSource getDataSource(const char* name) const noexcept;
private:
void registerProperty(utils::StaticString name, void* p, Type type) noexcept;
std::vector<Property> mProperties;
tsl::robin_map<utils::StaticString, void*> mPropertyMap;
std::unordered_map<utils::StaticString, void*> mPropertyMap;
std::unordered_map<utils::StaticString, DataSource> mDataSourceMap;
};
FILAMENT_UPCAST(DebugRegistry)

View File

@@ -443,6 +443,11 @@ public:
} ssao;
struct {
bool camera_at_origin = true;
struct {
float kp = 0.0f;
float ki = 0.0f;
float kd = 0.0f;
} pid;
} view;
struct {
// When set to true, the backend will attempt to capture the next frame and write the

View File

@@ -22,6 +22,7 @@
#include "Allocators.h"
#include "FrameInfo.h"
#include "FrameSkipper.h"
#include "PostProcessManager.h"
#include "RenderPass.h"
#include "details/SwapChain.h"
@@ -29,9 +30,11 @@
#include "private/backend/DriverApiForward.h"
#include <fg2/FrameGraphId.h>
#include <fg2/FrameGraphTexture.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -61,7 +64,7 @@ class ShadowMap;
* A concrete implementation of the Renderer Interface.
*/
class FRenderer : public Renderer {
static constexpr size_t MAX_FRAMETIME_HISTORY = 32u;
static constexpr unsigned MAX_FRAMETIME_HISTORY = 32u;
public:
explicit FRenderer(FEngine& engine);
@@ -107,9 +110,9 @@ public:
FrameRateOptions& frameRateOptions = mFrameRateOptions;
frameRateOptions = options;
// History can't be more than 32 frames (~0.5s)
frameRateOptions.history = std::min(frameRateOptions.history,
uint8_t(MAX_FRAMETIME_HISTORY));
// History can't be more than 31 frames (~0.5s), make it odd.
frameRateOptions.history = std::min(frameRateOptions.history / 2u * 2u + 1u,
MAX_FRAMETIME_HISTORY);
// History must at least be 3 frames
frameRateOptions.history = std::max(frameRateOptions.history, uint8_t(3));

View File

@@ -106,7 +106,7 @@ public:
VISIBILITY_STATE, // 1 | visibility data of the component
SKINNING_BUFFER, // 8 | bones uniform buffer handle, count, offset
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
VISIBLE_MASK, // 1 | each bit represents a visibility in a pass
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass
MORPH_WEIGHTS, // 4 | floats for morphing
CHANNELS, // 1 | currently light channels only

View File

@@ -19,6 +19,8 @@
#include <filament/View.h>
#include <filament/Renderer.h>
#include "upcast.h"
#include "Allocators.h"
@@ -26,6 +28,7 @@
#include "FrameInfo.h"
#include "Froxelizer.h"
#include "PerViewUniforms.h"
#include "PIDController.h"
#include "ShadowMap.h"
#include "ShadowMapManager.h"
#include "TypedUniformBuffer.h"
@@ -270,7 +273,10 @@ public:
return mHasPostProcessPass;
}
math::float2 updateScale(FrameInfo const& info) noexcept;
math::float2 updateScale(FEngine& engine,
FrameInfo const& info,
Renderer::FrameRateOptions const& frameRateOptions,
Renderer::DisplayInfo const& displayInfo) noexcept;
void setDynamicResolutionOptions(View::DynamicResolutionOptions const& options) noexcept;
@@ -555,6 +561,7 @@ private:
const FColorGrading* mColorGrading = nullptr;
const FColorGrading* mDefaultColorGrading = nullptr;
PIDController mPidController;
DynamicResolutionOptions mDynamicResolution;
math::float2 mScale = 1.0f;
bool mIsDynamicResolutionSupported = false;
@@ -581,6 +588,10 @@ private:
mutable bool mNeedsShadowMap = false;
ShadowMapManager mShadowMapManager;
#ifndef NDEBUG
std::array<DebugRegistry::FrameHistory, 5*60> mDebugFrameHistory;
#endif
};
FILAMENT_UPCAST(View)

View File

@@ -0,0 +1,13 @@
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}

View File

@@ -79,8 +79,9 @@ fragment {
#include "../../../../shaders/src/dithering.fs"
#include "../../../../shaders/src/vignette.fs"
#include "colorGrading.fs"
void dummy(){ }
void dummy(){}
float starburst(const vec2 uv) {
// get an offset that continuously moves with the camera
@@ -123,68 +124,52 @@ vec3 bloom(const vec3 color) {
return result;
}
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}
vec3 resolveFragment(const ivec2 uv) {
return texelFetch(materialParams_colorBuffer, uv, 0).rgb;
}
vec4 resolveAlphaFragment(const ivec2 uv) {
return texelFetch(materialParams_colorBuffer, uv, 0);
}
vec4 resolve() {
vec4 resolveFragment(const ivec2 uv) {
#if POST_PROCESS_OPAQUE
vec4 color = vec4(resolveFragment(ivec2(getUV())), 1.0);
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
return vec4(texelFetch(materialParams_colorBuffer, uv, 0).rgb, 1.0);
#else
vec4 color = resolveAlphaFragment(ivec2(getUV()));
color.rgb /= color.a + FLT_EPS;
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb *= color.a + FLT_EPS;
#endif
vec4 color = texelFetch(materialParams_colorBuffer, uv, 0);
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
}
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = resolve();
if (materialParams.dithering > 0) {
vec4 dithered = dither(postProcess.color, materialParams.temporalNoise);
#if POST_PROCESS_OPAQUE
postProcess.color.rgb = dithered.rgb;
#else
postProcess.color = dithered;
#endif
vec4 color = resolveFragment(ivec2(getUV()));
// Bloom
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
color.rgb *= color.a + FLT_EPS;
#endif
// dithering
if (materialParams.dithering > 0) {
color = dither(color, materialParams.temporalNoise);
}
// kill alpha computations when opaque / fxaa luminance
#if POST_PROCESS_OPAQUE
color.a = 1.0;
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
#endif
postProcess.color = color;
}
}

View File

@@ -71,64 +71,51 @@ fragment {
#include "../../../../shaders/src/dithering.fs"
#include "../../../../shaders/src/vignette.fs"
#include "colorGrading.fs"
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
void dummy(){}
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}
vec3 resolveFragment(const ivec2 uv) {
return subpassLoad(materialParams_colorBuffer).rgb;
}
vec4 resolveAlphaFragment(const ivec2 uv) {
return subpassLoad(materialParams_colorBuffer);
}
vec4 resolve() {
vec4 resolveFragment() {
#if POST_PROCESS_OPAQUE
vec4 color = vec4(resolveFragment(ivec2(getUV())), 1.0);
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
#else
vec4 color = resolveAlphaFragment(ivec2(getUV()));
color.rgb /= color.a + FLT_EPS;
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb *= color.a + FLT_EPS;
vec4 color = subpassLoad(materialParams_colorBuffer);
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
return color;
}
void postProcess(inout PostProcessInputs postProcess) {
vec4 color = resolveFragment();
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
void postProcess(inout PostProcessInputs postProcess) {
vec4 color = resolve();
if (materialParams.dithering > 0) {
vec4 dithered = dither(color, materialParams.temporalNoise);
#if POST_PROCESS_OPAQUE
color.rgb = dithered.rgb;
#else
color = dithered;
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
color.rgb *= color.a + FLT_EPS;
#endif
}
postProcess.tonemappedOutput = color;
// dithering
if (materialParams.dithering > 0) {
color = dither(color, materialParams.temporalNoise);
}
// kill alpha computations when opaque / fxaa luminance
#if POST_PROCESS_OPAQUE
color.a = 1.0;
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
#endif
postProcess.tonemappedOutput = color;
}
}

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.12.9"
spec.version = "1.14.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.12.9/filament-v1.12.9-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.14.0/filament-v1.14.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -45,6 +45,7 @@ using namespace utils;
@implementation FILViewController {
CADisplayLink* _displayLink;
CFTimeInterval _startTime;
viewer::RemoteServer* _server;
viewer::AutomationEngine* _automation;
@@ -102,6 +103,7 @@ using namespace utils;
[self stopDisplayLink];
// Call our render method 60 times a second.
_startTime = CACurrentMediaTime();
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
_displayLink.preferredFramesPerSecond = 60;
[_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode];
@@ -230,7 +232,8 @@ using namespace utils;
auto* animator = self.modelView.animator;
if (animator) {
if (animator->getAnimationCount() > 0) {
animator->applyAnimation(0, CACurrentMediaTime());
CFTimeInterval elapsedTime = CACurrentMediaTime() - _startTime;
animator->applyAnimation(0, static_cast<float>(elapsedTime));
}
animator->updateBoneMatrices();
}

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 12;
static constexpr size_t MATERIAL_VERSION = 14;
/**
* Supported shading models

View File

@@ -88,7 +88,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::float4 userTime; // time(s), (double)time - (float)time, 0, 0
float iblRoughnessOneLevel; // level for roughness == 1
float cameraFar; // camera *culling* far-plane distance (projection far is at +inf)
float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf)
float refractionLodOffset;
// bit 0: directional (sun) shadow enabled
@@ -129,8 +129,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float vsmReserved0;
float lodBias;
float reserved1;
float reserved2;
float oneOverFarMinusNear; // 1 / (f-n), always positive
float nearOverFarMinusNear; // n / (f-n), always positive
float reserved3;
// bring PerViewUib to 2 KiB
@@ -164,13 +164,14 @@ static_assert(sizeof(PerRenderableUib) % 256 == 0, "sizeof(Transform) should be
struct LightsUib {
static constexpr utils::StaticString _name{ "LightsUniforms" };
math::float4 positionFalloff; // { float3(pos), 1/falloff^2 }
math::half4 color; // { half3(col), 0 }
math::half4 directionIES; // { half3(dir), IES index }
math::half2 spotScaleOffset; // { scale, offset }
float intensity; // float
uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer)
uint32_t channels; // 0x000c00ll (ll: light channels, c: caster)
math::float4 reserved; // 0
math::float3 direction; // dir
float reserved1; // 0
math::half4 colorIES; // { half3(col), IES index }
math::float2 spotScaleOffset; // { scale, offset }
float reserved3; // 0
float intensity; // float
uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer)
uint32_t channels; // 0x000c00ll (ll: light channels, c: caster)
static uint32_t packTypeShadow(uint8_t type, bool contactShadow, uint8_t index, uint8_t layer) noexcept {
return (type & 0xF) | (contactShadow ? 0x10 : 0x00) | (index << 8) | (layer << 16);
@@ -184,9 +185,14 @@ static_assert(sizeof(LightsUib) == 64, "the actual UBO is an array of 256 mat4")
// UBO for punctual (spot light) shadows.
struct ShadowUib {
static constexpr utils::StaticString _name{ "ShadowUniforms" };
math::mat4f spotLightFromWorldMatrix[CONFIG_MAX_SHADOW_CASTING_SPOTS];
math::float4 directionShadowBias[CONFIG_MAX_SHADOW_CASTING_SPOTS]; // light direction, normal bias
struct alignas(16) ShadowData {
math::mat4f lightFromWorldMatrix;
math::float3 direction;
float normalBias;
};
ShadowData shadows[CONFIG_MAX_SHADOW_CASTING_SPOTS];
};
static_assert(sizeof(ShadowUib) <= 16384, "ShadowUib exceed max UBO size");
// UBO froxel record buffer.
struct FroxelRecordUib {
@@ -202,9 +208,8 @@ struct PerRenderableUibBone {
math::float4 s = { 1, 1, 1, 0 };
math::float4 ns = { 1, 1, 1, 0 };
};
static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384,
"Bones exceed max UBO size");
"PerRenderableUibBone exceed max UBO size");
} // namespace filament

View File

@@ -63,23 +63,38 @@ public:
Builder& add(utils::StaticString const& uniformName, size_t size,
Type type, Precision precision = Precision::DEFAULT);
// Add a known struct
Builder& add(utils::CString const& uniformName, size_t size,
utils::CString const& structName, size_t stride);
template<size_t N>
Builder& add(utils::StringLiteral<N> const& uniformName, size_t size,
Type type, Precision precision = Precision::DEFAULT) {
return add(utils::StaticString{ uniformName }, size, type, precision);
}
template<size_t N0, size_t N1>
Builder& add(utils::StringLiteral<N0> const& uniformName, size_t size,
utils::StringLiteral<N1> const& structName, size_t stride) {
return add(utils::StaticString{ uniformName }, size,
utils::StaticString{ structName }, stride);
}
// build and return the UniformInterfaceBlock
UniformInterfaceBlock build();
private:
friend class UniformInterfaceBlock;
struct Entry {
Entry(utils::CString name, uint32_t size, Type type, Precision precision) noexcept
: name(std::move(name)), size(size), type(type), precision(precision) { }
: name(std::move(name)), size(size), type(type), precision(precision), stride(strideForType(type, 0)) { }
Entry(utils::CString name, uint32_t size, utils::CString structName, size_t stride) noexcept
: name(std::move(name)), size(size), type(Type::STRUCT), structName(std::move(structName)), stride(stride) { }
utils::CString name;
uint32_t size;
Type type;
Precision precision;
Precision precision{};
utils::CString structName{};
uint32_t stride;
};
utils::CString mName;
std::vector<Entry> mEntries;
@@ -92,6 +107,7 @@ public:
Type type; // type of this uniform
uint32_t size; // size of the array in elements, or 1 if not an array
Precision precision;// precision of this uniform
utils::CString structName;// name of this uniform structure if type is STRUCT
// returns offset in bytes of this uniform (at index if an array)
inline size_t getBufferOffset(size_t index = 0) const {
assert(index < size);
@@ -125,7 +141,7 @@ private:
explicit UniformInterfaceBlock(Builder const& builder) noexcept;
static uint8_t baseAlignmentForType(Type type) noexcept;
static uint8_t strideForType(Type type) noexcept;
static uint8_t strideForType(Type type, uint32_t stride) noexcept;
utils::CString mName;
std::vector<UniformInfo> mUniformsInfoList;

View File

@@ -53,7 +53,7 @@ namespace filament {
//
// Standard variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 64 (-24)
// | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 40 (-24)
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex shader 0 0 0 X X X X
// Fragment shader X X 0 0 X X X
@@ -62,14 +62,18 @@ namespace filament {
//
// Depth variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 8 (-58)
// | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 6 (-58)
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex depth X 0 1 X 0 0 0
// Fragment depth X X 1 0 0 0 0
// Reserved 1 1 1 X 0 0 0
// Reserved X X 1 X X X 1
// Reserved X X 1 X X 1 X
// Reserved X X 1 X 1 X X
// Reserved X X 1 X 0 0 1
// Reserved X X 1 X 0 1 0
// Reserved X X 1 X 0 1 1
// Reserved X X 1 X 1 0 0
// Reserved X X 1 X 1 0 1
// Reserved X X 1 X 1 1 0
// Reserved X X 1 X 1 1 1
//
// 46 variants used, 82 reserved
//
@@ -87,14 +91,16 @@ namespace filament {
static constexpr uint8_t PICKING = 0x20; // picking (depth)
static constexpr uint8_t VSM = 0x40; // variance shadow maps
static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING |
DYNAMIC_LIGHTING |
SHADOW_RECEIVER |
DEPTH;
static constexpr uint8_t STANDARD_VARIANT = 0u;
static constexpr uint8_t STANDARD_MASK = DEPTH;
// the depth variant deactivates all variants that make no sense when writing the depth
// only -- essentially, all fragment-only variants.
static constexpr uint8_t DEPTH_VARIANT = DEPTH;
static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING |
DYNAMIC_LIGHTING |
SHADOW_RECEIVER |
DEPTH;
// this mask filters out the lighting variants
static constexpr uint8_t UNLIT_MASK = SKINNING_OR_MORPHING | FOG;
@@ -118,22 +124,31 @@ namespace filament {
inline void setVsm(bool v) noexcept { set(v, VSM); }
inline static constexpr bool isValidDepthVariant(uint8_t variantKey) noexcept {
// VSM and PICKING are mutually exclusive for DEPTH variants
// Can't have VSM and PICKING together with DEPTH variants
constexpr uint8_t RESERVED_MASK = VSM | PICKING | DEPTH;
constexpr uint8_t RESERVED_VALUE = VSM | PICKING | DEPTH;
return (variantKey & DEPTH_MASK) == DEPTH_VARIANT &&
variantKey != 0b1110000u &&
variantKey != 0b1111000u;
((variantKey & RESERVED_MASK) != RESERVED_VALUE);
}
inline static constexpr bool isValidStandardVariant(uint8_t variantKey) noexcept {
// can't have shadow receiver if we don't have any lighting
constexpr uint8_t RESERVED0_MASK = SHADOW_RECEIVER | DYNAMIC_LIGHTING | DIRECTIONAL_LIGHTING;
constexpr uint8_t RESERVED0_VALUE = SHADOW_RECEIVER;
// can't have VSM without shadow receiver
constexpr uint8_t RESERVED1_MASK = VSM | SHADOW_RECEIVER;
constexpr uint8_t RESERVED1_VALUE = VSM;
return (variantKey & STANDARD_MASK) == STANDARD_VARIANT &&
(variantKey & RESERVED0_MASK) != RESERVED0_VALUE &&
(variantKey & RESERVED1_MASK) != RESERVED1_VALUE;
}
static constexpr bool isReserved(uint8_t variantKey) noexcept {
// reserved variants that should just be skipped
// 1. If the DEPTH bit is set, then it must be a valid depth variant. Otherwise, the
// variant is reserved.
// 2. If SRE is set, either DYN or DIR must also be set (it makes no sense to have
// shadows without lights).
// 3. If VSM is set, then SRE must be set.
return ((variantKey & DEPTH) && !isValidDepthVariant(variantKey)) ||
(variantKey & 0b0010111u) == 0b0000100u ||
(variantKey & 0b1010100u) == 0b1000000u;
if (variantKey & DEPTH) {
return !isValidDepthVariant(variantKey);
}
return !isValidStandardVariant(variantKey);
}
static constexpr uint8_t filterVariantVertex(uint8_t variantKey) noexcept {

View File

@@ -18,8 +18,6 @@
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <private/filament/UniformInterfaceBlock.h>
using namespace utils;
@@ -64,6 +62,14 @@ UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
return *this;
}
UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
utils::CString const& uniformName, size_t size,
utils::CString const& structName, size_t stride) {
mEntries.emplace_back(uniformName, (uint32_t)size, structName, stride);
return *this;
}
UniformInterfaceBlock UniformInterfaceBlock::Builder::build() {
return UniformInterfaceBlock(*this);
}
@@ -89,7 +95,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
uint16_t offset = 0;
for (auto const& e : builder.mEntries) {
size_t alignment = baseAlignmentForType(e.type);
uint8_t stride = strideForType(e.type);
uint8_t stride = strideForType(e.type, e.stride);
if (e.size > 1) { // this is an array
// round the alignment up to that of a float4
alignment = (alignment + 3) & ~3;
@@ -101,7 +107,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
offset += padding;
UniformInfo& info = uniformsInfoList[i];
info = { e.name, offset, stride, e.type, e.size, e.precision };
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName };
// record this uniform info
infoMap[info.name.c_str()] = i;
@@ -154,11 +160,12 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::baseAlignmentForType(UniformInterf
case Type::UINT4:
case Type::MAT3:
case Type::MAT4:
case Type::STRUCT:
return 4;
}
}
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type) noexcept {
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type, uint32_t stride) noexcept {
switch (type) {
case Type::BOOL:
case Type::INT:
@@ -184,6 +191,8 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBloc
return 12;
case Type::MAT4:
return 16;
case Type::STRUCT:
return stride;
}
}

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