Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4436ffaaab |
@@ -15,7 +15,7 @@ runs:
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew
|
||||
key: ${{ runner.os }}-brew-20251211
|
||||
key: ${{ runner.os }}-brew-20250424
|
||||
- name: Install Mac Prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -242,12 +242,6 @@ foremost for `arm64-v8a`.
|
||||
|
||||
To build Android on Windows machines, see [android/Windows.md](android/Windows.md).
|
||||
|
||||
#### Important: SDK location
|
||||
|
||||
Either ensure your `ANDROID_HOME` environment variable is set or make sure the root project
|
||||
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
|
||||
the Android SDK.
|
||||
|
||||
#### Easy Android build
|
||||
|
||||
The easiest way to build Filament for Android is to use `build.sh` and the
|
||||
@@ -257,45 +251,8 @@ The easiest way to build Filament for Android is to use `build.sh` and the
|
||||
./build.sh -p android release
|
||||
```
|
||||
|
||||
To build a sample (such as `android/samples/sample-hello-triangle`) for an ARM 64-bit phone, you would run
|
||||
```shell
|
||||
./build.sh -p android -q arm64-v8a -k sample-hello-triangle release
|
||||
```
|
||||
|
||||
The output APK can be found in `android/samples/sample-hello-triangle/build/outputs/apk/release/sample-hello-triangle-release-unsigned.apk`
|
||||
|
||||
Run `build.sh -h` for more information.
|
||||
|
||||
#### Android Studio
|
||||
|
||||
You must use the latest stable release of Android Studio.
|
||||
|
||||
The Android build of filament is separated into java/kotlin client APIs, a layer of jni bindings
|
||||
that bridges java/kotlin with native code, and Filament and other component code that have been compiled
|
||||
into architecture-specific libraries. Our default Android Studio gradle setup can compile java/kotlin and
|
||||
the jni bindings for you, but it will treat the filament libraries as already compiled and present on
|
||||
the system.
|
||||
|
||||
Therefore, before compiling the sample app or any other targets, you must
|
||||
make sure that the native filament libraries have been compiled and are located at a prescribed location
|
||||
so that the jni bindings can link against them. You can do so by using the easy build script
|
||||
|
||||
```shell
|
||||
./build.sh -p android release -q arm64-v8a
|
||||
```
|
||||
|
||||
Note that the above step will also install host machine tools into prescribed locations. These tools are
|
||||
required for compiling Filament assets such as materials and environment maps.
|
||||
|
||||
Now we are ready to compile the apps. To open the project, point Studio to the `android` folder.
|
||||
After opening the project and syncing with Gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar. Additionally, you will need to select a deployment target.
|
||||
By doing so, Android Studio will automatically try to compile the app only for that specific
|
||||
device's architecture. So if you are targeting a new Pixel phone, make sure that the step above
|
||||
(compiling the library) is targeting ARM 64-bit (`-q arm64-v8a` ), and if you are running the app on
|
||||
an emulator on a Linux machine with an x86 64-bit chipset, you would indicate (`-q x86_64`) in the above step.
|
||||
|
||||
|
||||
#### Manual builds
|
||||
|
||||
Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.3'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.68.3'
|
||||
pod 'Filament', '~> 1.68.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,14 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.68.3
|
||||
|
||||
- materials: added support for the glTF `KHR_materials_dispersion` extension, which adds dispersion for refractive objects
|
||||
|
||||
## v1.68.2
|
||||
|
||||
- Support `setPresentationTime` with the Metal backend.
|
||||
|
||||
## v1.68.1
|
||||
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ buildscript {
|
||||
|
||||
ext.cmakeArgs = [
|
||||
"--no-warn-unused-cli",
|
||||
"-DANDROID_WEAK_API_DEFS=ON",
|
||||
"-DANDROID_PIE=ON",
|
||||
"-DANDROID_PLATFORM=21",
|
||||
"-DANDROID_STL=c++_static",
|
||||
|
||||
@@ -134,7 +134,6 @@ target_include_directories(filament-jni PRIVATE
|
||||
../../filament/backend/include
|
||||
../../third_party/robin-map
|
||||
../../third_party/perfetto
|
||||
../../libs/bluevk/include
|
||||
../../libs/utils/include)
|
||||
|
||||
# Ordering is significant in the following list. The PRIVATE qualifier prevents transitive deps.
|
||||
@@ -168,9 +167,3 @@ target_link_libraries(filament-jni
|
||||
|
||||
# Force a relink when the version script is changed:
|
||||
set_target_properties(filament-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_VULKAN=1)
|
||||
else()
|
||||
add_definitions(-DFILAMENT_SUPPORTS_VULKAN=0)
|
||||
endif()
|
||||
|
||||
@@ -63,14 +63,6 @@ Java_com_google_android_filament_Skybox_nBuilderColor(JNIEnv *, jclass,
|
||||
builder->color({r, g, b, a});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Skybox_nBuilderPriority(JNIEnv *, jclass,
|
||||
jlong nativeSkyBoxBuilder, jint priority) {
|
||||
Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder;
|
||||
builder->priority(uint8_t(priority));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Skybox_nBuilderBuild(JNIEnv *env, jclass type,
|
||||
jlong nativeSkyBoxBuilder, jlong nativeEngine) {
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/bitmap.h>
|
||||
#include <android/hardware_buffer_jni.h>
|
||||
#include <backend/platforms/PlatformEGLAndroid.h>
|
||||
# if FILAMENT_SUPPORTS_VULKAN
|
||||
# include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <filament/Engine.h>
|
||||
@@ -393,57 +388,6 @@ Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlon
|
||||
texture->setExternalImage(*engine, (void*)eglImage);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jclass clazz,
|
||||
jlong nativeTexture, jlong nativeEngine, jobject ahb) {
|
||||
Texture *texture = (Texture *) nativeTexture;
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
Platform* platform = engine->getPlatform();
|
||||
AHardwareBuffer* nativeBuffer = nullptr;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
nativeBuffer = AHardwareBuffer_fromHardwareBuffer(env, ahb);
|
||||
}
|
||||
if (!nativeBuffer) {
|
||||
// either we're not on Android 26, or ahb wasn't a AHardwareBuffer
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (engine->getBackend() == Backend::OPENGL) {
|
||||
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid.
|
||||
#if UTILS_HAS_RTTI
|
||||
if (!dynamic_cast<PlatformEGLAndroid*>(platform)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
auto* eglPlatform = (PlatformEGLAndroid*) platform;
|
||||
auto ref = eglPlatform->createExternalImage(nativeBuffer, false);
|
||||
texture->setExternalImage(*engine, ref);
|
||||
}
|
||||
|
||||
#if FILAMENT_SUPPORTS_VULKAN
|
||||
else if (engine->getBackend() == Backend::VULKAN) {
|
||||
// CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid.
|
||||
#if UTILS_HAS_RTTI
|
||||
if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
auto* vulkanPlatform = (VulkanPlatformAndroid*) platform;
|
||||
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false);
|
||||
texture->setExternalImage(*engine, ref);
|
||||
}
|
||||
#endif // FILAMENT_SUPPORTS_VULKAN
|
||||
// success!
|
||||
return JNI_TRUE;
|
||||
#else
|
||||
// other platforms could come here
|
||||
return JNI_FALSE;
|
||||
#endif // __ANDROID__
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv*, jclass,
|
||||
jlong nativeTexture, jlong nativeEngine, jlong nativeStream) {
|
||||
@@ -663,4 +607,3 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JN
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -155,24 +155,6 @@ public class Skybox {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rendering priority of the Skybox. By default, it is set to the lowest
|
||||
* priority (7) such that the Skybox is always rendered after the opaque objects,
|
||||
* to reduce overdraw when depth culling is enabled.
|
||||
*
|
||||
* @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority
|
||||
* (rendered last).
|
||||
*
|
||||
* @return Builder reference for chaining calls.
|
||||
*
|
||||
* @see RenderableManager.Builder#priority
|
||||
*/
|
||||
@NonNull
|
||||
public Builder priority(@IntRange(from = 0, to = 7) int priority) {
|
||||
nBuilderPriority(mNativeBuilder, priority);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>Skybox</code> object
|
||||
*
|
||||
@@ -280,7 +262,6 @@ public class Skybox {
|
||||
private static native void nBuilderShowSun(long nativeSkyboxBuilder, boolean show);
|
||||
private static native void nBuilderIntensity(long nativeSkyboxBuilder, float intensity);
|
||||
private static native void nBuilderColor(long nativeSkyboxBuilder, float r, float g, float b, float a);
|
||||
private static native void nBuilderPriority(long nativeSkyboxBuilder, int priority);
|
||||
private static native long nBuilderBuild(long nativeSkyboxBuilder, long nativeEngine);
|
||||
private static native void nSetLayerMask(long nativeSkybox, int select, int value);
|
||||
private static native int nGetLayerMask(long nativeSkybox);
|
||||
|
||||
@@ -71,16 +71,6 @@ import static com.google.android.filament.Texture.Type.COMPRESSED;
|
||||
* @see MaterialInstance#setParameter(String, Texture, TextureSampler)
|
||||
*/
|
||||
public class Texture {
|
||||
|
||||
private static Class<?> HardwareBufferClass = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
HardwareBufferClass = Class.forName("android.hardware.HardwareBuffer");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Sampler[] sSamplerValues = Sampler.values();
|
||||
private static final InternalFormat[] sInternalFormatValues = InternalFormat.values();
|
||||
|
||||
@@ -1182,38 +1172,6 @@ public class Texture {
|
||||
nSetExternalImage(getNativeObject(), engine.getNativeObject(), eglImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the external image to associate with this <code>Texture</code>.
|
||||
*
|
||||
* <p>Typically, the external image is OS specific, and can be a video or camera frame.
|
||||
* There are many restrictions when using an external image as a texture, such as:</p>
|
||||
* <ul>
|
||||
* <li> only the level of detail (lod) 0 can be specified</li>
|
||||
* <li> only nearest or linear filtering is supported</li>
|
||||
* <li> the size and format of the texture is defined by the external image</li>
|
||||
* <li> only the CLAMP_TO_EDGE wrap mode is supported</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param engine {@link Engine} this texture is associated to. Must be the
|
||||
* instance passed to {@link Builder#build Builder.build()}.
|
||||
* @param externalImageRef An OS specific Object. On Android it must be a
|
||||
* <code>android.hardware.HardwareBuffer</code>
|
||||
*/
|
||||
public void setExternalImage(@NonNull Engine engine, Object externalImageRef) {
|
||||
if (HardwareBufferClass != null) {
|
||||
if (!HardwareBufferClass.isInstance(externalImageRef)) {
|
||||
throw new IllegalArgumentException("externalImageRef must be a AHardwareBuffer");
|
||||
}
|
||||
if (!nSetExternalImageByAHB(getNativeObject(), engine.getNativeObject(), externalImageRef)) {
|
||||
throw new IllegalStateException("Error setting AHardwareBuffer as external image");
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"setExternalImage(Engine, Object) not supported on this platform");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the external stream to associate with this <code>Texture</code>.
|
||||
*
|
||||
@@ -1406,8 +1364,6 @@ public class Texture {
|
||||
private static native void nSetExternalImage(
|
||||
long nativeObject, long nativeEngine, long eglImage);
|
||||
|
||||
private static native boolean nSetExternalImageByAHB(long nativeTexture, long nativeObject, Object ahb);
|
||||
|
||||
private static native void nSetExternalStream(long nativeTexture,
|
||||
long nativeEngine, long nativeStream);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.68.3
|
||||
VERSION_NAME=1.68.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -80,12 +80,54 @@ frame and the external texture are perfectly synchronized.
|
||||
|
||||

|
||||
|
||||
## Building Samples
|
||||
## Prerequisites
|
||||
|
||||
Before you start, make sure to read [Filament's README](../../README.md). You need to be able to
|
||||
compile Filament's native library and Filament's AAR for this project. The easiest way to proceed
|
||||
is to install all the required dependencies and to run the following commands at the root of the
|
||||
source tree.
|
||||
source tree:
|
||||
|
||||
To build the samples, please follow the steps described in [BUILDING.md](../../BUILDING.md#android)
|
||||
```shell
|
||||
./build.sh -p desktop -i release
|
||||
./build.sh -p android release
|
||||
```
|
||||
|
||||
This will build all the native components and the AAR required by this sample application.
|
||||
|
||||
If you do not use the build script, you must set the `filament_tools_dir` property when invoking
|
||||
Gradle, either from the command line or from `local.properties`. This property must point to the
|
||||
distribution/install directory for desktop (produced by make/ninja install). This directory must
|
||||
contain `bin/matc` and `bin/cmgen`.
|
||||
|
||||
Example:
|
||||
```shell
|
||||
./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
|
||||
```
|
||||
|
||||
## Important: SDK location
|
||||
|
||||
Either ensure your `ANDROID_HOME` environment variable is set or make sure the root project
|
||||
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
|
||||
the Android SDK.
|
||||
|
||||
## Compiling
|
||||
|
||||
### Android Studio
|
||||
|
||||
You must use the latest stable release of Android Studio. To open the project, point Studio to the
|
||||
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar.
|
||||
|
||||
To compile and run each sample make sure you have selected the appropriate build variant
|
||||
(arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal"
|
||||
variant which includes all the other ones.
|
||||
|
||||
### Command Line
|
||||
|
||||
From the `android` directory in the project root:
|
||||
|
||||
```shell
|
||||
./gradlew :samples:sample-hello-triangle:installDebug
|
||||
```
|
||||
|
||||
Replace `sample-hello-triangle` with your preferred project.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
/.idea/caches
|
||||
/.idea/gradle.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
/src/main/assets
|
||||
.externalNativeBuild
|
||||
@@ -1,53 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
}
|
||||
|
||||
clean.doFirst {
|
||||
delete "src/main/assets"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.google.android.filament.texturetarget'
|
||||
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.texturetarget"
|
||||
minSdkVersion 26
|
||||
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 {
|
||||
noCompress 'filamat', 'ktx'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.jdk
|
||||
targetCompatibility versions.jdk
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation deps.kotlin
|
||||
implementation project(':filament-android')
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:exported="true"
|
||||
android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,429 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.texturetarget
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.hardware.HardwareBuffer
|
||||
import android.opengl.Matrix
|
||||
import android.os.Bundle
|
||||
import android.view.Choreographer
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceView
|
||||
import android.view.animation.LinearInterpolator
|
||||
import com.google.android.filament.*
|
||||
import com.google.android.filament.RenderableManager.PrimitiveType
|
||||
import com.google.android.filament.VertexBuffer.AttributeType
|
||||
import com.google.android.filament.VertexBuffer.VertexAttribute
|
||||
import com.google.android.filament.android.DisplayHelper
|
||||
import com.google.android.filament.android.FilamentHelper
|
||||
import com.google.android.filament.android.UiHelper
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.channels.Channels
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class MainActivity : Activity() {
|
||||
companion object {
|
||||
init {
|
||||
Filament.init()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var surfaceView: SurfaceView
|
||||
private lateinit var uiHelper: UiHelper
|
||||
private lateinit var displayHelper: DisplayHelper
|
||||
private lateinit var choreographer: Choreographer
|
||||
|
||||
private lateinit var engine: Engine
|
||||
private lateinit var renderer: Renderer
|
||||
private lateinit var scene: Scene
|
||||
private lateinit var view: View
|
||||
private lateinit var camera: Camera
|
||||
|
||||
private lateinit var triangleMaterial: Material
|
||||
private lateinit var texturedMaterial: Material
|
||||
private lateinit var triangleVertexBuffer: VertexBuffer
|
||||
private lateinit var triangleIndexBuffer: IndexBuffer
|
||||
private lateinit var quadVertexBuffer: VertexBuffer
|
||||
private lateinit var quadIndexBuffer: IndexBuffer
|
||||
|
||||
@Entity private var triangleRenderable = 0
|
||||
@Entity private var quadRenderable = 0
|
||||
|
||||
private var swapChain: SwapChain? = null
|
||||
|
||||
private val frameScheduler = FrameCallback()
|
||||
private val animator = ValueAnimator.ofFloat(0.0f, 360.0f)
|
||||
|
||||
private var hardwareBuffer: HardwareBuffer? = null
|
||||
private var texture: Texture? = null
|
||||
private var renderTarget: RenderTarget? = null
|
||||
|
||||
private lateinit var offscreenView: View
|
||||
private lateinit var offscreenCamera: Camera
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
surfaceView = SurfaceView(this)
|
||||
setContentView(surfaceView)
|
||||
choreographer = Choreographer.getInstance()
|
||||
displayHelper = DisplayHelper(this)
|
||||
setupSurfaceView()
|
||||
setupFilament()
|
||||
setupView()
|
||||
setupScene()
|
||||
}
|
||||
|
||||
private fun setupSurfaceView() {
|
||||
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
|
||||
uiHelper.renderCallback = SurfaceCallback()
|
||||
uiHelper.attachTo(surfaceView)
|
||||
}
|
||||
|
||||
private fun setupFilament() {
|
||||
engine = Engine.create()
|
||||
renderer = engine.createRenderer()
|
||||
scene = engine.createScene()
|
||||
view = engine.createView()
|
||||
camera = engine.createCamera(engine.entityManager.create())
|
||||
offscreenView = engine.createView()
|
||||
offscreenCamera = engine.createCamera(engine.entityManager.create())
|
||||
}
|
||||
|
||||
private fun setupView() {
|
||||
scene.skybox = Skybox.Builder()
|
||||
.priority(0)
|
||||
.color(0.0f, 0.0f, 1.0f, 1.0f).build(engine)
|
||||
|
||||
// This is the view that will be drawn on screen.
|
||||
view.camera = camera
|
||||
view.scene = scene
|
||||
view.isPostProcessingEnabled = false
|
||||
|
||||
// This is the view that will be rendered off-screen.
|
||||
offscreenView.camera = offscreenCamera
|
||||
offscreenView.scene = scene
|
||||
offscreenView.isPostProcessingEnabled = false
|
||||
}
|
||||
|
||||
private fun setupScene() {
|
||||
loadMaterials()
|
||||
createTriangleMesh()
|
||||
createQuadMesh()
|
||||
|
||||
// layer 1: skybox
|
||||
// layer 2: triangle
|
||||
// layer 3: quad
|
||||
|
||||
triangleMaterial.defaultInstance.cullingMode = Material.CullingMode.NONE;
|
||||
texturedMaterial.defaultInstance.cullingMode = Material.CullingMode.NONE;
|
||||
|
||||
// The triangle is a regular renderable.
|
||||
triangleRenderable = EntityManager.get().create()
|
||||
RenderableManager.Builder(1)
|
||||
.geometry(0, PrimitiveType.TRIANGLES, triangleVertexBuffer, triangleIndexBuffer, 0, 3)
|
||||
.material(0, triangleMaterial.defaultInstance)
|
||||
.culling(false)
|
||||
.castShadows(false)
|
||||
.receiveShadows(false)
|
||||
.layerMask(7, 2)
|
||||
.build(engine, triangleRenderable)
|
||||
|
||||
// The quad is a regular renderable.
|
||||
quadRenderable = EntityManager.get().create()
|
||||
RenderableManager.Builder(1)
|
||||
.geometry(0, PrimitiveType.TRIANGLES, quadVertexBuffer, quadIndexBuffer, 0, 6)
|
||||
.material(0, texturedMaterial.defaultInstance)
|
||||
.culling(false)
|
||||
.castShadows(false)
|
||||
.receiveShadows(false)
|
||||
.layerMask(7, 4)
|
||||
.build(engine, quadRenderable)
|
||||
|
||||
// We only want to render the triangle in the offscreen view.
|
||||
offscreenView.setVisibleLayers(7, 3) // render skybox + triangle
|
||||
|
||||
// We only want to render the quad in the on-screen view.
|
||||
view.setVisibleLayers(7, 4) // render quad only
|
||||
|
||||
scene.addEntity(triangleRenderable)
|
||||
scene.addEntity(quadRenderable)
|
||||
|
||||
startAnimation()
|
||||
}
|
||||
|
||||
private fun loadMaterials() {
|
||||
readUncompressedAsset("materials/baked_color.filamat").let {
|
||||
triangleMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
readUncompressedAsset("materials/textured.filamat").let {
|
||||
texturedMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTriangleMesh() {
|
||||
val intSize = 4
|
||||
val floatSize = 4
|
||||
val shortSize = 2
|
||||
val vertexSize = 3 * floatSize + intSize
|
||||
|
||||
data class Vertex(val x: Float, val y: Float, val z: Float, val color: Int)
|
||||
fun ByteBuffer.put(v: Vertex): ByteBuffer {
|
||||
putFloat(v.x)
|
||||
putFloat(v.y)
|
||||
putFloat(v.z)
|
||||
putInt(v.color)
|
||||
return this
|
||||
}
|
||||
|
||||
val vertexCount = 3
|
||||
val a1 = PI * 2.0 / 3.0
|
||||
val a2 = PI * 4.0 / 3.0
|
||||
|
||||
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.put(Vertex(1.0f, 0.0f, 0.0f, 0xffff0000.toInt()))
|
||||
.put(Vertex(cos(a1).toFloat(), sin(a1).toFloat(), 0.0f, 0xff00ff00.toInt()))
|
||||
.put(Vertex(cos(a2).toFloat(), sin(a2).toFloat(), 0.0f, 0xff0000ff.toInt()))
|
||||
.flip()
|
||||
|
||||
triangleVertexBuffer = VertexBuffer.Builder()
|
||||
.bufferCount(1)
|
||||
.vertexCount(vertexCount)
|
||||
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
|
||||
.attribute(VertexAttribute.COLOR, 0, AttributeType.UBYTE4, 3 * floatSize, vertexSize)
|
||||
.normalized(VertexAttribute.COLOR)
|
||||
.build(engine)
|
||||
triangleVertexBuffer.setBufferAt(engine, 0, vertexData)
|
||||
|
||||
val indexData = ByteBuffer.allocate(vertexCount * shortSize)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.putShort(0)
|
||||
.putShort(1)
|
||||
.putShort(2)
|
||||
.flip()
|
||||
|
||||
triangleIndexBuffer = IndexBuffer.Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
|
||||
.build(engine)
|
||||
triangleIndexBuffer.setBuffer(engine, indexData)
|
||||
}
|
||||
|
||||
private fun createQuadMesh() {
|
||||
val floatSize = 4
|
||||
val shortSize = 2
|
||||
val vertexSize = (2 * floatSize) + (2 * floatSize) // position + UV
|
||||
|
||||
data class Vertex(val x: Float, val y: Float, val u: Float, val v: Float)
|
||||
fun ByteBuffer.put(v: Vertex): ByteBuffer {
|
||||
putFloat(v.x)
|
||||
putFloat(v.y)
|
||||
putFloat(v.u)
|
||||
putFloat(v.v)
|
||||
return this
|
||||
}
|
||||
|
||||
val vertexCount = 4
|
||||
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.put(Vertex(-1.0f, -1.0f, 0.0f, 0.0f))
|
||||
.put(Vertex( 1.0f, -1.0f, 1.0f, 0.0f))
|
||||
.put(Vertex( 1.0f, 1.0f, 1.0f, 1.0f))
|
||||
.put(Vertex(-1.0f, 1.0f, 0.0f, 1.0f))
|
||||
.flip()
|
||||
|
||||
quadVertexBuffer = VertexBuffer.Builder()
|
||||
.bufferCount(1)
|
||||
.vertexCount(vertexCount)
|
||||
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, vertexSize)
|
||||
.attribute(VertexAttribute.UV0, 0, AttributeType.FLOAT2, 2 * floatSize, vertexSize)
|
||||
.build(engine)
|
||||
quadVertexBuffer.setBufferAt(engine, 0, vertexData)
|
||||
|
||||
val indexData = ByteBuffer.allocate(6 * shortSize)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.putShort(0).putShort(1).putShort(2)
|
||||
.putShort(0).putShort(2).putShort(3)
|
||||
.flip()
|
||||
|
||||
quadIndexBuffer = IndexBuffer.Builder()
|
||||
.indexCount(6)
|
||||
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
|
||||
.build(engine)
|
||||
quadIndexBuffer.setBuffer(engine, indexData)
|
||||
}
|
||||
|
||||
private fun startAnimation() {
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.duration = 4000
|
||||
animator.repeatMode = ValueAnimator.RESTART
|
||||
animator.repeatCount = ValueAnimator.INFINITE
|
||||
animator.addUpdateListener { a ->
|
||||
val transformMatrix = FloatArray(16)
|
||||
Matrix.setRotateM(transformMatrix, 0, -(a.animatedValue as Float), 0.0f, 0.0f, 1.0f)
|
||||
val tcm = engine.transformManager
|
||||
tcm.setTransform(tcm.getInstance(triangleRenderable), transformMatrix)
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
choreographer.postFrameCallback(frameScheduler)
|
||||
animator.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
choreographer.removeFrameCallback(frameScheduler)
|
||||
animator.cancel()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
choreographer.removeFrameCallback(frameScheduler)
|
||||
animator.cancel()
|
||||
uiHelper.detach()
|
||||
|
||||
// Destroy all renderables.
|
||||
scene.remove(triangleRenderable)
|
||||
scene.remove(quadRenderable)
|
||||
|
||||
// Destroy all resources.
|
||||
engine.destroyEntity(triangleRenderable)
|
||||
engine.destroyEntity(quadRenderable)
|
||||
engine.destroyRenderer(renderer)
|
||||
engine.destroyVertexBuffer(triangleVertexBuffer)
|
||||
engine.destroyIndexBuffer(triangleIndexBuffer)
|
||||
engine.destroyVertexBuffer(quadVertexBuffer)
|
||||
engine.destroyIndexBuffer(quadIndexBuffer)
|
||||
engine.destroyMaterial(triangleMaterial)
|
||||
engine.destroyMaterial(texturedMaterial)
|
||||
engine.destroyView(view)
|
||||
engine.destroyView(offscreenView)
|
||||
engine.destroyScene(scene)
|
||||
engine.destroyCameraComponent(camera.entity)
|
||||
engine.destroyCameraComponent(offscreenCamera.entity)
|
||||
renderTarget?.let { engine.destroyRenderTarget(it) }
|
||||
texture?.let { engine.destroyTexture(it) }
|
||||
hardwareBuffer?.close()
|
||||
|
||||
val entityManager = EntityManager.get()
|
||||
entityManager.destroy(triangleRenderable)
|
||||
entityManager.destroy(quadRenderable)
|
||||
entityManager.destroy(camera.entity)
|
||||
entityManager.destroy(offscreenCamera.entity)
|
||||
|
||||
engine.destroy()
|
||||
}
|
||||
|
||||
inner class FrameCallback : Choreographer.FrameCallback {
|
||||
override fun doFrame(frameTimeNanos: Long) {
|
||||
choreographer.postFrameCallback(this)
|
||||
if (uiHelper.isReadyToRender) {
|
||||
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
||||
// Render the triangle to the texture.
|
||||
renderer.render(offscreenView)
|
||||
// Render the quad to the screen.
|
||||
renderer.render(view)
|
||||
renderer.endFrame()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class SurfaceCallback : UiHelper.RendererCallback {
|
||||
override fun onNativeWindowChanged(surface: Surface) {
|
||||
swapChain?.let { engine.destroySwapChain(it) }
|
||||
swapChain = engine.createSwapChain(surface, uiHelper.swapChainFlags)
|
||||
displayHelper.attach(renderer, surfaceView.display)
|
||||
}
|
||||
|
||||
override fun onDetachedFromSurface() {
|
||||
displayHelper.detach()
|
||||
swapChain?.let {
|
||||
engine.destroySwapChain(it)
|
||||
engine.flushAndWait()
|
||||
swapChain = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResized(width: Int, height: Int) {
|
||||
// On-screen camera
|
||||
val zoom = 1.0
|
||||
val aspect = width.toDouble() / height.toDouble()
|
||||
camera.setProjection(Camera.Projection.ORTHO, -aspect * zoom, aspect * zoom, -zoom, zoom, 0.0, 10.0)
|
||||
view.viewport = Viewport(0, 0, width, height)
|
||||
|
||||
// Off-screen camera
|
||||
val offscreenZoom = 1.5
|
||||
offscreenCamera.setProjection(Camera.Projection.ORTHO,
|
||||
-aspect * offscreenZoom, aspect * offscreenZoom,
|
||||
-offscreenZoom, offscreenZoom, 0.0, 10.0)
|
||||
offscreenView.viewport = Viewport(0, 0, width, height)
|
||||
|
||||
// If we have a render target, destroy it.
|
||||
renderTarget?.let { engine.destroyRenderTarget(it) }
|
||||
texture?.let { engine.destroyTexture(it) }
|
||||
hardwareBuffer?.close()
|
||||
|
||||
// Create a new render target.
|
||||
hardwareBuffer = HardwareBuffer.create(width, height,
|
||||
HardwareBuffer.RGBA_8888, 1,
|
||||
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
|
||||
|
||||
texture = Texture.Builder()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.usage(Texture.Usage.COLOR_ATTACHMENT or Texture.Usage.SAMPLEABLE)
|
||||
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
|
||||
.format(Texture.InternalFormat.RGBA8)
|
||||
.external()
|
||||
.build(engine)
|
||||
|
||||
texture!!.setExternalImage(engine, hardwareBuffer!!)
|
||||
|
||||
renderTarget = RenderTarget.Builder()
|
||||
.texture(RenderTarget.AttachmentPoint.COLOR, texture!!)
|
||||
.build(engine)
|
||||
|
||||
offscreenView.renderTarget = renderTarget
|
||||
|
||||
// Set the texture on the quad material.
|
||||
texturedMaterial.defaultInstance.setParameter("texture", texture!!,
|
||||
TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR,
|
||||
TextureSampler.WrapMode.CLAMP_TO_EDGE))
|
||||
|
||||
FilamentHelper.synchronizePendingFrames(engine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUncompressedAsset(assetName: String): ByteBuffer {
|
||||
assets.openFd(assetName).use { fd ->
|
||||
val input = fd.createInputStream()
|
||||
val dst = ByteBuffer.allocate(fd.length.toInt())
|
||||
val src = Channels.newChannel(input)
|
||||
src.read(dst)
|
||||
src.close()
|
||||
return dst.apply { rewind() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
material {
|
||||
name : baked_color,
|
||||
shadingModel : unlit,
|
||||
requires : [
|
||||
color
|
||||
]
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
// You must always call the prepareMaterial() function
|
||||
prepareMaterial(material);
|
||||
|
||||
// We set the material's color to the color interpolated from
|
||||
// the model's vertices
|
||||
material.baseColor = getColor();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
material {
|
||||
name : textured,
|
||||
shadingModel : unlit,
|
||||
parameters : [
|
||||
{
|
||||
type : samplerExternal,
|
||||
name : texture
|
||||
}
|
||||
],
|
||||
requires: [
|
||||
uv0
|
||||
]
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_texture, getUV0());
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0"/>
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
||||
@@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
@@ -1,3 +0,0 @@
|
||||
<resources>
|
||||
<string name="app_name">Texture Target</string>
|
||||
</resources>
|
||||
@@ -1,8 +0,0 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -17,7 +17,6 @@ include ':samples:sample-multi-view'
|
||||
include ':samples:sample-page-curl'
|
||||
include ':samples:sample-stream-test'
|
||||
include ':samples:sample-texture-view'
|
||||
include ':samples:sample-texture-target'
|
||||
include ':samples:sample-textured-object'
|
||||
include ':samples:sample-transparent-view'
|
||||
|
||||
|
||||
@@ -556,14 +556,11 @@ function build_android {
|
||||
archive_android "Release"
|
||||
fi
|
||||
|
||||
local root_dir=$(pwd)
|
||||
|
||||
pushd android > /dev/null
|
||||
|
||||
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/debug/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
@@ -576,7 +573,6 @@ function build_android {
|
||||
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/debug/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
:filamat-android:assembleDebug
|
||||
@@ -585,7 +581,6 @@ function build_android {
|
||||
for sample in ${ANDROID_SAMPLES}; do
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/debug/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${MATOPT_GRADLE_OPTION} \
|
||||
:samples:${sample}:assembleDebug
|
||||
@@ -618,7 +613,6 @@ function build_android {
|
||||
if [[ "${ISSUE_RELEASE_BUILD}" == "true" ]]; then
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/release/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
@@ -631,7 +625,6 @@ function build_android {
|
||||
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/release/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
:filamat-android:assembleRelease
|
||||
@@ -640,7 +633,6 @@ function build_android {
|
||||
for sample in ${ANDROID_SAMPLES}; do
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
|
||||
-Pcom.google.android.filament.tools-dir=${root_dir}/out/release/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${MATOPT_GRADLE_OPTION} \
|
||||
:samples:${sample}:assembleRelease
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
libs/viewer/test_settings
|
||||
filament/test/test_filament --gtest_filter=-FilamentTest.FroxelData:FilamentExposureWithEngineTest.SetExposure:FilamentExposureWithEngineTest.ComputeEV100:RenderingTest.*
|
||||
filament/test/test_material_parser
|
||||
libs/math/test_math
|
||||
libs/image/test_image compare libs/image/tests/reference/
|
||||
libs/utils/test_utils
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
FILAMENT_BOT_TOKEN=$2
|
||||
|
||||
set -ex
|
||||
|
||||
function update_to_main() {
|
||||
python3 docs_src/build/run.py
|
||||
mkdir -p tmp
|
||||
@@ -51,5 +49,3 @@ else
|
||||
echo "has edits (to /docs_src): ${HAS_EDITS}"
|
||||
echo "bypass: ${DO_BYPASS}"
|
||||
fi
|
||||
|
||||
set +ex
|
||||
|
||||
@@ -81,28 +81,6 @@ def pull_markdeep_docs():
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, directory=MARKDEEP_DIR, **kwargs)
|
||||
|
||||
def do_GET(self):
|
||||
print(f'================ {self.path}')
|
||||
# Use the checked-in markdeep since its locked to a version
|
||||
if self.path == '/third_party/markdeep/markdeep.min.js':
|
||||
file_path = f'{ROOT_DIR}/third_party/markdeep/markdeep.min.js'
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/javascript')
|
||||
self.send_header('Content-Length', len(content))
|
||||
self.end_headers()
|
||||
|
||||
# Send the file content
|
||||
self.wfile.write(content)
|
||||
except FileNotFoundError:
|
||||
self.send_error(404, 'File not found')
|
||||
else:
|
||||
# For all other paths, use the default behavior (serve from MARKDEEP_DIR)
|
||||
super().do_GET()
|
||||
|
||||
def start_server(port):
|
||||
"""Starts the web server in a separate thread."""
|
||||
httpd = Server(("", port), Handler)
|
||||
@@ -115,9 +93,6 @@ def pull_markdeep_docs():
|
||||
PORT = 12345
|
||||
httpd = start_server(PORT)
|
||||
|
||||
# Workaround for unknown dead-lock when the selenium tries to make request to the local server above.
|
||||
time.sleep(3)
|
||||
|
||||
# Set up Chrome options for headless mode
|
||||
chrome_options = Options()
|
||||
chrome_options.add_argument("--headless")
|
||||
@@ -132,8 +107,7 @@ def pull_markdeep_docs():
|
||||
# Open the URL with ?export, which markdeep will export the resulting html.
|
||||
driver.get(f"http://localhost:{PORT}/{doc}.md.html?export")
|
||||
|
||||
time.sleep(1.5)
|
||||
|
||||
time.sleep(3)
|
||||
# We extract the html from the resulting "page" (an html output itself).
|
||||
text = driver.find_elements(By.TAG_NAME, "pre")[0].text
|
||||
|
||||
|
||||
@@ -97,7 +97,6 @@ in table [standardProperties].
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**dispersion** | Strength of the dispersion effect for refractive objects, specified as 20/Abbe number
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
|
||||
[Table [standardProperties]: Properties of the standard model]
|
||||
@@ -127,7 +126,6 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
**absorption** | float3 | [0..n] |
|
||||
**microThickness** | float | [0..n] |
|
||||
**thickness** | float | [0..n] |
|
||||
**dispersion** | float | [0..n] | Realistic values are between [0, 1], with the exception of Rutile, which has a value of 2.04
|
||||
[Table [standardPropertiesTypes]: Range and type of the standard model's properties]
|
||||
|
||||
|
||||
@@ -155,14 +153,13 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
as-is, which can lead to physically impossible materials, however, this might be desirable
|
||||
for artistic reasons.
|
||||
|
||||
!!! Note: About `thickness`, `microThickness` and `dispersion` for refraction
|
||||
!!! Note: About `thickness` and `microThickness` for refraction
|
||||
`thickness` represents the thickness of solid objects in the direction of the normal, for
|
||||
satisfactory results, this should be provided per fragment (e.g.: as a texture) or at least per
|
||||
vertex. `microThickness` represent the thickness of the thin layer of an object, and can
|
||||
generally be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m,
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Dispersion controls the angular
|
||||
separation of colors transmitting through a volume, and can be set by a contant value.
|
||||
Currently `thickness` and `dispersion` are not used when `refractionType` is set to `thin`.
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Currently `thickness` is not
|
||||
used when `refractionType` is set to `thin`.
|
||||
|
||||
### Base color
|
||||
|
||||
@@ -654,23 +651,6 @@ the `refractionType` is set to `solid` and `absorption` coefficients are set.
|
||||
![Figure [varyingThickness]: `thickness` varying from 0.0 at the top of the prism to 3.0 at the
|
||||
bottom of the prism](images/material_thickness.png)
|
||||
|
||||
### Dispersion
|
||||
|
||||
The dispersion property controls the angular separation of colors transmitting through a relatively
|
||||
clear volume. It can only be used when `refractionType` is set to `volume`.
|
||||
Its value is specified as 20/Abbe number. When the value is zero, no dispersion is used.
|
||||
|
||||
Table [commonMatDispersion] describes acceptable dispersion values for various types of materials.
|
||||
|
||||
Material | Abbe Number (V) | Dispersion (20/V)
|
||||
--------------------------:|:------------------:|:-----------------
|
||||
Rutile | 9.8 | 2.04
|
||||
Polycarbonate | 32 | 0.625
|
||||
Diamond | 55 | 0.36
|
||||
Water | 55 | 0.36
|
||||
Crown Glass | 59 | 0.33
|
||||
[Table [commonMatDispersion]: Dispersion of common materials]
|
||||
|
||||
## Subsurface model
|
||||
|
||||
### Thickness
|
||||
@@ -2296,7 +2276,6 @@ struct MaterialInputs {
|
||||
float3 absorption; // default float3(0.0, 0.0, 0.0)
|
||||
float ior; // default: 1.5
|
||||
float microThickness; // default: 0.0, not available with refractionType "solid"
|
||||
float dispersion; // default: 0.0, not available with refractionType "thin"
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ set(SRCS
|
||||
src/Driver.cpp
|
||||
src/Handle.cpp
|
||||
src/HandleAllocator.cpp
|
||||
src/JobQueue.cpp
|
||||
src/ostream.cpp
|
||||
src/noop/NoopDriver.cpp
|
||||
src/noop/PlatformNoop.cpp
|
||||
@@ -60,7 +59,6 @@ set(PRIVATE_HDRS
|
||||
src/CompilerThreadPool.h
|
||||
src/DataReshaper.h
|
||||
src/DriverBase.h
|
||||
src/JobQueue.h
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -335,25 +333,13 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUVertexBufferInfo.h
|
||||
)
|
||||
if (WIN32)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatformWindows.h
|
||||
src/webgpu/platform/WebGPUPlatformWindows.cpp
|
||||
)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||
elseif (LINUX)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatformLinux.h
|
||||
src/webgpu/platform/WebGPUPlatformLinux.cpp
|
||||
)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformLinux.cpp)
|
||||
elseif (APPLE OR IOS)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatformApple.h
|
||||
src/webgpu/platform/WebGPUPlatformApple.mm
|
||||
)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformApple.mm)
|
||||
elseif (ANDROID)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatformAndroid.h
|
||||
src/webgpu/platform/WebGPUPlatformAndroid.cpp
|
||||
)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformAndroid.cpp)
|
||||
endif()
|
||||
|
||||
if (TNT_DEV)
|
||||
@@ -582,7 +568,6 @@ if (APPLE OR LINUX)
|
||||
test/test_ReadPixels.cpp
|
||||
test/test_BufferUpdates.cpp
|
||||
test/test_Callbacks.cpp
|
||||
test/test_JobQueue.cpp
|
||||
test/test_MemoryMappedBuffer.cpp
|
||||
test/test_MsaaSwapChain.cpp
|
||||
test/test_MRT.cpp
|
||||
|
||||
@@ -33,7 +33,8 @@ protected:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Platform Interface
|
||||
|
||||
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override;
|
||||
Driver* createDriver(void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) override;
|
||||
|
||||
int getOSVersion() const noexcept override;
|
||||
|
||||
@@ -42,7 +43,7 @@ protected:
|
||||
|
||||
void terminate() noexcept override;
|
||||
|
||||
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
|
||||
@@ -54,9 +54,9 @@ public:
|
||||
// a 3rd party library could be considered. However, this was a simple and
|
||||
// quick change and works for now.
|
||||
// gets the size (height and width) of the surface/window
|
||||
[[nodiscard]] virtual wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const = 0;
|
||||
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
|
||||
// either returns a valid surface or panics
|
||||
[[nodiscard]] virtual wgpu::Surface createSurface(void* nativeWindow, uint64_t flags) = 0;
|
||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||
// either returns a valid adapter or panics
|
||||
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
|
||||
// either returns a valid device or panics
|
||||
@@ -74,9 +74,10 @@ protected:
|
||||
[[nodiscard]] Driver* createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) override;
|
||||
|
||||
private:
|
||||
// returns adapter request option variations applicable for the particular
|
||||
// platform
|
||||
[[nodiscard]] virtual std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() = 0;
|
||||
[[nodiscard]] static std::vector<wgpu::RequestAdapterOptions> getAdapterOptions();
|
||||
|
||||
// we may consider having the driver own this in the future
|
||||
wgpu::Instance mInstance;
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatformAndroid : public WebGPUPlatform {
|
||||
public:
|
||||
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
|
||||
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
|
||||
|
||||
protected:
|
||||
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatformApple : public WebGPUPlatform {
|
||||
public:
|
||||
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
|
||||
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
|
||||
|
||||
protected:
|
||||
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatformLinux : public WebGPUPlatform {
|
||||
public:
|
||||
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
|
||||
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
|
||||
|
||||
protected:
|
||||
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatformWindows : public WebGPUPlatform {
|
||||
public:
|
||||
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
|
||||
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
|
||||
|
||||
protected:
|
||||
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JobQueue.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
JobQueue::JobQueue(PassKey) {}
|
||||
|
||||
JobQueue::JobId JobQueue::push(Job job, JobId const preIssuedJobId/* = InvalidJobId*/) {
|
||||
JobId jobId = preIssuedJobId;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mQueueMutex);
|
||||
if (mIsStopping) {
|
||||
return InvalidJobId;
|
||||
}
|
||||
|
||||
if (jobId == InvalidJobId) {
|
||||
jobId = genNextJobId();
|
||||
mJobsMap[jobId] = std::move(job);
|
||||
} else {
|
||||
// Use the job ID previously issued by `issueJobId()`
|
||||
auto it = mJobsMap.find(jobId);
|
||||
if (it == mJobsMap.end()) {
|
||||
// Pre-issued job does not exist, either users passed a wrong id (unlikely)
|
||||
// or the job must have been canceled (likely)
|
||||
return InvalidJobId;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(!static_cast<bool>(it->second))
|
||||
<< "pre-issued job has already been populated";
|
||||
it->second = std::move(job);
|
||||
}
|
||||
mJobOrder.push(jobId);
|
||||
}
|
||||
|
||||
// Always notify. A ThreadWorker might be waiting.
|
||||
mQueueCondition.notify_one();
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
JobQueue::Job JobQueue::pop(bool shouldBlock) {
|
||||
std::unique_lock<std::mutex> lock(mQueueMutex);
|
||||
|
||||
decltype(mJobsMap)::iterator it;
|
||||
|
||||
while (true) {
|
||||
if (shouldBlock) {
|
||||
// Wait only if we're in blocking mode and the queue is empty
|
||||
mQueueCondition.wait(lock, [this] { return !mJobOrder.empty() || mIsStopping; });
|
||||
}
|
||||
|
||||
if (mJobOrder.empty()) {
|
||||
// When `shouldBlock` is true, this means the queue is stopping now.
|
||||
// When `shouldBlock` is false, this means there's no job.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JobId jobId = mJobOrder.front();
|
||||
mJobOrder.pop();
|
||||
|
||||
it = mJobsMap.find(jobId);
|
||||
if (it != mJobsMap.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If execution reaches this line, the job must have been canceled right after being added.
|
||||
// Therefore, we should continue the loop and attempt to retrieve the next available job.
|
||||
}
|
||||
|
||||
Job job = std::move(it->second);
|
||||
mJobsMap.erase(it);
|
||||
return job;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<JobQueue::Job> JobQueue::popBatch(int const maxJobsToPop) {
|
||||
utils::FixedCapacityVector<Job> jobs;
|
||||
|
||||
if (UTILS_UNLIKELY(maxJobsToPop == 0)) {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mQueueMutex);
|
||||
if (mJobOrder.empty()) {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
// Calculate jobs to take. If maxJobsToPop is negative, we take all jobs.
|
||||
size_t jobsToTake = mJobOrder.size();
|
||||
if (0 < maxJobsToPop && maxJobsToPop < static_cast<int>(jobsToTake)) {
|
||||
jobsToTake = maxJobsToPop;
|
||||
}
|
||||
jobs.reserve(jobsToTake);
|
||||
|
||||
while (0 < jobsToTake && !mJobOrder.empty()) {
|
||||
JobId jobId = mJobOrder.front();
|
||||
mJobOrder.pop();
|
||||
|
||||
auto it = mJobsMap.find(jobId);
|
||||
if (UTILS_UNLIKELY(it == mJobsMap.end())) {
|
||||
// The job was probably canceled.
|
||||
continue;
|
||||
}
|
||||
|
||||
jobs.push_back(std::move(it->second));
|
||||
--jobsToTake;
|
||||
mJobsMap.erase(it);
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
JobQueue::JobId JobQueue::issueJobId() noexcept {
|
||||
std::lock_guard<std::mutex> lock(mQueueMutex);
|
||||
JobId const jobId = genNextJobId();
|
||||
// Preallocate a job, which serves two main purposes. It provides a valid jobId that can be
|
||||
// checked for integrity when passed to the `push` method, and it enables job cancellation for
|
||||
// tasks that are yet to be pushed.
|
||||
mJobsMap[jobId];
|
||||
return jobId;
|
||||
}
|
||||
|
||||
bool JobQueue::cancel(JobId const jobId) noexcept {
|
||||
std::lock_guard<std::mutex> lock(mQueueMutex);
|
||||
|
||||
auto it = mJobsMap.find(jobId);
|
||||
if (it == mJobsMap.end()) {
|
||||
return false; // Job not found, must have been completed or canceled.
|
||||
}
|
||||
|
||||
mJobsMap.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JobQueue::stop() noexcept {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mQueueMutex);
|
||||
mIsStopping = true;
|
||||
}
|
||||
mQueueCondition.notify_all(); // Wake up all waiting threads
|
||||
}
|
||||
|
||||
JobQueue::JobId JobQueue::genNextJobId() noexcept {
|
||||
// We assume this method is called within the critical section.
|
||||
JobId newJobId = mNextJobId++;
|
||||
// We assume the job ID won't overflow or wraps around to zero within the application's lifetime.
|
||||
assert_invariant(newJobId != InvalidJobId);
|
||||
return newJobId;
|
||||
}
|
||||
|
||||
JobWorker::~JobWorker() = default;
|
||||
|
||||
void JobWorker::terminate() {
|
||||
// This is called from workers `terminate()`, which may hinder the concurrent use of multiple
|
||||
// workers. Consider removing this line and require the owner/caller to explicitly invoke it to
|
||||
// enable multiple worker instances.
|
||||
if (mQueue) {
|
||||
mQueue->stop();
|
||||
}
|
||||
}
|
||||
|
||||
AmortizationWorker::AmortizationWorker(JobQueue::Ptr queue, PassKey)
|
||||
: JobWorker(std::move(queue)) {
|
||||
}
|
||||
|
||||
AmortizationWorker::~AmortizationWorker() = default;
|
||||
|
||||
void AmortizationWorker::process(int const jobCount) {
|
||||
if (!mQueue || jobCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jobCount == 1) {
|
||||
// Handle single job without vector allocation.
|
||||
if (auto job = mQueue->pop(false)) {
|
||||
job();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle batch (jobCount > 1 or jobCount < 0 for "all pending jobs")
|
||||
utils::FixedCapacityVector<JobQueue::Job> jobs = mQueue->popBatch(jobCount);
|
||||
if (jobs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& job: jobs) {
|
||||
job();
|
||||
}
|
||||
}
|
||||
|
||||
void AmortizationWorker::terminate() {
|
||||
JobWorker::terminate();
|
||||
|
||||
// Drain all pending jobs.
|
||||
process(-1);
|
||||
}
|
||||
|
||||
ThreadWorker::ThreadWorker(JobQueue::Ptr queue, Config config, PassKey)
|
||||
: JobWorker(std::move(queue)), mConfig(std::move(config)) {
|
||||
mThread = std::thread([this]() {
|
||||
utils::JobSystem::setThreadName(mConfig.name.data());
|
||||
utils::JobSystem::setThreadPriority(mConfig.priority);
|
||||
|
||||
if (mConfig.onBegin) {
|
||||
mConfig.onBegin();
|
||||
}
|
||||
|
||||
while (JobQueue::Job job = mQueue->pop(true)) {
|
||||
job();
|
||||
}
|
||||
|
||||
if (mConfig.onEnd) {
|
||||
mConfig.onEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ThreadWorker::~ThreadWorker() = default;
|
||||
|
||||
void ThreadWorker::terminate() {
|
||||
JobWorker::terminate();
|
||||
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_JOBQUEUE_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_JOBQUEUE_H
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/JobSystem.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* A thread-safe producer-consumer queue with batching capabilities.
|
||||
*
|
||||
* This class is thread-safe. All public methods can be called from any thread.
|
||||
*
|
||||
* This class is stateless regarding concurrency. The *caller* decides the blocking behavior and/or
|
||||
* batching when they call a 'pop' methods.
|
||||
*
|
||||
* A typical use case looks like this:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* #include "private/backend/JobQueue.h"
|
||||
* using namespace filament::backend;
|
||||
*
|
||||
* JobQueue::Ptr queue = JobQueue::create();
|
||||
* JobWorker::Ptr worker = AmortizationWorker::create(queue);
|
||||
* [ or JobWorker::Ptr worker = ThreadWorker::create(queue, config); ]
|
||||
*
|
||||
* void loop() {
|
||||
* worker->process(2); // for AmortizationWorker
|
||||
* }
|
||||
*
|
||||
* void cleanup() {
|
||||
* worker->terminate();
|
||||
* }
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* JobId id = queue->push([](){ ... });
|
||||
* queue->cancel(id);
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* JobId preIssuedId = queue->issueJobId();
|
||||
* JobId id = queue->push([](){ ... }, preIssuedId);
|
||||
* assert(id == preIssuedId);
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
class JobQueue {
|
||||
struct PassKey {};
|
||||
public:
|
||||
using Job = utils::Invocable<void()>;
|
||||
using JobId = uint32_t;
|
||||
using Ptr = std::shared_ptr<JobQueue>;
|
||||
static constexpr JobId InvalidJobId = std::numeric_limits<JobId>::max();
|
||||
|
||||
/**
|
||||
* Creates an instance of JobQueue. Users should call this to create one.
|
||||
* @return An instance of JobQueue
|
||||
*/
|
||||
static Ptr create() {
|
||||
return std::make_shared<JobQueue>(PassKey{});
|
||||
}
|
||||
|
||||
explicit JobQueue(PassKey); // This can be created only via `create()`
|
||||
|
||||
/**
|
||||
* Pushes a new job into queue.
|
||||
*
|
||||
* If the queue is in the process of shutting down (via a call to `stop`), this method does
|
||||
* nothing (a no-op) and returns an invalid job ID.
|
||||
*
|
||||
* @param job The function/lambda to be executed.
|
||||
* @param preIssuedJobId The previously issued job ID where this job is assigned to.
|
||||
* If the value is `InvalidJobId`, a new job ID is generated internally.
|
||||
* @return A new job ID. If a `preIssuedJobId` is provided, that specific ID is returned instead.
|
||||
*/
|
||||
JobId push(Job job, JobId preIssuedJobId = InvalidJobId);
|
||||
|
||||
/**
|
||||
* Retrieves the next job from queue.
|
||||
*
|
||||
* This method returns a valid job ID if there are pending jobs in the queue, even if the queue
|
||||
* is currently in the process of shutting down (via a call to `stop`).
|
||||
*
|
||||
* @param shouldBlock If true (typically used by ThreadWorker), waits for a job and returns it.
|
||||
* If false (typically used by AmortizationWorker), tries retrieving a job. But may return an
|
||||
* empty job if there's no pending job.
|
||||
* @return The next job. If an empty job is returned, it has different meaning depending on the
|
||||
* `shouldBlock` value:
|
||||
* - true: Stop processing (shutting down).
|
||||
* - false: No jobs currently in queue.
|
||||
*/
|
||||
Job pop(bool shouldBlock);
|
||||
|
||||
/**
|
||||
* Retrieves a batch of next jobs from queue. Always non-blocking.
|
||||
*
|
||||
* This method returns a valid job ID if there are pending jobs in the queue, even if the queue
|
||||
* is currently in the process of shutting down (via a call to `stop`).
|
||||
*
|
||||
* @param maxJobsToPop The maximum number of jobs to retrieve.
|
||||
* If < 0, retrieves all pending jobs.
|
||||
* @return A FixedCapacityVector<Job> containing the retrieved jobs.
|
||||
* Returns an empty vector if the queue is empty.
|
||||
*/
|
||||
utils::FixedCapacityVector<Job> popBatch(int maxJobsToPop);
|
||||
|
||||
/**
|
||||
* Generate a new job ID. This newly generated ID is meant to be used for the `preIssuedJobId`
|
||||
* parameter of `push` method.
|
||||
*
|
||||
* @return A new job ID.
|
||||
*/
|
||||
JobId issueJobId() noexcept;
|
||||
|
||||
/**
|
||||
* Cancels a job by its ID.
|
||||
*
|
||||
* @param jobId The job ID to cancel.
|
||||
* @return true if the job was found and cancelled, false otherwise.
|
||||
*/
|
||||
bool cancel(JobId jobId) noexcept;
|
||||
|
||||
/**
|
||||
* Signals the queue to shut down, after which no further jobs can be added using the `push`
|
||||
* method. but all jobs already pushed can still be processed using `pop`.
|
||||
*/
|
||||
void stop() noexcept;
|
||||
|
||||
private:
|
||||
JobQueue(const JobQueue&) = delete;
|
||||
JobQueue& operator=(const JobQueue&) = delete;
|
||||
|
||||
JobId genNextJobId() noexcept;
|
||||
|
||||
std::mutex mQueueMutex;
|
||||
std::condition_variable mQueueCondition;
|
||||
std::unordered_map<JobId, Job> mJobsMap;
|
||||
std::queue<JobId> mJobOrder;
|
||||
JobId mNextJobId = 0;
|
||||
bool mIsStopping = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base class for all worker types.
|
||||
*/
|
||||
class JobWorker {
|
||||
public:
|
||||
using Ptr = std::unique_ptr<JobWorker>;
|
||||
|
||||
virtual ~JobWorker();
|
||||
|
||||
/**
|
||||
* Processes a batch of jobs. (For non-threaded workers)
|
||||
* @param jobCount Max jobs to process (<= 0 for all).
|
||||
*/
|
||||
virtual void process(int jobCount) {}
|
||||
|
||||
/**
|
||||
* Terminates the worker.
|
||||
*/
|
||||
virtual void terminate();
|
||||
|
||||
protected:
|
||||
explicit JobWorker(JobQueue::Ptr queue) : mQueue(std::move(queue)) {}
|
||||
|
||||
JobQueue::Ptr mQueue;
|
||||
|
||||
private:
|
||||
JobWorker(const JobWorker&) = delete;
|
||||
JobWorker& operator=(const JobWorker&) = delete;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A non-threaded worker that consumes jobs in batches.
|
||||
*/
|
||||
class AmortizationWorker final : public JobWorker {
|
||||
struct PassKey {};
|
||||
public:
|
||||
/**
|
||||
* Creates an instance of AmortizationWorker. Users should call this to create one.
|
||||
* @return An instance of AmortizationWorker
|
||||
*/
|
||||
static Ptr create(JobQueue::Ptr queue) {
|
||||
return std::make_unique<AmortizationWorker>(std::move(queue), PassKey{});
|
||||
}
|
||||
|
||||
explicit AmortizationWorker(JobQueue::Ptr queue, PassKey); // This can be created only via `create()`
|
||||
|
||||
~AmortizationWorker() override;
|
||||
|
||||
/**
|
||||
* Polls the queue and executes a batch of jobs.
|
||||
*
|
||||
* @param jobCount The max number of jobs to process.
|
||||
* 0 = do nothing.
|
||||
* 1 = pop one (optimized).
|
||||
* > 1 = pop batch.
|
||||
* <= -1 = pop all.
|
||||
*/
|
||||
void process(int jobCount) override;
|
||||
|
||||
/**
|
||||
* Signals the queue to stop and drain all pending jobs.
|
||||
* This is safe to call multiple times.
|
||||
*/
|
||||
void terminate() override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A threaded worker that consumes jobs one by one, blocking when empty.
|
||||
*/
|
||||
class ThreadWorker final : public JobWorker {
|
||||
struct PassKey {};
|
||||
public:
|
||||
using Priority = utils::JobSystem::Priority;
|
||||
|
||||
/**
|
||||
* Config settings for the worker
|
||||
*/
|
||||
struct Config {
|
||||
std::string_view name = "";
|
||||
Priority priority = Priority::NORMAL;
|
||||
utils::Invocable<void()> onBegin; // Executed when the thread worker begins
|
||||
utils::Invocable<void()> onEnd; // Executed when the thread worker ends
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an instance of ThreadWorker. Users should call this to create one.
|
||||
* @return An instance of ThreadWorker
|
||||
*/
|
||||
static Ptr create(JobQueue::Ptr queue, Config config) {
|
||||
return std::make_unique<ThreadWorker>(std::move(queue), std::move(config), PassKey{});
|
||||
}
|
||||
|
||||
ThreadWorker(JobQueue::Ptr queue, Config config, PassKey); // This can be created only via `create()`
|
||||
|
||||
~ThreadWorker() override;
|
||||
|
||||
/**
|
||||
* Signals the queue to stop and joins the worker thread.
|
||||
* This is safe to call multiple times.
|
||||
*/
|
||||
void terminate() override;
|
||||
|
||||
private:
|
||||
Config mConfig;
|
||||
std::thread mThread;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PRIVATE_JOBQUEUE_H
|
||||
@@ -22,15 +22,7 @@
|
||||
|
||||
// We need to keep this up top for the linux (X11) name collisions.
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#if defined(__ANDROID__)
|
||||
#include "backend/platforms/WebGPUPlatformAndroid.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "backend/platforms/WebGPUPlatformApple.h"
|
||||
#elif defined(__linux__)
|
||||
#include "backend/platforms/WebGPUPlatformLinux.h"
|
||||
#elif defined(WIN32)
|
||||
#include "backend/platforms/WebGPUPlatformWindows.h"
|
||||
#endif
|
||||
#include "backend/platforms/WebGPUPlatform.h"
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
@@ -144,17 +136,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
|
||||
}
|
||||
if (*backend == Backend::WEBGPU) {
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#if defined(__ANDROID__)
|
||||
return new WebGPUPlatformAndroid();
|
||||
#elif defined(__APPLE__)
|
||||
return new WebGPUPlatformApple();
|
||||
#elif defined(__linux__)
|
||||
return new WebGPUPlatformLinux();
|
||||
#elif defined(WIN32)
|
||||
return new WebGPUPlatformWindows();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
return new WebGPUPlatform();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
@@ -312,8 +312,6 @@ void MetalDriver::execute(std::function<void(void)> const& fn) noexcept {
|
||||
}
|
||||
|
||||
void MetalDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
assert_invariant(mContext->currentDrawSwapChain);
|
||||
mContext->currentDrawSwapChain->setPresentationTime(monotonic_clock_ns);
|
||||
}
|
||||
|
||||
void MetalDriver::endFrame(uint32_t frameId) {
|
||||
|
||||
@@ -141,8 +141,6 @@ public:
|
||||
// FrameScheduledCallback.
|
||||
void present();
|
||||
|
||||
void setPresentationTime(int64_t timeNs) { presentationTimeNs = timeNs; }
|
||||
|
||||
NSUInteger getSurfaceWidth() const;
|
||||
NSUInteger getSurfaceHeight() const;
|
||||
NSUInteger getSampleCount() const;
|
||||
@@ -161,7 +159,7 @@ private:
|
||||
bool isCaMetalLayer() const { return type == SwapChainType::CAMETALLAYER; }
|
||||
bool isHeadless() const { return type == SwapChainType::HEADLESS; }
|
||||
|
||||
void scheduleFrameScheduledCallback(int64_t presentationTimeNs);
|
||||
void scheduleFrameScheduledCallback();
|
||||
void scheduleFrameCompletedCallback();
|
||||
|
||||
MetalAttachment acquireBaseDrawable();
|
||||
@@ -194,11 +192,6 @@ private:
|
||||
|
||||
int64_t abandonedUntilFrame = -1;
|
||||
|
||||
// If zero, the next presentation should happen as soon as possible.
|
||||
// Otherwise, this is the timestamp when the present should happen.
|
||||
// Resets to 0 after the present.
|
||||
int64_t presentationTimeNs = 0;
|
||||
|
||||
// These fields store a callback to notify the client that a frame is ready for presentation. If
|
||||
// !frameScheduled.callback, then the Metal backend automatically calls presentDrawable when the
|
||||
// frame is committed. Otherwise, the Metal backend will not automatically present the frame.
|
||||
|
||||
@@ -325,19 +325,11 @@ void MetalSwapChain::present() {
|
||||
if (frameCompleted.callback) {
|
||||
scheduleFrameCompletedCallback();
|
||||
}
|
||||
const auto timeNs = presentationTimeNs;
|
||||
presentationTimeNs = 0;
|
||||
if (drawable) {
|
||||
if (frameScheduled.callback) {
|
||||
scheduleFrameScheduledCallback(timeNs);
|
||||
scheduleFrameScheduledCallback();
|
||||
} else {
|
||||
if (presentationTimeNs) {
|
||||
const CFTimeInterval timeSeconds =
|
||||
(CFTimeInterval) presentationTimeNs / 1000000000.0;
|
||||
[getPendingCommandBuffer(&context) presentDrawable:drawable atTime:timeSeconds];
|
||||
} else {
|
||||
[getPendingCommandBuffer(&context) presentDrawable:drawable];
|
||||
}
|
||||
[getPendingCommandBuffer(&context) presentDrawable:drawable];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,22 +341,15 @@ public:
|
||||
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
|
||||
|
||||
static PresentDrawableData* create(id<CAMetalDrawable> drawable,
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags,
|
||||
int64_t presentationTimeNs) {
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags) {
|
||||
assert_invariant(drawableMutex);
|
||||
assert_invariant(driver);
|
||||
return new PresentDrawableData(drawable, drawableMutex, driver, flags, presentationTimeNs);
|
||||
return new PresentDrawableData(drawable, drawableMutex, driver, flags);
|
||||
}
|
||||
|
||||
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
|
||||
if (shouldPresent) {
|
||||
if (that->mPresentationTimeNs) {
|
||||
const CFTimeInterval timeSeconds =
|
||||
(CFTimeInterval) that->mPresentationTimeNs / 1000000000.0;
|
||||
[that->mDrawable presentAtTime:timeSeconds];
|
||||
} else {
|
||||
[that->mDrawable present];
|
||||
}
|
||||
[that->mDrawable present];
|
||||
}
|
||||
|
||||
if (that->mFlags & SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER) {
|
||||
@@ -382,12 +367,8 @@ public:
|
||||
|
||||
private:
|
||||
PresentDrawableData(id<CAMetalDrawable> drawable, std::shared_ptr<std::mutex> drawableMutex,
|
||||
MetalDriver* driver, uint64_t flags, int64_t presentationTimeNs)
|
||||
: mDrawable(drawable),
|
||||
mDrawableMutex(drawableMutex),
|
||||
mDriver(driver),
|
||||
mFlags(flags),
|
||||
mPresentationTimeNs(presentationTimeNs) {}
|
||||
MetalDriver* driver, uint64_t flags)
|
||||
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver), mFlags(flags) {}
|
||||
|
||||
static void cleanupAndDestroy(PresentDrawableData *that) {
|
||||
if (that->mDrawable) {
|
||||
@@ -403,7 +384,6 @@ private:
|
||||
std::shared_ptr<std::mutex> mDrawableMutex;
|
||||
MetalDriver* mDriver = nullptr;
|
||||
uint64_t mFlags = 0;
|
||||
int64_t mPresentationTimeNs = 0;
|
||||
};
|
||||
|
||||
void presentDrawable(bool presentFrame, void* user) {
|
||||
@@ -411,7 +391,7 @@ void presentDrawable(bool presentFrame, void* user) {
|
||||
PresentDrawableData::maybePresentAndDestroyAsync(presentDrawableData, presentFrame);
|
||||
}
|
||||
|
||||
void MetalSwapChain::scheduleFrameScheduledCallback(int64_t presentationTimeNs) {
|
||||
void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
if (!frameScheduled.callback) {
|
||||
return;
|
||||
}
|
||||
@@ -420,11 +400,8 @@ void MetalSwapChain::scheduleFrameScheduledCallback(int64_t presentationTimeNs)
|
||||
|
||||
struct Callback {
|
||||
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags,
|
||||
int64_t presentationTimeNs)
|
||||
: f(callback),
|
||||
data(PresentDrawableData::create(drawable, drawableMutex, driver, flags,
|
||||
presentationTimeNs)) {}
|
||||
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags)
|
||||
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver, flags)) {}
|
||||
std::shared_ptr<FrameScheduledCallback> f;
|
||||
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
|
||||
std::unique_ptr<PresentDrawableData> data;
|
||||
@@ -442,7 +419,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback(int64_t presentationTimeNs)
|
||||
uint64_t const flags = frameScheduled.flags;
|
||||
ReleasablePointer* callback = [[ReleasablePointer alloc]
|
||||
initWithPointer:new Callback(frameScheduled.callback, drawable, layerDrawableMutex,
|
||||
context.driver, flags, presentationTimeNs)];
|
||||
context.driver, flags)];
|
||||
|
||||
backend::CallbackHandler* handler = frameScheduled.handler;
|
||||
MetalDriver* driver = context.driver;
|
||||
|
||||
@@ -111,9 +111,22 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
id<MTLLibrary> library = nil;
|
||||
switch (program.getShaderLanguage()) {
|
||||
case ShaderLanguage::MSL: {
|
||||
// By default, Metal uses the most recent language version.
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
options.fastMathEnabled = YES;
|
||||
// The docs for MTLCompileOptions say that Metal uses the most
|
||||
// recent language version if it is not specified, but this is
|
||||
// not always true: it is possible for the environment to be set
|
||||
// up in a way where an older version is used by default. So we
|
||||
// explicitly set the minimum version we require.
|
||||
#if defined(FILAMENT_IOS)
|
||||
options.languageVersion = MTLLanguageVersion2_0;
|
||||
#else
|
||||
if (@available(macOS 11.0, *)) {
|
||||
options.languageVersion = MTLLanguageVersion2_3;
|
||||
} else {
|
||||
options.languageVersion = MTLLanguageVersion2_2;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert_invariant(source[source.size() - 1] == '\0');
|
||||
// the shader string is null terminated and the length includes the null character
|
||||
|
||||
@@ -142,18 +142,6 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
bool hasFences() const noexcept {
|
||||
#if defined(BACKEND_OPENGL_VERSION_GLES) && !defined(FILAMENT_IOS) && !defined(__EMSCRIPTEN__)
|
||||
# ifndef BACKEND_OPENGL_LEVEL_GLES30
|
||||
return false;
|
||||
# else
|
||||
return mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0;
|
||||
# endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr inline size_t getIndexForCap(GLenum cap) noexcept;
|
||||
constexpr static inline size_t getIndexForBufferTarget(GLenum target) noexcept;
|
||||
|
||||
|
||||
@@ -1344,6 +1344,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GLTexture* t = handle_cast<GLTexture*>(binfo.handle);
|
||||
|
||||
assert_invariant(t);
|
||||
assert_invariant(t->target != SamplerType::SAMPLER_EXTERNAL);
|
||||
assert_invariant(rt->width <= valueForLevel(binfo.level, t->width) &&
|
||||
rt->height <= valueForLevel(binfo.level, t->height));
|
||||
|
||||
@@ -1417,7 +1418,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
case SamplerType::SAMPLER_3D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// this could be GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_ARRAY
|
||||
target = t->gl.target;
|
||||
// note: multi-sampled textures can't have mipmaps
|
||||
@@ -1426,6 +1426,10 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
target = getCubemapTarget(binfo.layer);
|
||||
// note: cubemaps can't be multi-sampled
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// This is an error. We have asserted in debug build.
|
||||
target = t->gl.target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1454,7 +1458,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
||||
case GL_TEXTURE_2D:
|
||||
case GL_TEXTURE_EXTERNAL_OES:
|
||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||
case GL_TEXTURE_2D_MULTISAMPLE:
|
||||
#endif
|
||||
@@ -1462,9 +1465,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
|
||||
target, t->gl.id, binfo.level);
|
||||
} else {
|
||||
// in principle, it's possible to have a renderbuffer that's external
|
||||
// (it filament this never happens, currently)
|
||||
assert_invariant(target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES);
|
||||
assert_invariant(target == GL_TEXTURE_2D);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
|
||||
GL_RENDERBUFFER, t->gl.id);
|
||||
}
|
||||
@@ -1789,16 +1790,16 @@ void OpenGLDriver::createFenceR(Handle<HwFence> fh, ImmutableCString&& tag) {
|
||||
GLFence* const f = handle_cast<GLFence*>(fh);
|
||||
assert_invariant(f->state);
|
||||
|
||||
if (mPlatform.canCreateFence()) {
|
||||
std::lock_guard const lock(f->state->lock);
|
||||
f->fence = mPlatform.createFence();
|
||||
f->state->cond.notify_all();
|
||||
return;
|
||||
}
|
||||
bool const platformCanCreateFence = mPlatform.canCreateFence();
|
||||
|
||||
if (!mContext.hasFences()) {
|
||||
// this would happen on ES2
|
||||
f->state->status = FenceStatus::ERROR;
|
||||
if (mContext.isES2() || platformCanCreateFence) {
|
||||
std::lock_guard const lock(f->state->lock);
|
||||
if (platformCanCreateFence) {
|
||||
f->fence = mPlatform.createFence();
|
||||
f->state->cond.notify_all();
|
||||
} else {
|
||||
f->state->status = FenceStatus::ERROR;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1813,8 +1814,6 @@ void OpenGLDriver::createFenceR(Handle<HwFence> fh, ImmutableCString&& tag) {
|
||||
state->cond.notify_all();
|
||||
}
|
||||
});
|
||||
#else
|
||||
f->state->status = FenceStatus::ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2290,7 +2289,7 @@ mat3f OpenGLDriver::getStreamTransformMatrix(Handle<HwStream> sh) {
|
||||
void OpenGLDriver::destroyFence(Handle<HwFence> fh) {
|
||||
if (fh) {
|
||||
GLFence const* const f = handle_cast<GLFence*>(fh);
|
||||
if (mPlatform.canCreateFence()) {
|
||||
if (mPlatform.canCreateFence() || mContext.isES2()) {
|
||||
mPlatform.destroyFence(f->fence);
|
||||
}
|
||||
// note: it's invalid to call this during a fenceWait(fh) on another thread. For this
|
||||
@@ -2299,7 +2298,7 @@ void OpenGLDriver::destroyFence(Handle<HwFence> fh) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::fenceCancel(Handle<HwFence> fh) {
|
||||
void OpenGLDriver::fenceCancel(FenceHandle fh) {
|
||||
// Even though this is a synchronous call, the fence handle must be (and stay) valid
|
||||
assert_invariant(fh);
|
||||
GLFence const* const f = handle_cast<GLFence*>(fh);
|
||||
@@ -2332,30 +2331,30 @@ FenceStatus OpenGLDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
|
||||
// we don't need to acquire a reference to f->state here because `f` already has one, and
|
||||
// `f` is not supposed to become invalid while we wait.
|
||||
|
||||
if (mPlatform.canCreateFence()) {
|
||||
std::unique_lock lock(f->state->lock);
|
||||
if (f->fence == nullptr) {
|
||||
// we've been called before the fence was created asynchronously,
|
||||
// so we need to wait for that, before using the real fence.
|
||||
// By construction, "f" can't be destroyed while we wait, because its
|
||||
// construction call is in the queue and a destroy call will have to come later.
|
||||
f->state->cond.wait_until(lock, until, [f] {
|
||||
return f->fence != nullptr;
|
||||
});
|
||||
bool const platformCanCreateFence = mPlatform.canCreateFence();
|
||||
if (mContext.isES2() || platformCanCreateFence) {
|
||||
if (platformCanCreateFence) {
|
||||
std::unique_lock lock(f->state->lock);
|
||||
if (f->fence == nullptr) {
|
||||
// the only possible choice here is that we timed out
|
||||
assert_invariant(f->state->status == FenceStatus::TIMEOUT_EXPIRED);
|
||||
return FenceStatus::TIMEOUT_EXPIRED;
|
||||
// we've been called before the fence was created asynchronously,
|
||||
// so we need to wait for that, before using the real fence.
|
||||
// By construction, "f" can't be destroyed while we wait, because its
|
||||
// construction call is in the queue and a destroy call will have to come later.
|
||||
f->state->cond.wait_until(lock, until, [f] {
|
||||
return f->fence != nullptr;
|
||||
});
|
||||
if (f->fence == nullptr) {
|
||||
// the only possible choice here is that we timed out
|
||||
assert_invariant(f->state->status == FenceStatus::TIMEOUT_EXPIRED);
|
||||
return FenceStatus::TIMEOUT_EXPIRED;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
// here we know that we have the platform fence
|
||||
assert_invariant(f->fence);
|
||||
return mPlatform.waitFence(f->fence, timeout);
|
||||
}
|
||||
lock.unlock();
|
||||
// here we know that we have the platform fence
|
||||
assert_invariant(f->fence);
|
||||
return mPlatform.waitFence(f->fence, timeout);
|
||||
}
|
||||
|
||||
if (!mContext.hasFences()) {
|
||||
// this would be the case on ES2
|
||||
// platform doesn't support fences -- nothing we can do.
|
||||
return FenceStatus::ERROR;
|
||||
}
|
||||
|
||||
@@ -2366,8 +2365,6 @@ FenceStatus OpenGLDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
|
||||
return f->state->status != FenceStatus::TIMEOUT_EXPIRED;
|
||||
});
|
||||
return f->state->status;
|
||||
#else
|
||||
return FenceStatus::ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <new>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -329,40 +328,9 @@ void OpenGLProgram::updateUniforms(
|
||||
glUniform4iv(loc, u.size, bi);
|
||||
break;
|
||||
|
||||
case UniformType::MAT3: {
|
||||
// glUniformMatrix3fv expect a packed mat3, but the UBO is in std140
|
||||
// also, the GLES spec says:
|
||||
// Locations for sequential array indices are not required to be sequential.
|
||||
// The location for "a[1]" may or may not be equal to the location for "a[0]" + 1.
|
||||
// Furthermore, since unused elements at the end of uniform arrays may be trimmed
|
||||
// the location of the i + 1 array element may not be valid even if the location of
|
||||
// the i element is valid. As a direct consequence, the value of the location of
|
||||
// "a[0]" + 1 may refer to a different uniform entirely. Applications that wish to
|
||||
// set individual array elements should query the locations of each element
|
||||
// separately.
|
||||
|
||||
struct std140 {
|
||||
struct mat3 : public std::array<std::array<GLfloat, 4>, 3> {
|
||||
};
|
||||
};
|
||||
std140::mat3 const* const b = reinterpret_cast<std140::mat3 const*>(bf);
|
||||
|
||||
struct mat3 : public std::array<std::array<GLfloat, 3>, 3> {
|
||||
explicit mat3(std140::mat3 const& other) noexcept
|
||||
: std::array<std::array<GLfloat, 3>, 3>{ {
|
||||
{ { other[0][0], other[0][1], other[0][2] } },
|
||||
{ { other[1][0], other[1][1], other[1][2] } },
|
||||
{ { other[2][0], other[2][1], other[2][2] } }
|
||||
} } {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<mat3> const temp{ b, b + u.size };
|
||||
glUniformMatrix3fv(loc, u.size, GL_FALSE,
|
||||
reinterpret_cast<GLfloat const*>(temp.data()));
|
||||
case UniformType::MAT3:
|
||||
glUniformMatrix3fv(loc, u.size, GL_FALSE, bf);
|
||||
break;
|
||||
}
|
||||
|
||||
case UniformType::MAT4:
|
||||
glUniformMatrix4fv(loc, u.size, GL_FALSE, bf);
|
||||
break;
|
||||
|
||||
@@ -14,19 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <backend/platforms/PlatformWebGL.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
|
||||
const DriverConfig& driverConfig) {
|
||||
return createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
const Platform::DriverConfig& driverConfig) {
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
}
|
||||
|
||||
int PlatformWebGL::getOSVersion() const noexcept {
|
||||
@@ -38,7 +34,7 @@ void PlatformWebGL::terminate() noexcept {
|
||||
|
||||
Platform::SwapChain* PlatformWebGL::createSwapChain(
|
||||
void* nativeWindow, uint64_t flags) noexcept {
|
||||
return static_cast<SwapChain*>(nativeWindow);
|
||||
return (SwapChain*)nativeWindow;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformWebGL::createSwapChain(
|
||||
@@ -47,7 +43,7 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PlatformWebGL::destroySwapChain(SwapChain* swapChain) noexcept {
|
||||
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
@@ -55,7 +51,7 @@ bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformWebGL::commit(SwapChain* swapChain) noexcept {
|
||||
void PlatformWebGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -169,13 +169,6 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#define FVK_PROFILE_MARKER(marker)
|
||||
#endif
|
||||
|
||||
// In some instances, we want to enable systrace markers regardless of the build type (ie.e debug vs.
|
||||
// release) or the compile-time configuration (i.e. FVK_DEBUG_SYSTRACE).
|
||||
#define FVK_ALWAYS_ON_SYSTRACE_CONTEXT() FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FVK_ALWAYS_ON_SYSTRACE_START(marker) FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, marker)
|
||||
#define FVK_ALWAYS_ON_SYSTRACE_END() FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FVK_ALWAYS_ON_SYSTRACE_SCOPE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
|
||||
#ifndef FVK_HANDLE_ARENA_SIZE_IN_MB
|
||||
#define FVK_HANDLE_ARENA_SIZE_IN_MB 8
|
||||
#endif
|
||||
|
||||
@@ -22,12 +22,11 @@
|
||||
#include "VulkanBufferCache.h"
|
||||
#include "VulkanBufferProxy.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanDriverFactory.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/VulkanSamplerCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
@@ -1970,16 +1969,16 @@ void VulkanDriver::pushGroupMarker(char const* string) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.pushGroupMarker(string);
|
||||
#endif
|
||||
FVK_ALWAYS_ON_SYSTRACE_CONTEXT();
|
||||
FVK_ALWAYS_ON_SYSTRACE_START(string);
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START(string);
|
||||
}
|
||||
|
||||
void VulkanDriver::popGroupMarker(int) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.popGroupMarker();
|
||||
#endif
|
||||
FVK_ALWAYS_ON_SYSTRACE_CONTEXT();
|
||||
FVK_ALWAYS_ON_SYSTRACE_END();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::startCapture(int) {}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "backend/BufferObjectStreamDescriptor.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatformAndroid.h>
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformAndroid::getAdapterOptions() {
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
@@ -55,7 +55,7 @@ std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformAndroid::getAdapterOption
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatformAndroid::getSurfaceExtent(void* nativeWindow) const {
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||
return wgpu::Extent2D{
|
||||
.width = static_cast<uint32_t>(ANativeWindow_getWidth(window)),
|
||||
@@ -63,7 +63,7 @@ wgpu::Extent2D WebGPUPlatformAndroid::getSurfaceExtent(void* nativeWindow) const
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatformAndroid::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatformApple.h>
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformApple::getAdapterOptions() {
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
@@ -63,7 +63,7 @@ std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformApple::getAdapterOptions(
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatformApple::getSurfaceExtent(void* nativeWindow) const {
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
// Both IOS and MacOS expects CAMetalLayer.
|
||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||
return wgpu::Extent2D{
|
||||
@@ -72,7 +72,7 @@ wgpu::Extent2D WebGPUPlatformApple::getSurfaceExtent(void* nativeWindow) const {
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatformApple::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
// Both IOS and MacOS expects CAMetalLayer.
|
||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||
@@ -87,4 +87,4 @@ wgpu::Surface WebGPUPlatformApple::createSurface(void* nativeWindow, uint64_t /*
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <backend/platforms/WebGPUPlatformLinux.h>
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformLinux::getAdapterOptions() {
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
@@ -95,7 +95,7 @@ std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformLinux::getAdapterOptions(
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatformLinux::getSurfaceExtent(void* nativeWindow) const {
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
auto surfaceExtent = wgpu::Extent2D{};
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
@@ -130,7 +130,7 @@ wgpu::Extent2D WebGPUPlatformLinux::getSurfaceExtent(void* nativeWindow) const {
|
||||
return surfaceExtent;
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatformLinux::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatformWindows.h>
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformWindows::getAdapterOptions() {
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
@@ -61,7 +61,7 @@ std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformWindows::getAdapterOption
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatformWindows::getSurfaceExtent(void* nativeWindow) const {
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
HWND window = static_cast<HWND>(nativeWindow);
|
||||
RECT windowRect;
|
||||
GetWindowRect(window, &windowRect);
|
||||
@@ -71,7 +71,7 @@ wgpu::Extent2D WebGPUPlatformWindows::getSurfaceExtent(void* nativeWindow) const
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatformWindows::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
// TODO verify this is necessary for Dawn implementation as well:
|
||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "JobQueue.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST(JobQueue, PushAndPop) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v = 1; });
|
||||
JobQueue::Job job = queue->pop(false);
|
||||
ASSERT_TRUE(job);
|
||||
job();
|
||||
EXPECT_EQ(1, v);
|
||||
}
|
||||
|
||||
TEST(JobQueue, PopEmpty) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobQueue::Job job = queue->pop(false);
|
||||
ASSERT_FALSE(job);
|
||||
}
|
||||
|
||||
TEST(JobQueue, PopBatch) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
auto jobs = queue->popBatch(2);
|
||||
EXPECT_EQ(2, jobs.size());
|
||||
for (auto& job : jobs) {
|
||||
job();
|
||||
}
|
||||
EXPECT_EQ(2, v);
|
||||
|
||||
jobs = queue->popBatch(10);
|
||||
EXPECT_EQ(1, jobs.size());
|
||||
for (auto& job : jobs) {
|
||||
job();
|
||||
}
|
||||
EXPECT_EQ(3, v);
|
||||
}
|
||||
|
||||
TEST(JobQueue, PopAll) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
auto jobs = queue->popBatch(-1);
|
||||
EXPECT_EQ(3, jobs.size());
|
||||
for (auto& job : jobs) {
|
||||
job();
|
||||
}
|
||||
EXPECT_EQ(3, v);
|
||||
}
|
||||
|
||||
TEST(JobQueue, Cancel) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
int v = 0;
|
||||
|
||||
JobQueue::JobId idToCancel = queue->push([&v]() { v = 1; });
|
||||
queue->push([&v]() { v = 2; });
|
||||
|
||||
EXPECT_TRUE(queue->cancel(idToCancel));
|
||||
|
||||
auto jobs = queue->popBatch(-1);
|
||||
EXPECT_EQ(1, jobs.size());
|
||||
jobs[0]();
|
||||
EXPECT_EQ(2, v);
|
||||
}
|
||||
|
||||
TEST(JobQueue, CancelInvalid) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
EXPECT_FALSE(queue->cancel(123));
|
||||
}
|
||||
|
||||
TEST(JobQueue, Stop) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
int v = 0;
|
||||
queue->push([&v]() { v = 1; });
|
||||
queue->stop();
|
||||
// After stop, we can't push new jobs. This should be a no-op.
|
||||
JobQueue::JobId id = queue->push([&v]() { v = 2; });
|
||||
EXPECT_EQ(JobQueue::InvalidJobId, id);
|
||||
|
||||
auto job = queue->pop(false);
|
||||
EXPECT_TRUE(job);
|
||||
job();
|
||||
EXPECT_EQ(1, v);
|
||||
|
||||
job = queue->pop(false);
|
||||
EXPECT_FALSE(job);
|
||||
}
|
||||
|
||||
TEST(JobQueue, PreIssuedJobId) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobQueue::JobId preIssuedId = queue->issueJobId();
|
||||
JobQueue::JobId id = queue->push([]() {}, preIssuedId);
|
||||
EXPECT_EQ(id, preIssuedId);
|
||||
}
|
||||
|
||||
TEST(JobQueue, MultipleProducersConsumers) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
std::atomic_int v = {0};
|
||||
constexpr int NUM_THREADS = 4;
|
||||
constexpr int JOBS_PER_THREAD = 200;
|
||||
|
||||
// Multiple producers
|
||||
std::vector<std::thread> producers;
|
||||
std::atomic_bool doneProducing = false;
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
producers.emplace_back([&]() {
|
||||
for (int j = 0; j < JOBS_PER_THREAD; ++j) {
|
||||
queue->push([&v]() { v++; });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Multiple consumers
|
||||
std::thread blockingConsumer = std::thread([&]() {
|
||||
while (true) {
|
||||
if (auto job = queue->pop(true)) {
|
||||
job();
|
||||
} else {
|
||||
break; // This means the job queue is stopped.
|
||||
}
|
||||
}
|
||||
});
|
||||
std::thread nonBlockingConsumer = std::thread([&]() {
|
||||
while (true) {
|
||||
if (auto job = queue->pop(false)) {
|
||||
job();
|
||||
} else {
|
||||
if (doneProducing.load()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
});
|
||||
std::thread nonBlockingPopBatchConsumer = std::thread([&]() {
|
||||
while (true) {
|
||||
utils::FixedCapacityVector<JobQueue::Job> jobs = queue->popBatch(2);
|
||||
if (!jobs.empty()) {
|
||||
for (auto& job : jobs) {
|
||||
job();
|
||||
}
|
||||
} else {
|
||||
if (doneProducing.load()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Waiting for producers to complete pushing jobs
|
||||
for (auto& t : producers) {
|
||||
t.join();
|
||||
}
|
||||
doneProducing = true; // signal for non-blocking consumer
|
||||
queue->stop();
|
||||
|
||||
// Waiting for consumers to complete handling jobs
|
||||
blockingConsumer.join();
|
||||
nonBlockingConsumer.join();
|
||||
nonBlockingPopBatchConsumer.join();
|
||||
|
||||
EXPECT_EQ(NUM_THREADS * JOBS_PER_THREAD, v.load());
|
||||
}
|
||||
|
||||
|
||||
TEST(AmortizationWorker, Process) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobWorker::Ptr worker = AmortizationWorker::create(queue);
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
worker->process(2);
|
||||
EXPECT_EQ(2, v);
|
||||
|
||||
worker->process(1);
|
||||
EXPECT_EQ(3, v);
|
||||
|
||||
// No pending jobs, so it should be a no-op.
|
||||
worker->process(1);
|
||||
EXPECT_EQ(3, v);
|
||||
}
|
||||
|
||||
TEST(AmortizationWorker, ProcessAll) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobWorker::Ptr worker = AmortizationWorker::create(queue);
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
worker->process(-1);
|
||||
EXPECT_EQ(3, v);
|
||||
|
||||
// No pending jobs, so it should be a no-op.
|
||||
worker->process(1);
|
||||
EXPECT_EQ(3, v);
|
||||
}
|
||||
|
||||
TEST(AmortizationWorker, TerminateDrainsAllJobs) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobWorker::Ptr worker = AmortizationWorker::create(queue);
|
||||
int v = 0;
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
// `terminate` should drain all jobs
|
||||
worker->terminate();
|
||||
EXPECT_EQ(2, v);
|
||||
|
||||
// After terminate, pushing new jobs should not work.
|
||||
queue->push([&v]() { v++; });
|
||||
worker->process(1);
|
||||
EXPECT_EQ(2, v);
|
||||
}
|
||||
|
||||
|
||||
TEST(ThreadWorker, Process) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
JobWorker::Ptr worker = ThreadWorker::create(queue, {});
|
||||
std::atomic_int v = {0};
|
||||
|
||||
queue->push([&v]() { v++; });
|
||||
queue->push([&v]() { v++; });
|
||||
|
||||
// `terminate` should drain all jobs
|
||||
worker->terminate();
|
||||
EXPECT_EQ(2, v.load());
|
||||
|
||||
// After terminate, pushing new jobs should not work.
|
||||
queue->push([&v]() { v++; });
|
||||
worker->terminate();
|
||||
EXPECT_EQ(2, v.load());
|
||||
}
|
||||
|
||||
TEST(ThreadWorker, Callbacks) {
|
||||
JobQueue::Ptr queue = JobQueue::create();
|
||||
bool beginCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
ThreadWorker::Config config = {
|
||||
.name = "TestThread",
|
||||
.priority = ThreadWorker::Priority::NORMAL,
|
||||
.onBegin = [&beginCalled]() { beginCalled = true; },
|
||||
.onEnd = [&endCalled]() { endCalled = true; }
|
||||
};
|
||||
JobWorker::Ptr worker = ThreadWorker::create(queue, std::move(config));
|
||||
worker->terminate();
|
||||
|
||||
EXPECT_TRUE(beginCalled);
|
||||
EXPECT_TRUE(endCalled);
|
||||
}
|
||||
@@ -371,12 +371,7 @@ public:
|
||||
//! Indicates whether an existing parameter is a sampler or not.
|
||||
bool isSampler(const char* UTILS_NONNULL name) const noexcept;
|
||||
|
||||
/**
|
||||
* Returns a view of the material source (.mat which is a JSON-ish file) string,
|
||||
* if it has been set. Otherwise, it returns a view of an empty string.
|
||||
* The lifetime of the string_view is tied to the lifetime of the Material.
|
||||
*/
|
||||
std::string_view getSource() const noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the name of the transform field associated for the given sampler parameter.
|
||||
|
||||
@@ -131,20 +131,6 @@ public:
|
||||
*/
|
||||
Builder& color(math::float4 color) noexcept;
|
||||
|
||||
/**
|
||||
* Set the rendering priority of the Skybox. By default, it is set to the lowest
|
||||
* priority (7) such that the Skybox is always rendered after the opaque objects,
|
||||
* to reduce overdraw when depth culling is enabled.
|
||||
*
|
||||
* @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority
|
||||
* (rendered last).
|
||||
*
|
||||
* @return Builder reference for chaining calls.
|
||||
*
|
||||
* @see RenderableManager::Builder::priority()
|
||||
*/
|
||||
Builder& priority(uint8_t priority) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the Skybox object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -62,10 +62,7 @@ bool FrameSkipper::shouldRenderFrame(DriverApi& driver) const noexcept {
|
||||
// The fence hasn't signaled yet, skip this frame
|
||||
return false;
|
||||
}
|
||||
// If we get a FenceStatus::ERROR, it doesn't necessarily indicate a "bug", it could
|
||||
// just be that fences are not supported. Regardless, we should return `true` in that
|
||||
// case.
|
||||
assert_invariant(status != FenceStatus::TIMEOUT_EXPIRED);
|
||||
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -146,10 +146,6 @@ bool Material::isSampler(const char* name) const noexcept {
|
||||
return downcast(this)->isSampler(name);
|
||||
}
|
||||
|
||||
std::string_view Material::getSource() const noexcept {
|
||||
return downcast(this)->getSource();
|
||||
}
|
||||
|
||||
const char* Material::getParameterTransformName(const char* samplerName) const noexcept {
|
||||
return downcast(this)->getParameterTransformName(samplerName);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,6 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
|
||||
|
||||
MaterialParser::ParseResult const materialResult = materialParser->parse();
|
||||
|
||||
CString name;
|
||||
materialParser->getName(&name);
|
||||
if (UTILS_UNLIKELY(materialResult == MaterialParser::ParseResult::ERROR_MISSING_BACKEND)) {
|
||||
CString languageNames;
|
||||
for (auto it = languages.begin(); it != languages.end(); ++it) {
|
||||
@@ -54,7 +52,7 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||
<< "the material " << name.c_str_safe() << " was not built for any of the " << to_string(backend)
|
||||
<< "the material was not built for any of the " << to_string(backend)
|
||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||
}
|
||||
|
||||
@@ -63,13 +61,13 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
||||
<< "could not parse the material package for material " << name.c_str_safe();
|
||||
<< "could not parse the material package";
|
||||
|
||||
uint32_t version = 0;
|
||||
materialParser->getMaterialVersion(&version);
|
||||
FILAMENT_CHECK_POSTCONDITION(version == MATERIAL_VERSION)
|
||||
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
||||
<< version << " for material " << name.c_str_safe();
|
||||
<< version << ".";
|
||||
|
||||
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
|
||||
|
||||
@@ -259,8 +257,6 @@ void MaterialDefinition::processMain() {
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
perViewLayoutIndex = ColorPassDescriptorSet::getIndex(isLit, isSSR, hasFog);
|
||||
|
||||
mMaterialParser->getSourceShader(&source);
|
||||
}
|
||||
|
||||
void MaterialDefinition::processBlendingMode() {
|
||||
|
||||
@@ -113,7 +113,6 @@ struct MaterialDefinition {
|
||||
|
||||
utils::CString name;
|
||||
uint64_t cacheId = 0;
|
||||
utils::CString source;
|
||||
|
||||
private:
|
||||
friend class MaterialCache;
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
@@ -239,40 +237,6 @@ bool MaterialParser::getName(CString* cstring) const noexcept {
|
||||
return unflattener.read(cstring);
|
||||
}
|
||||
|
||||
bool MaterialParser::getSourceShader(CString* cstring) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSource);
|
||||
// Source material is optional, treat it as a success.
|
||||
if (start == end) return true;
|
||||
|
||||
Unflattener unflattener(start, end);
|
||||
|
||||
// First get the compressed blob.
|
||||
const char* compressed;
|
||||
size_t compressedSize = 0;
|
||||
if (!unflattener.read(&compressed, &compressedSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the bound and decompress it.
|
||||
const size_t decompressBound =
|
||||
ZSTD_getFrameContentSize(compressed, compressedSize);
|
||||
if (ZSTD_isError(decompressBound)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dst_buffer = std::make_unique<char[]>(decompressBound);
|
||||
const size_t decompressed =
|
||||
ZSTD_decompress(
|
||||
dst_buffer.get(), decompressBound,
|
||||
compressed, compressedSize);
|
||||
if (ZSTD_isError(decompressed)) {
|
||||
return false;
|
||||
}
|
||||
*cstring = CString { dst_buffer.get(), decompressed };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MaterialParser::getCacheId(uint64_t* cacheId) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialCacheId);
|
||||
if (start == end) return false;
|
||||
|
||||
@@ -145,8 +145,6 @@ public:
|
||||
return getMaterialChunk().hasShader(model, variant, stage);
|
||||
}
|
||||
|
||||
bool getSourceShader(utils::CString* cstring) const noexcept;
|
||||
|
||||
filaflat::MaterialChunk const& getMaterialChunk() const noexcept {
|
||||
return mImpl.mMaterialChunk;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/CallStack.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
@@ -133,7 +132,7 @@ struct Engine::BuilderDetails {
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
void* mSharedContext = nullptr;
|
||||
bool mPaused = false;
|
||||
std::unordered_map<CString, bool> mFeatureFlags;
|
||||
std::unordered_map<std::string_view, bool> mFeatureFlags;
|
||||
|
||||
static Config validateConfig(Config config) noexcept;
|
||||
};
|
||||
@@ -284,7 +283,7 @@ FEngine::FEngine(Builder const& builder) :
|
||||
|
||||
// update all the features flags specified in the builder
|
||||
for (auto const& feature : builder->mFeatureFlags) {
|
||||
auto* const p = getFeatureFlagPtr(feature.first.c_str_safe(), true);
|
||||
auto* const p = getFeatureFlagPtr(feature.first, true);
|
||||
if (p) {
|
||||
*p = feature.second;
|
||||
}
|
||||
@@ -1590,7 +1589,7 @@ Engine::Builder& Engine::Builder::paused(bool const paused) noexcept {
|
||||
}
|
||||
|
||||
Engine::Builder& Engine::Builder::feature(char const* name, bool const value) noexcept {
|
||||
mImpl->mFeatureFlags.emplace(name, value);
|
||||
mImpl->mFeatureFlags[name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@ struct Material::BuilderDetails {
|
||||
UboBatchingMode mUboBatchingMode = UboBatchingMode::DEFAULT;
|
||||
std::unordered_map<
|
||||
CString,
|
||||
std::variant<int32_t, float, bool>> mConstantSpecializations;
|
||||
std::variant<int32_t, float, bool>,
|
||||
CString::Hasher> mConstantSpecializations;
|
||||
};
|
||||
|
||||
FMaterial::DefaultMaterialBuilder::DefaultMaterialBuilder() {
|
||||
|
||||
@@ -252,10 +252,6 @@ public:
|
||||
return mUseUboBatching;
|
||||
}
|
||||
|
||||
std::string_view getSource() const noexcept {
|
||||
return mDefinition.source.c_str_safe();
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
void applyPendingEdits() noexcept;
|
||||
|
||||
|
||||
@@ -298,10 +298,6 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
TextureType const textureType = texture->getTextureType();
|
||||
SamplerType const samplerType = texture->getTarget();
|
||||
auto const& featureFlags = mMaterial->getEngine().features.engine.debug;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(texture->getUsage() & TextureUsage::SAMPLEABLE)
|
||||
<< "Texture for parameter \"" << name << "\"" << " is not SAMPLEABLE";
|
||||
|
||||
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
|
||||
DescriptorSet::isTextureCompatibleWithDescriptor(
|
||||
textureType, samplerType, descriptorType),
|
||||
|
||||
@@ -98,11 +98,15 @@ RenderTarget* RenderTarget::Builder::build(Engine& engine) {
|
||||
if (color.texture) {
|
||||
FILAMENT_CHECK_PRECONDITION(color.texture->getUsage() & TextureUsage::COLOR_ATTACHMENT)
|
||||
<< "Texture usage must contain COLOR_ATTACHMENT";
|
||||
FILAMENT_CHECK_PRECONDITION(color.texture->getTarget() != Texture::Sampler::SAMPLER_EXTERNAL)
|
||||
<< "Color attachment can't be an external texture";
|
||||
}
|
||||
|
||||
if (depth.texture) {
|
||||
FILAMENT_CHECK_PRECONDITION(depth.texture->getUsage() & TextureUsage::DEPTH_ATTACHMENT)
|
||||
<< "Texture usage must contain DEPTH_ATTACHMENT";
|
||||
FILAMENT_CHECK_PRECONDITION(depth.texture->getTarget() != Texture::Sampler::SAMPLER_EXTERNAL)
|
||||
<< "Depth attachment can't be an external texture";
|
||||
}
|
||||
|
||||
const size_t maxDrawBuffers = downcast(engine).getDriverApi().getMaxDrawBuffers();
|
||||
|
||||
@@ -51,7 +51,6 @@ struct Skybox::BuilderDetails {
|
||||
float4 mColor{0, 0, 0, 1};
|
||||
float mIntensity = FIndirectLight::DEFAULT_INTENSITY;
|
||||
bool mShowSun = false;
|
||||
uint8_t mPriority = 7;
|
||||
};
|
||||
|
||||
using BuilderType = Skybox;
|
||||
@@ -78,18 +77,13 @@ Skybox::Builder& Skybox::Builder::color(float4 const color) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Skybox::Builder& Skybox::Builder::priority(uint8_t const priority) noexcept {
|
||||
mImpl->mPriority = priority;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Skybox::Builder& Skybox::Builder::showSun(bool const show) noexcept {
|
||||
mImpl->mShowSun = show;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Skybox* Skybox::Builder::build(Engine& engine) {
|
||||
FTexture const* cubemap = downcast(mImpl->mEnvironmentMap);
|
||||
FTexture* cubemap = downcast(mImpl->mEnvironmentMap);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!cubemap || cubemap->isCubemap())
|
||||
<< "environment maps must be a cubemap";
|
||||
@@ -124,7 +118,7 @@ FSkybox::FSkybox(FEngine& engine, const Builder& builder) noexcept
|
||||
.material(0, mSkyboxMaterialInstance)
|
||||
.castShadows(false)
|
||||
.receiveShadows(false)
|
||||
.priority(builder->mPriority)
|
||||
.priority(0x7)
|
||||
.culling(false)
|
||||
.build(engine, mSkybox);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
|
||||
<< uint8_t(engine.getActiveFeatureLevel());
|
||||
|
||||
// SAMPLER_EXTERNAL implies external textures.
|
||||
// SAMPLER_EXTERNAL implies imported.
|
||||
if (mImpl->mTarget == SamplerType::SAMPLER_EXTERNAL) {
|
||||
mImpl->mExternal = true;
|
||||
}
|
||||
@@ -229,6 +229,7 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
maxLevelCount = FTexture::maxLevelCount(mImpl->mWidth, mImpl->mHeight);
|
||||
break;
|
||||
@@ -236,19 +237,11 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
maxLevelCount = FTexture::maxLevelCount(std::max(
|
||||
{ mImpl->mWidth, mImpl->mHeight, mImpl->mDepth }));
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// external samplers can't mipmap
|
||||
maxLevelCount = 1;
|
||||
break;
|
||||
}
|
||||
mImpl->mLevels = std::min(mImpl->mLevels, maxLevelCount);
|
||||
|
||||
if (mImpl->mUsage == TextureUsage::NONE) {
|
||||
mImpl->mUsage = TextureUsage::DEFAULT;
|
||||
if (mImpl->mExternal) {
|
||||
// external textures can't be uploadable
|
||||
mImpl->mUsage = TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
}
|
||||
|
||||
auto const& featureFlags = downcast(engine).features.engine.debug;
|
||||
@@ -669,6 +662,7 @@ bool FTexture::textureHandleCanMutate() const noexcept {
|
||||
}
|
||||
|
||||
void FTexture::updateLodRange(uint8_t const baseLevel, uint8_t const levelCount) noexcept {
|
||||
assert_invariant(!mExternal);
|
||||
if (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) {
|
||||
auto& range = mLodRange;
|
||||
uint8_t const last = int8_t(baseLevel + levelCount);
|
||||
|
||||
@@ -106,22 +106,13 @@ void DescriptorSet::commitSlow(DescriptorSetLayout const& layout,
|
||||
}
|
||||
});
|
||||
|
||||
// FIXME: see [b/468072646]
|
||||
// We only validate empty descriptors at FEATURE_LEVEL_1 and above.
|
||||
// This is because at FL0 it's expected that some descriptor won't be set. In theory, the
|
||||
// corresponding layouts should not even contain those descriptors. However, making that change
|
||||
// is difficult and risky, and will be done at a later time.
|
||||
// Note: that the correct fix is actually needed to properly support FL3 once we want
|
||||
// to take advantage of having more samplers.
|
||||
if (UTILS_LIKELY(driver.getFeatureLevel() > backend::FeatureLevel::FEATURE_LEVEL_0)) {
|
||||
auto const unsetValidDescriptors = layout.getValidDescriptors() & ~mValid;
|
||||
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty() && !mSetUndefinedParameterWarning)) {
|
||||
unsetValidDescriptors.forEachSetBit([&](auto i) {
|
||||
LOG(WARNING) << (layout.isSampler(i) ? "Sampler" : "Buffer") << " descriptor " << i
|
||||
<< " of " << mName.c_str() << " is not set. Please report this issue.";
|
||||
});
|
||||
mSetUndefinedParameterWarning = true;
|
||||
}
|
||||
auto const unsetValidDescriptors = layout.getValidDescriptors() & ~mValid;
|
||||
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty() && !mSetUndefinedParameterWarning)) {
|
||||
unsetValidDescriptors.forEachSetBit([&](auto i) {
|
||||
LOG(WARNING) << (layout.isSampler(i) ? "Sampler" : "Buffer") << " descriptor " << i
|
||||
<< " of " << mName.c_str() << " is not set. Please report this issue.";
|
||||
});
|
||||
mSetUndefinedParameterWarning = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,9 @@ if (TNT_DEV)
|
||||
filament_bimap_test.cpp
|
||||
filament_framegraph_test.cpp
|
||||
filament_test.cpp
|
||||
filament_test_material.cpp
|
||||
${RESGEN_SOURCE})
|
||||
|
||||
target_link_libraries(test_${TARGET} PRIVATE filamat filament gtest)
|
||||
target_link_libraries(test_${TARGET} PRIVATE filament gtest)
|
||||
target_compile_options(test_${TARGET} PRIVATE ${COMPILER_FLAGS})
|
||||
target_include_directories(test_${TARGET} PRIVATE ${RESOURCE_DIR})
|
||||
set_target_properties(test_${TARGET} PROPERTIES FOLDER Tests)
|
||||
@@ -93,7 +92,15 @@ endif()
|
||||
add_executable(test_material_parser
|
||||
filament_test_material_parser.cpp
|
||||
${RESGEN_SOURCE})
|
||||
target_link_libraries(test_material_parser PRIVATE filamat filament gtest)
|
||||
target_link_libraries(test_material_parser PRIVATE filament gtest)
|
||||
target_compile_options(test_material_parser PRIVATE ${COMPILER_FLAGS})
|
||||
target_include_directories(test_material_parser PRIVATE ${RESOURCE_DIR})
|
||||
set_target_properties(test_material_parser PROPERTIES FOLDER Tests)
|
||||
|
||||
add_executable(test_material
|
||||
filament_test_material.cpp
|
||||
${RESGEN_SOURCE})
|
||||
target_link_libraries(test_material PRIVATE filament gtest)
|
||||
target_compile_options(test_material PRIVATE ${COMPILER_FLAGS})
|
||||
target_include_directories(test_material PRIVATE ${RESOURCE_DIR})
|
||||
set_target_properties(test_material PROPERTIES FOLDER Tests)
|
||||
@@ -13,9 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
@@ -25,7 +24,7 @@
|
||||
using namespace filament;
|
||||
|
||||
TEST(MaterialTransformName, QuerySamplerWithTransform) {
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
Engine* engine = Engine::create(Engine::Backend::DEFAULT);
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_TRANSFORMNAME_DATA,
|
||||
@@ -40,7 +39,7 @@ TEST(MaterialTransformName, QuerySamplerWithTransform) {
|
||||
}
|
||||
|
||||
TEST(MaterialTransformName, QueryMultipleSamplersWithTransforms) {
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
Engine* engine = Engine::create(Engine::Backend::DEFAULT);
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_TRANSFORMNAME_DATA,
|
||||
@@ -56,7 +55,7 @@ TEST(MaterialTransformName, QueryMultipleSamplersWithTransforms) {
|
||||
}
|
||||
|
||||
TEST(MaterialTransformName, QuerySamplerWithoutTransform) {
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
Engine* engine = Engine::create(Engine::Backend::DEFAULT);
|
||||
Material* material = Material::Builder()
|
||||
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_TRANSFORMNAME_DATA,
|
||||
FILAMENT_TEST_RESOURCES_TEST_MATERIAL_TRANSFORMNAME_SIZE)
|
||||
@@ -70,7 +69,7 @@ TEST(MaterialTransformName, QuerySamplerWithoutTransform) {
|
||||
}
|
||||
|
||||
TEST(MaterialTransformName, QueryMultipleSamplersWithoutTransforms) {
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
Engine* engine = Engine::create(Engine::Backend::DEFAULT);
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_TRANSFORMNAME_DATA,
|
||||
@@ -85,49 +84,7 @@ TEST(MaterialTransformName, QueryMultipleSamplersWithoutTransforms) {
|
||||
Engine::destroy(engine);
|
||||
}
|
||||
|
||||
TEST(Material, MaterialWithSourceMaterialSuccessfullyRetrieveSource) {
|
||||
// Need to set a specific backend to create a proper MaterialParser.
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
|
||||
std::string shaderCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = vec4(1.);
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.init();
|
||||
builder.materialSource(shaderCode);
|
||||
filamat::Package result = builder.build(engine->getJobSystem());
|
||||
ASSERT_TRUE(result.isValid());
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(result.getData(), result.getSize())
|
||||
.build(*engine);
|
||||
ASSERT_NE(material, nullptr);
|
||||
|
||||
EXPECT_EQ(material->getSource(), shaderCode);
|
||||
|
||||
engine->destroy(material);
|
||||
Engine::destroy(engine);
|
||||
}
|
||||
|
||||
|
||||
TEST(Material, MaterialWithoutSourceMaterialReturnsEmptySource) {
|
||||
// Need to set a specific backend to create a proper MaterialParser.
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.init();
|
||||
filamat::Package result = builder.build(engine->getJobSystem());
|
||||
ASSERT_TRUE(result.isValid());
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(result.getData(), result.getSize())
|
||||
.build(*engine);
|
||||
ASSERT_NE(material, nullptr);
|
||||
|
||||
EXPECT_EQ(material->getSource(), "");
|
||||
|
||||
engine->destroy(material);
|
||||
Engine::destroy(engine);
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.68.3"
|
||||
spec.version = "1.68.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.68.3/filament-v1.68.3-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.68.0/filament-v1.68.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -101,8 +101,6 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
DictionaryMetalLibrary = charTo64bitNum("DIC_MLIB"),
|
||||
|
||||
MaterialCrc32 = charTo64bitNum("MAT_CRC "),
|
||||
|
||||
MaterialSource = charTo64bitNum("MAT_SRC "),
|
||||
};
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
@@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t {
|
||||
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
|
||||
using AttributeBitset = utils::bitset32;
|
||||
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 31;
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 30;
|
||||
enum class Property : uint8_t {
|
||||
BASE_COLOR, //!< float4, all shading models
|
||||
ROUGHNESS, //!< float, lit shading models only
|
||||
@@ -230,7 +230,6 @@ enum class Property : uint8_t {
|
||||
ABSORPTION, //!< float3, how much light is absorbed by the material
|
||||
TRANSMISSION, //!< float, how much light is refracted through the material
|
||||
IOR, //!< float, material's index of refraction
|
||||
DISPERSION, //!< float, material's dispersion
|
||||
MICRO_THICKNESS, //!< float, thickness of the thin layer
|
||||
BENT_NORMAL, //!< float3, all shading models only, except unlit
|
||||
SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth
|
||||
|
||||
@@ -16,7 +16,6 @@ set(HDRS
|
||||
set(COMMON_PRIVATE_HDRS
|
||||
src/eiff/Chunk.h
|
||||
src/eiff/ChunkContainer.h
|
||||
src/eiff/CompressedStringChunk.h
|
||||
src/eiff/DictionaryTextChunk.h
|
||||
src/eiff/Flattener.h
|
||||
src/eiff/LineDictionary.h
|
||||
@@ -29,7 +28,6 @@ set(COMMON_PRIVATE_HDRS
|
||||
set(COMMON_SRCS
|
||||
src/eiff/Chunk.cpp
|
||||
src/eiff/ChunkContainer.cpp
|
||||
src/eiff/CompressedStringChunk.cpp
|
||||
src/eiff/DictionaryTextChunk.cpp
|
||||
src/eiff/LineDictionary.cpp
|
||||
src/eiff/MaterialTextChunk.cpp
|
||||
@@ -83,7 +81,7 @@ include_directories(${CMAKE_BINARY_DIR})
|
||||
add_library(${TARGET} STATIC ${HDRS} ${PRIVATE_HDRS} ${SRCS})
|
||||
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
|
||||
target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v zstd)
|
||||
target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_link_libraries(${TARGET} libtint)
|
||||
@@ -135,7 +133,6 @@ set(FILAMAT_DEPS
|
||||
spirv-cross-core
|
||||
spirv-cross-glsl
|
||||
spirv-cross-msl
|
||||
zstd
|
||||
)
|
||||
|
||||
set(FILAMAT_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libfilamat_combined.a")
|
||||
|
||||
@@ -660,12 +660,6 @@ public:
|
||||
*/
|
||||
MaterialBuilder& useDefaultDepthVariant() noexcept;
|
||||
|
||||
/**
|
||||
* Sets the source ASCII material (aka .mat file).
|
||||
* The provided `source` string_view must remain valid until MaterialBuilder::build() is called.
|
||||
*/
|
||||
MaterialBuilder& materialSource(std::string_view source) noexcept;
|
||||
|
||||
/**
|
||||
* Build the material. If you are using the Filament engine with this library, you should use
|
||||
* the job system provided by Engine.
|
||||
@@ -911,7 +905,6 @@ private:
|
||||
|
||||
ShaderCode mMaterialFragmentCode;
|
||||
ShaderCode mMaterialVertexCode;
|
||||
std::string_view mMaterialSource;
|
||||
|
||||
PropertyList mProperties;
|
||||
ParameterList mParameters;
|
||||
|
||||
@@ -46,7 +46,6 @@ std::unordered_map<std::string, Property> Enums::mStringToProperty = {
|
||||
{ "absorption", Property::ABSORPTION },
|
||||
{ "transmission", Property::TRANSMISSION },
|
||||
{ "ior", Property::IOR },
|
||||
{ "dispersion", Property::DISPERSION },
|
||||
{ "microThickness", Property::MICRO_THICKNESS },
|
||||
{ "bentNormal", Property::BENT_NORMAL },
|
||||
{ "specularFactor", Property::SPECULAR_FACTOR },
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
#include "eiff/BlobDictionary.h"
|
||||
#include "eiff/ChunkContainer.h"
|
||||
#include "eiff/CompressedStringChunk.h"
|
||||
#include "eiff/DictionarySpirvChunk.h"
|
||||
#include "eiff/DictionaryTextChunk.h"
|
||||
#include "eiff/LineDictionary.h"
|
||||
@@ -1252,11 +1251,6 @@ MaterialBuilder& MaterialBuilder::useLegacyMorphing() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::materialSource(std::string_view source) noexcept {
|
||||
mMaterialSource = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Package MaterialBuilder::build(JobSystem& jobSystem) {
|
||||
if (materialBuilderClients == 0) {
|
||||
slog.e << "Error: MaterialBuilder::init() must be called before build()." << io::endl;
|
||||
@@ -1707,11 +1701,6 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
hasher({ frag.data(), frag.size() })));
|
||||
|
||||
container.emplace<uint64_t>(MaterialCacheId, materialId);
|
||||
if (!mMaterialSource.empty()) {
|
||||
container.push<CompressedStringChunk>(
|
||||
MaterialSource, mMaterialSource,
|
||||
CompressedStringChunk::CompressionLevel::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialBuilder::writeSurfaceChunks(ChunkContainer& container) const noexcept {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CompressedStringChunk.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zstd.h>
|
||||
|
||||
namespace filamat {
|
||||
|
||||
namespace {
|
||||
int toZstdCompressionLevel(CompressedStringChunk::CompressionLevel compressionLevel) {
|
||||
switch (compressionLevel) {
|
||||
case CompressedStringChunk::CompressionLevel::MIN:
|
||||
return ZSTD_minCLevel();
|
||||
case CompressedStringChunk::CompressionLevel::MAX:
|
||||
return ZSTD_maxCLevel();
|
||||
case CompressedStringChunk::CompressionLevel::DEFAULT:
|
||||
return ZSTD_defaultCLevel();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void CompressedStringChunk::flatten(filamat::Flattener& f) {
|
||||
const size_t bufferBound = ZSTD_compressBound(mString.size());
|
||||
std::vector<uint8_t> compressed(bufferBound);
|
||||
|
||||
const size_t compressedSize = ZSTD_compress(
|
||||
compressed.data(), compressed.size(),
|
||||
mString.data(), mString.size(),
|
||||
toZstdCompressionLevel(mCompressionLevel));
|
||||
|
||||
if (ZSTD_isError(compressedSize)) {
|
||||
utils::slog.e << "Error compressing the input string." << utils::io::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
f.writeBlob((const char*) compressed.data(), compressedSize);
|
||||
}
|
||||
|
||||
} // namespace filamat
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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_COMPRESSEDSTRINGCHUNK_H
|
||||
#define TNT_COMPRESSEDSTRINGCHUNK_H
|
||||
|
||||
#include "Chunk.h"
|
||||
#include "Flattener.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filamat {
|
||||
|
||||
class CompressedStringChunk final : public Chunk {
|
||||
public:
|
||||
enum class CompressionLevel { MIN, MAX, DEFAULT };
|
||||
CompressedStringChunk(
|
||||
ChunkType type, std::string_view string, CompressionLevel compressionLevel)
|
||||
: Chunk(type),
|
||||
mString(utils::CString(string.data(), string.size())),
|
||||
mCompressionLevel(compressionLevel) {}
|
||||
~CompressedStringChunk() override = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener& f) override;
|
||||
utils::CString mString;
|
||||
CompressionLevel mCompressionLevel;
|
||||
};
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
|
||||
#endif // TNT_COMPRESSEDSTRINGCHUNK_H
|
||||
@@ -1248,7 +1248,6 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
|
||||
case Property::ABSORPTION: return "ABSORPTION";
|
||||
case Property::TRANSMISSION: return "TRANSMISSION";
|
||||
case Property::IOR: return "IOR";
|
||||
case Property::DISPERSION: return "DISPERSION";
|
||||
case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
|
||||
case Property::BENT_NORMAL: return "BENT_NORMAL";
|
||||
case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR";
|
||||
|
||||
@@ -475,32 +475,6 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerTransmission) {
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, StaticCodeAnalyzerDispersion) {
|
||||
std::string fragmentCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.absorption = vec3(0.0);
|
||||
material.transmission = 0.96;
|
||||
material.ior = 1.33;
|
||||
material.dispersion = 0.33;
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderStage::FRAGMENT,
|
||||
fragmentCode, "", filamat::MaterialBuilder::Shading::LIT,
|
||||
filamat::MaterialBuilder::RefractionMode::CUBEMAP);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ABSORPTION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::TRANSMISSION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::IOR)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::DISPERSION)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatRoughness) {
|
||||
std::string fragmentCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
@@ -990,20 +964,6 @@ TEST_F(MaterialCompiler, FeatureLevel0Ess3CallFails) {
|
||||
EXPECT_FALSE(result.isValid());
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, EmbedMaterialSourceSucceeds) {
|
||||
std::string shaderCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = vec4(1.);
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.materialSource(shaderCode);
|
||||
|
||||
filamat::Package result = builder.build(*jobSystem);
|
||||
EXPECT_TRUE(result.isValid());
|
||||
}
|
||||
|
||||
#if FILAMENT_SUPPORTS_WEBGPU
|
||||
TEST_F(MaterialCompiler, WgslConversionBakedColor) {
|
||||
std::string bakedColorCodeFrag(R"(
|
||||
|
||||
@@ -157,7 +157,6 @@ public:
|
||||
|
||||
bool getInsertLineDirectives() const noexcept { return mInsertLineDirectives; }
|
||||
bool getInsertLineDirectiveChecks() const noexcept { return mInsertLineDirectiveChecks; }
|
||||
bool getIncludeSourceMaterial() const noexcept { return mIncludeSourceMaterial; }
|
||||
|
||||
protected:
|
||||
bool mDebug = false;
|
||||
@@ -180,7 +179,6 @@ protected:
|
||||
bool mIncludeEssl1 = true;
|
||||
bool mInsertLineDirectives = true;
|
||||
bool mInsertLineDirectiveChecks = true;
|
||||
bool mIncludeSourceMaterial = false;
|
||||
};
|
||||
|
||||
} // namespace matp
|
||||
|
||||
@@ -54,15 +54,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#if defined(__ANDROID__)
|
||||
#include "backend/platforms/WebGPUPlatformAndroid.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "backend/platforms/WebGPUPlatformApple.h"
|
||||
#elif defined(__linux__)
|
||||
#include "backend/platforms/WebGPUPlatformLinux.h"
|
||||
#elif defined(WIN32)
|
||||
#include "backend/platforms/WebGPUPlatformWindows.h"
|
||||
#endif
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
#endif
|
||||
|
||||
#include <filagui/ImGuiHelper.h>
|
||||
@@ -92,15 +84,7 @@ namespace {
|
||||
using namespace filament::backend;
|
||||
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#if defined(__ANDROID__)
|
||||
class FilamentAppWebGPUPlatform : public WebGPUPlatformAndroid {
|
||||
#elif defined(__APPLE__)
|
||||
class FilamentAppWebGPUPlatform : public WebGPUPlatformApple {
|
||||
#elif defined(__linux__)
|
||||
class FilamentAppWebGPUPlatform : public WebGPUPlatformLinux {
|
||||
#elif defined(WIN32)
|
||||
class FilamentAppWebGPUPlatform : public WebGPUPlatformWindows {
|
||||
#endif
|
||||
class FilamentAppWebGPUPlatform : public WebGPUPlatform {
|
||||
public:
|
||||
FilamentAppWebGPUPlatform(Config::WebGPUBackend backend)
|
||||
: mBackend(backend) {}
|
||||
|
||||
@@ -94,11 +94,10 @@ struct alignas(4) MaterialKey {
|
||||
bool hasSheen : 1;
|
||||
bool hasIOR : 1;
|
||||
bool hasVolume : 1;
|
||||
bool hasDispersion : 1;
|
||||
bool hasSpecular : 1;
|
||||
bool hasSpecularTexture : 1;
|
||||
bool hasSpecularColorTexture : 1;
|
||||
bool padding : 1;
|
||||
bool padding : 2;
|
||||
// -- 32 bit boundary --
|
||||
uint8_t specularTextureUV;
|
||||
uint8_t specularColorTextureUV;
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace filament::gltfio {
|
||||
struct ArchiveRequirements {
|
||||
Shading shadingModel;
|
||||
BlendingMode blendingMode;
|
||||
tsl::robin_map<utils::CString, bool> features;
|
||||
tsl::robin_map<utils::CString, bool, utils::CString::Hasher> features;
|
||||
};
|
||||
|
||||
} // namespace filament::uberz
|
||||
|
||||