Compare commits

..

32 Commits

Author SHA1 Message Date
Benjamin Doherty
0d9bdcc008 Merge branch 'rc/1.12.11' into release 2021-11-01 14:55:58 -07:00
Benjamin Doherty
b5c634045e Update RELEASE_NOTES for 1.12.11 2021-10-28 16:13:50 -07:00
Ben Doherty
1f05531d53 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-10-28 16:02:23 -07:00
Benjamin Doherty
88f382f0e3 Revert "refactor colorgrading materials"
This reverts commit fb86a77cf8.
2021-10-28 15:56:50 -07:00
Benjamin Doherty
3e59925900 Remove problematic configuration-cache setting for release build 2021-10-28 15:56:26 -07:00
Benjamin Doherty
ea404f8d4f Remove problematic configuration-cache setting for release build 2021-10-26 10:09:51 -07:00
Benjamin Doherty
602a550d93 Bump version to 1.12.11 2021-10-25 12:30:37 -07:00
Benjamin Doherty
12abbe2d23 Merge branch 'rc/1.12.10' into release 2021-10-25 11:06:18 -07:00
Benjamin Doherty
5fea428243 Release Filament 1.12.10 2021-10-25 11:06:12 -07:00
Benjamin Doherty
fb0ee97588 Update RELEASE_NOTES for 1.12.10 2021-10-25 11:00:06 -07:00
Ben Doherty
56ef48c9c3 Fix, call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (#4749) 2021-10-25 10:39:24 -07:00
Benjamin Doherty
47c3dd3dd1 Revert "refactor colorgrading materials"
This reverts commit fb86a77cf8.
2021-10-25 10:37:52 -07:00
clayly
9a3c9ccbf3 android-samples-gradle-plugin-id (#4740) 2021-10-24 11:12:15 -07:00
Ben Doherty
ef4dfcecd6 Metal: support framebuffer fetch on M1 devices (#4735) 2021-10-21 11:27:05 -07:00
Ben Doherty
5943382d23 iOS: avoid precision issues with CACurrentMediaTime (#4753) 2021-10-21 11:13:51 -07:00
Benjamin Doherty
c181648bfa Bump version to 1.12.10 2021-10-20 12:15:51 -07:00
Benjamin Doherty
1dcf347b87 Release Filament 1.12.9 2021-10-20 12:11:46 -07:00
Ben Doherty
47714007f7 Fix VirtualMachineEnv.cpp with older JNI versions (#4752) 2021-10-20 11:18:03 -07:00
Mathias Agopian
54277572d2 code health: cleanup FrameInfo and FrameSkipper a bit
in particular add a terminate() method to FrameSkipper instead of
destroying driver objects in the destructor -- this matches all other
objects.

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

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

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


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

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

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

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

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

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

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

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

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.12.9'
implementation 'com.google.android.filament:filament-android:1.12.11'
}
```
@@ -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.12.11'
```
### Snapshots

View File

@@ -3,7 +3,21 @@
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.12.12 (currently main branch)
## v1.12.11
- Metal: Color grading performance improvement on M1 devices.
- samples: Fix glitchy animation seen in gltf-viewer iOS sample.
## v1.12.10
- engine: rewrite dynamic resolution scaling controller for better accuracy and less jittering.
- Java: fix missing ASTC texture enum.
- tools: Fix normal map issues in mipgen.
- WebGL: expose some `SurfaceOrientation` functions.
## v1.12.9
- engine: New API: `MultiSampleAntiAliasingOptions` and HDR-aware MSAA resolve. When `customResolve`
is enabled, improves anti-aliasing quality [**NEW API**].

View File

@@ -63,7 +63,7 @@ buildscript {
'minSdk': 19,
'targetSdk': 30,
'compileSdk': 30,
'kotlin': '1.5.30',
'kotlin': '1.5.31',
'buildTools': '30.0.3',
'ndk': '22.1.7171670'
]
@@ -77,7 +77,7 @@ buildscript {
]
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}"
}

View File

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

View File

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

View File

@@ -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;
}
/**

View File

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

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.12.9
VERSION_NAME=1.12.11
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -17,7 +19,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.hellocamera"
minSdkVersion versions.minSdk
minSdkVersion 23
targetSdkVersion versions.targetSdk
}

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -14,5 +14,6 @@ android {
dependencies {
implementation deps.kotlin
implementation deps.androidx.annotations
implementation project(':filament-android')
}

View File

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

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,5 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
@@ -17,7 +19,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.streamtest"
minSdkVersion versions.minSdk
minSdkVersion 23
targetSdkVersion versions.targetSdk
}

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -116,6 +116,7 @@ set(PRIVATE_HDRS
src/Intersections.h
src/MaterialParser.h
src/PerViewUniforms.h
src/PIDController.h
src/PostProcessManager.h
src/RenderPass.h
src/RenderPrimitive.h

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -75,6 +75,16 @@ 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
};
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.12.9"
spec.version = "1.12.11"
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.12.11/filament-v1.12.11-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

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

View File

@@ -121,10 +121,16 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
CompilerMSL::Options mslOptions = {};
mslOptions.platform = platform,
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(1, 1);
mslOptions.msl_version = config.shaderModel == filament::backend::ShaderModel::GL_ES_30 ?
CompilerMSL::Options::make_msl_version(2, 0) : CompilerMSL::Options::make_msl_version(2, 2);
if (config.shaderModel == filament::backend::ShaderModel::GL_ES_30) {
if (config.hasFramebufferFetch) {
mslOptions.use_framebuffer_fetch_subpasses = true;
// On macOS, framebuffer fetch is only available starting with MSL 2.3. Filament will only
// use framebuffer fetch materials on devices that support it.
if (config.shaderModel == filament::backend::ShaderModel::GL_CORE_41) {
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(2, 3);
}
}
mslCompiler.set_msl_options(mslOptions);

View File

@@ -234,8 +234,13 @@ LinearImage PNGDecoder::decode() {
png_set_tRNS_to_alpha(mPNG);
}
if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) {
png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
double gamma = 1.0;
png_get_gAMA(mPNG, mInfo, &gamma);
if (gamma != 1.0) {
png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
}
} else {
png_set_gamma_fixed(mPNG, PNG_FP_1, PNG_FP_1);
png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_GAMMA_LINEAR);
}
if (bitDepth < 16) {

View File

@@ -52,7 +52,7 @@ void updateOrCompare(LinearImage limgResult, const utils::Path& fnameGolden,
// Load the PNG file at the given path.
std::ifstream in(fnameGolden, std::ios::binary);
ASSERT_PRECONDITION(in, "Unable to open: %s", fnameGolden.c_str());
LinearImage limgGolden = ImageDecoder::decode(in, fnameGolden, ImageDecoder::ColorSpace::LINEAR);
LinearImage limgGolden = ImageDecoder::decode(in, fnameGolden);
// Convert 4-channel RGBM into proper RGB.
if (fnameGolden.getExtension() == "rgbm" && limgGolden.getChannels() == 4) {

View File

@@ -564,12 +564,48 @@ int main(int argc, char** argv) {
}
if (ImGui::CollapsingHeader("Debug")) {
auto& debug = engine->getDebugRegistry();
if (ImGui::Button("Capture frame")) {
auto& debug = engine->getDebugRegistry();
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
}
auto dataSource = debug.getDataSource("d.view.frame_info");
if (dataSource.data) {
ImGuiExt::PlotLinesSeries("FrameInfo", 6,
[](int series) {
const ImVec4 colors[] = {
{ 1, 0, 0, 1 }, // target
{ 0, 0.5f, 0, 1 }, // frame-time
{ 0, 1, 0, 1 }, // frame-time denoised
{ 1, 1, 0, 1 }, // i
{ 1, 0, 1, 1 }, // d
{ 0, 1, 1, 1 }, // e
};
ImGui::PushStyleColor(ImGuiCol_PlotLines, colors[series]);
},
[](int series, void* buffer, int i) -> float {
auto const* p = (DebugRegistry::FrameHistory const*)buffer + i;
switch (series) {
case 0: return 0.03f * p->target;
case 1: return 0.03f * p->frameTime;
case 2: return 0.03f * p->frameTimeDenoised;
case 3: return p->pid_i * 0.5f / 100.0f + 0.5f;
case 4: return p->pid_d * 0.5f / 0.100f + 0.5f;
case 5: return p->pid_e * 0.5f / 1.000f + 0.5f;
default: return 0.0f;
}
},
[](int series) {
if (series < 6) ImGui::PopStyleColor();
},
const_cast<void*>(dataSource.data), int(dataSource.count), 0,
nullptr, 0.0f, 1.0f, { 0, 100 });
}
ImGui::SliderFloat("Kp", debug.getPropertyAddress<float>("d.view.pid.kp"), 0, 2);
ImGui::SliderFloat("Ki", debug.getPropertyAddress<float>("d.view.pid.ki"), 0, 10);
ImGui::SliderFloat("Kd", debug.getPropertyAddress<float>("d.view.pid.kd"), 0, 10);
}
if (ImGui::BeginPopupModal("MessageBox", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {

View File

@@ -440,6 +440,9 @@ int main(int argc, char* argv[]) {
if (!outputStream) {
cerr << "The output file cannot be opened: " << path << endl;
} else {
if (g_filter == Filter::GAUSSIAN_NORMALS) {
image = vectorsToColors(image);
}
if (!ImageEncoder::encode(outputStream, g_format, image, g_compression, path)) {
cerr << "An error occurred while encoding the image." << endl;
return 1;

View File

@@ -502,6 +502,26 @@ Filament.loadClassExtensions = function() {
return new Int16Array(arrayBuffer);
};
Filament.SurfaceOrientation.prototype.getQuatsHalf4 = function (nverts) {
const attribType = Filament.VertexBuffer$AttributeType.HALF4;
const quatsBufferSize = 8 * nverts;
const quatsBuffer = Filament._malloc(quatsBufferSize);
this._getQuats(quatsBuffer, nverts, attribType);
const arrayBuffer = Filament.HEAPU8.subarray(quatsBuffer, quatsBuffer + quatsBufferSize).slice().buffer;
Filament._free(quatsBuffer);
return new Uint16Array(arrayBuffer);
};
Filament.SurfaceOrientation.prototype.getQuatsFloat4 = function (nverts) {
const attribType = Filament.VertexBuffer$AttributeType.FLOAT4;
const quatsBufferSize = 16 * nverts;
const quatsBuffer = Filament._malloc(quatsBufferSize);
this._getQuats(quatsBuffer, nverts, attribType);
const arrayBuffer = Filament.HEAPU8.subarray(quatsBuffer, quatsBuffer + quatsBufferSize).slice().buffer;
Filament._free(quatsBuffer);
return new Float32Array(arrayBuffer);
};
Filament.gltfio$AssetLoader.prototype.createAssetFromJson = function(buffer) {
if ('string' == typeof buffer && buffer.endsWith('.glb')) {
console.error('Please use createAssetFromBinary for glb files.');

View File

@@ -653,6 +653,8 @@ export class SurfaceOrientation$Builder {
export class SurfaceOrientation {
public getQuats(quatCount: number): Int16Array;
public getQuatsHalf4(quatCount: number): Uint16Array;
public getQuatsFloat4(quatCount: number): Float32Array;
public delete(): void;
}

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.12.9",
"version": "1.12.11",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",