Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a809368b | ||
|
|
ea53eb9290 | ||
|
|
05875057c9 | ||
|
|
44125926d1 | ||
|
|
60734349de | ||
|
|
fbfd5ec0ec | ||
|
|
157c0264af | ||
|
|
9bacfdb390 | ||
|
|
f9aaf5c42e | ||
|
|
a74a95cc65 | ||
|
|
21db695e79 | ||
|
|
cf917f1093 | ||
|
|
a8d3a61c25 | ||
|
|
80f13a8149 | ||
|
|
e172f3a67f | ||
|
|
c382c0a9cc | ||
|
|
bc0ea16ff0 | ||
|
|
b2dc8aa84c | ||
|
|
78b29fe967 | ||
|
|
9987e8b6ab | ||
|
|
0d9bdcc008 | ||
|
|
81fa33abeb | ||
|
|
af7f87e19b | ||
|
|
3073b03d56 | ||
|
|
23dab0a013 | ||
|
|
fd68144ba2 | ||
|
|
51e92cd142 | ||
|
|
50f33f7196 | ||
|
|
b5c634045e | ||
|
|
1f05531d53 | ||
|
|
88f382f0e3 | ||
|
|
3e59925900 | ||
|
|
1182d30fb9 | ||
|
|
095179eeb3 | ||
|
|
69b48eaadb | ||
|
|
4031c6f0d3 | ||
|
|
7a22f7dfc5 | ||
|
|
728ebf5023 | ||
|
|
62476d2f06 | ||
|
|
ea404f8d4f | ||
|
|
602a550d93 | ||
|
|
12abbe2d23 | ||
|
|
5fea428243 | ||
|
|
fb0ee97588 | ||
|
|
56ef48c9c3 | ||
|
|
47c3dd3dd1 | ||
|
|
9a3c9ccbf3 | ||
|
|
ef4dfcecd6 | ||
|
|
5943382d23 | ||
|
|
c181648bfa | ||
|
|
1dcf347b87 | ||
|
|
47714007f7 | ||
|
|
54277572d2 | ||
|
|
52f2c0e107 | ||
|
|
59abc8cc20 | ||
|
|
e15796b348 | ||
|
|
1711eaa4d6 | ||
|
|
acbd6a5ca8 | ||
|
|
775090fdda | ||
|
|
0915b86927 | ||
|
|
c10f7b01f4 | ||
|
|
84142ac506 | ||
|
|
ff190847a1 | ||
|
|
3ceec28189 | ||
|
|
481038152f | ||
|
|
60bd72730d |
@@ -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
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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**].
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
|
||||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("filament-tools-plugin") {
|
||||
id = "filament-tools-plugin"
|
||||
implementationClass = "FilamentToolsPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
26
build.sh
26
build.sh
@@ -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."
|
||||
;;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#include <OpenGLES/ES3/glext.h>
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "../GLUtils.h"
|
||||
|
||||
#include <math/vec2.h>
|
||||
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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 {
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -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;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "../OpenGLDriverFactory.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
@@ -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>
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "PlatformWebGL.h"
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "../OpenGLDriverFactory.h"
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
128
filament/src/PIDController.h
Normal file
128
filament/src/PIDController.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
13
filament/src/materials/colorGrading/colorGrading.fs
Normal file
13
filament/src/materials/colorGrading/colorGrading.fs
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user