Compare commits

..
Author SHA1 Message Date
Powei Feng 94cdc79ce3 osmesa: Enable Mesa/OSMesa rendering for mac
This commit enables local running of the renderdiff tests and also
for github workflows.
2025-03-18 17:01:22 -07:00
116 changed files with 573 additions and 3469 deletions
+5 -8
View File
@@ -106,19 +106,16 @@ jobs:
run: pip install mako setuptools pyyaml
- name: Run script
run: |
echo "$(brew --version)"
bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff_tests
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: ubuntu-22.04-8core
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: ubuntu-22.04-4core
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
-3
View File
@@ -7,6 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.58.1'
implementation 'com.google.android.filament:filament-android:1.58.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.58.1'
pod 'Filament', '~> 1.58.0'
```
## Documentation
-4
View File
@@ -7,10 +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.58.2
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
## v1.58.1
+3 -3
View File
@@ -94,7 +94,7 @@ buildscript {
ext.versions = [
'jdk': 17,
'minSdk': 26,
'minSdk': 21,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '2.0.21',
@@ -125,7 +125,7 @@ buildscript {
ext.cmakeArgs = [
"--no-warn-unused-cli",
"-DANDROID_PIE=ON",
"-DANDROID_PLATFORM=26",
"-DANDROID_PLATFORM=21",
"-DANDROID_STL=c++_static",
"-DFILAMENT_DIST_DIR=${filamentPath}".toString(),
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(),
@@ -200,7 +200,7 @@ subprojects {
ndkVersion versions.ndk
defaultConfig {
minSdkVersion 26
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
externalNativeBuild {
@@ -46,8 +46,9 @@ import static com.google.android.filament.Asserts.assertFloat4In;
*
* <h1>Performance</h1>
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
*
* <h1>Ordering</h1>
*
@@ -159,10 +160,6 @@ public class ColorGrading {
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is {@link QualityLevel#MEDIUM}.
*
* @param qualityLevel The desired quality of the color grading process
@@ -178,8 +175,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER
*
* @param format The desired format of the 3D LUT.
@@ -195,8 +190,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is 32
*
* @param dim The desired dimension of the LUT. Between 16 and 64.
@@ -623,3 +616,4 @@ public class ColorGrading {
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
}
@@ -31,6 +31,8 @@ package com.google.android.filament;
* <li>DisplayRangeToneMapper</li>
* </ul>
* </ul>
*
* You can create custom tone mapping operators by subclassing ToneMapper.
*/
public class ToneMapper {
private final long mNativeObject;
+1 -10
View File
@@ -4,8 +4,6 @@ project(filament-utils-android)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(IMAGEIO_DIR ../../libs/imageio)
set(CMAKE_SYSTEM_VERSION 26)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../gltfio-android ${CMAKE_CURRENT_BINARY_DIR}/gltfio-android)
add_library(camutils STATIC IMPORTED)
@@ -32,10 +30,6 @@ add_library(iblprefilter STATIC IMPORTED)
set_target_properties(iblprefilter PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament-iblprefilter.a)
add_library(bluevk STATIC IMPORTED)
set_target_properties(bluevk PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbluevk.a)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
@@ -63,8 +57,7 @@ target_include_directories(filament-utils-jni PRIVATE
..
../../filament/backend/include
${IMAGEIO_DIR}/include
../../libs/utils/include
../../libs/bluevk/include)
../../libs/utils/include)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.symbols)
@@ -78,6 +71,4 @@ target_link_libraries(filament-utils-jni
image
ktxreader
viewer
bluevk
android
)
@@ -12,10 +12,6 @@ android {
}
}
defaultConfig {
minSdkVersion 26
}
packagingOptions {
// No need to package up the following shared libs, which arise as a side effect of our
// externalNativeBuild dependencies. When clients pick and choose from project-level gradle
@@ -14,21 +14,12 @@
* limitations under the License.
*/
#include <android/hardware_buffer.h>
#include <android/hardware_buffer_jni.h>
#include <jni.h>
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/Skybox.h>
#include <backend/Platform.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <utils/Log.h>
#include <ktxreader/Ktx1Reader.h>
#include "common/NioUtils.h"
@@ -38,8 +29,6 @@ using namespace filament::math;
using namespace image;
using namespace ktxreader;
using namespace filament::backend;
jlong nCreateHDRTexture(JNIEnv* env, jclass,
jlong nativeEngine, jobject javaBuffer, jint remaining, jint internalFormat);
@@ -90,37 +79,6 @@ static jboolean nGetSphericalHarmonics(JNIEnv* env, jclass, jobject javaBuffer,
return success ? JNI_TRUE : JNI_FALSE;
}
static jlong nSetExternalImageOnTexture(JNIEnv* env, jclass, jlong nativeEngine, jlong nativeTexture,
jobject hardwareBuffer, jboolean srgb) {
utils::slog.e <<"--------- jni nSetExternalImageOnTexture" << utils::io::endl;
Engine* engine = (Engine*) nativeEngine;
Texture* texture = (Texture*) nativeTexture;
Platform* platform = engine->getPlatform();
AHardwareBuffer* nativeBuffer = nullptr;
if (__builtin_available(android 26, *)) {
nativeBuffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
}
utils::slog.e <<"--------- jni nSetExternalImageOnTexture buf=" << nativeBuffer << utils::io::endl;
if (!nativeBuffer) {
return 0;
}
if (engine->getBackend() == Backend::OPENGL) {
PlatformEGLAndroid* eglPlatform = (PlatformEGLAndroid*) platform;
auto ref = eglPlatform->createExternalImage(nativeBuffer, srgb == JNI_TRUE);
texture->setExternalImage(*engine, ref);
} else if (engine->getBackend() == Backend::VULKAN) {
VulkanPlatformAndroid* vulkanPlatform = (VulkanPlatformAndroid*) platform;
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, srgb == JNI_TRUE);
texture->setExternalImage(*engine, ref);
}
return 0;
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -150,14 +108,5 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
rc = env->RegisterNatives(hdrloaderClass, hdrMethods, sizeof(hdrMethods) / sizeof(JNINativeMethod));
if (rc != JNI_OK) return rc;
jclass loaderClass = env->FindClass("com/google/android/filament/utils/ExternalImage");
if (loaderClass == nullptr) return JNI_ERR;
static const JNINativeMethod methods[] = {
{ (char*) "nSetExternalImageOnTexture", (char*) "(JJLandroid/hardware/HardwareBuffer;Z)J",
reinterpret_cast<void*>(nSetExternalImageOnTexture) },
};
rc = env->RegisterNatives(loaderClass, methods, sizeof(methods) / sizeof(JNINativeMethod));
if (rc != JNI_OK) return rc;
return JNI_VERSION_1_6;
}
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2017 Romain Guy
*
* 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.utils
import android.hardware.HardwareBuffer
import com.google.android.filament.Engine
import com.google.android.filament.Texture
object ExternalImage {
fun setOnTexture(
engine: Engine,
texture: Texture,
buffer: HardwareBuffer,
srgb: Boolean,
) {
val nativeEngine = engine.nativeObject
val nativeTexture = texture.nativeObject
val l = nSetExternalImageOnTexture(nativeEngine, nativeTexture, buffer, srgb)
}
private external fun nSetExternalImageOnTexture(
nativeEngine: Long,
nativeTexture: Long,
buffer: HardwareBuffer,
srgb: Boolean,
): Long
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.58.1
VERSION_NAME=1.58.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -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,55 +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.externalimg'
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.externalimg"
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 deps.androidx.core
implementation project(':filament-android')
implementation project(':filament-utils-android')
}
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<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,247 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.externalimg
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.hardware.camera2.*
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.Surface
import androidx.core.content.ContextCompat
import android.Manifest
import android.graphics.ImageFormat
import android.hardware.HardwareBuffer
import android.media.ImageReader
import android.opengl.Matrix
import android.os.Build
import android.os.Looper
import androidx.annotation.RequiresApi
import com.google.android.filament.*
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
/**
* Toy class that handles all interaction with the Android camera2 API.
* Sets the "textureTransform" and "videoTexture" parameters on the given Filament material.
*/
class CameraHelper(val activity: Activity, private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance) {
private lateinit var cameraId: String
private lateinit var captureRequest: CaptureRequest
private val cameraOpenCloseLock = Semaphore(1)
private var backgroundHandler: Handler? = null
private var backgroundThread: HandlerThread? = null
private var cameraDevice: CameraDevice? = null
private var captureSession: CameraCaptureSession? = null
private var resolution = Size(640, 480)
private var filamentTexture: Texture? = null
private var filamentStream: Stream? = null
private val imageReader = ImageReader.newInstance(
resolution.width,
resolution.height,
ImageFormat.PRIVATE,
kImageReaderMaxImages,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
@Suppress("deprecation")
private val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(activity)
} else {
activity.windowManager.defaultDisplay!!
}
@RequiresApi(30)
class Api30Impl {
companion object {
fun getDisplay(context: Context) = context.display!!
}
}
private val cameraCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
this@CameraHelper.cameraDevice = cameraDevice
createCaptureSession()
}
override fun onDisconnected(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
cameraDevice.close()
this@CameraHelper.cameraDevice = null
}
override fun onError(cameraDevice: CameraDevice, error: Int) {
onDisconnected(cameraDevice)
this@CameraHelper.activity.finish()
}
}
/**
* Fetches the latest image (if any) from ImageReader and passes its HardwareBuffer to Filament.
*/
fun pushExternalImageToFilament() {
val stream = filamentStream
if (stream != null) {
imageReader.acquireLatestImage()?.also {
stream.setAcquiredImage(it.hardwareBuffer, Handler(Looper.getMainLooper())) {
it.close()
}
}
}
}
/**
* Finds the front-facing Android camera, requests permission, and sets up a listener that will
* start a capture session as soon as the camera is ready.
*/
fun openCamera() {
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
for (cameraId in manager.cameraIdList) {
val characteristics = manager.getCameraCharacteristics(cameraId)
val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)
if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) {
continue
}
this.cameraId = cameraId
Log.i(kLogTag, "Selected camera $cameraId.")
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
resolution = map.getOutputSizes(SurfaceTexture::class.java)[0]
Log.i(kLogTag, "Highest resolution is $resolution.")
}
} catch (e: CameraAccessException) {
Log.e(kLogTag, e.toString())
} catch (e: NullPointerException) {
Log.e(kLogTag, "Camera2 API is not supported on this device.")
}
val permission = ContextCompat.checkSelfPermission(this.activity, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(arrayOf(Manifest.permission.CAMERA), kRequestCameraPermission)
return
}
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw RuntimeException("Time out waiting to lock camera opening.")
}
manager.openCamera(cameraId, cameraCallback, backgroundHandler)
}
fun onResume() {
backgroundThread = HandlerThread("CameraBackground").also { it.start() }
backgroundHandler = Handler(backgroundThread?.looper!!)
}
fun onPause() {
backgroundThread?.quitSafely()
try {
backgroundThread?.join()
backgroundThread = null
backgroundHandler = null
} catch (e: InterruptedException) {
Log.e(kLogTag, e.toString())
}
}
fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray): Boolean {
if (requestCode == kRequestCameraPermission) {
if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Log.e(kLogTag, "Unable to obtain camera position.")
}
return true
}
return false
}
private fun createCaptureSession() {
filamentStream?.apply { filamentEngine.destroyStream(this) }
// [Re]create the Filament Stream object that gets bound to the Texture.
filamentStream = Stream.Builder().build(filamentEngine)
// Create the Filament Texture object if we haven't done so already.
if (filamentTexture == null) {
filamentTexture = Texture.Builder()
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
.format(Texture.InternalFormat.RGB8)
.build(filamentEngine)
}
// We are texturing a front-facing square shape so we need to generate a matrix that transforms (u, v, 0, 1)
// into a new UV coordinate according to the screen rotation and the aspect ratio of the camera image.
val aspectRatio = resolution.width.toFloat() / resolution.height.toFloat()
val textureTransform = FloatArray(16)
Matrix.setIdentityM(textureTransform, 0)
when (display.rotation) {
Surface.ROTATION_0 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 90.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f, 1.0f / aspectRatio, 1.0f)
}
Surface.ROTATION_90 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 1.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 180.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
Surface.ROTATION_270 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
}
// Connect the Stream to the Texture and the Texture to the MaterialInstance.
val sampler = TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR, TextureSampler.WrapMode.CLAMP_TO_EDGE)
filamentTexture!!.setExternalStream(filamentEngine, filamentStream!!)
filamentMaterial.setParameter("videoTexture", filamentTexture!!, sampler)
filamentMaterial.setParameter("textureTransform", MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
// Start the capture session. You could also use TEMPLATE_PREVIEW here.
val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
captureRequestBuilder.addTarget(imageReader.surface)
cameraDevice?.createCaptureSession(listOf(imageReader.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
if (cameraDevice == null) return
captureSession = cameraCaptureSession
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
captureRequest = captureRequestBuilder.build()
captureSession!!.setRepeatingRequest(captureRequest, null, backgroundHandler)
Log.i(kLogTag, "Created CaptureRequest.")
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Log.e(kLogTag, "onConfigureFailed")
}
}, null)
}
companion object {
private const val kLogTag = "CameraHelper"
private const val kRequestCameraPermission = 1
private const val kImageReaderMaxImages = 7
}
}
@@ -1,247 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.externalimg
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.hardware.camera2.*
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.Surface
import androidx.core.content.ContextCompat
import android.Manifest
import android.graphics.ImageFormat
import android.hardware.HardwareBuffer
import android.media.ImageReader
import android.opengl.Matrix
import android.os.Build
import android.os.Looper
import androidx.annotation.RequiresApi
import com.google.android.filament.*
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
/**
* Toy class that handles all interaction with the Android camera2 API.
* Sets the "textureTransform" and "videoTexture" parameters on the given Filament material.
*/
class CameraHelper(val activity: Activity, private val filamentEngine: Engine, private val filamentMaterial: MaterialInstance) {
private lateinit var cameraId: String
private lateinit var captureRequest: CaptureRequest
private val cameraOpenCloseLock = Semaphore(1)
private var backgroundHandler: Handler? = null
private var backgroundThread: HandlerThread? = null
private var cameraDevice: CameraDevice? = null
private var captureSession: CameraCaptureSession? = null
private var resolution = Size(640, 480)
private var filamentTexture: Texture? = null
private var filamentStream: Stream? = null
private val imageReader = ImageReader.newInstance(
resolution.width,
resolution.height,
ImageFormat.PRIVATE,
kImageReaderMaxImages,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
@Suppress("deprecation")
private val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(activity)
} else {
activity.windowManager.defaultDisplay!!
}
@RequiresApi(30)
class Api30Impl {
companion object {
fun getDisplay(context: Context) = context.display!!
}
}
private val cameraCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
this@CameraHelper.cameraDevice = cameraDevice
createCaptureSession()
}
override fun onDisconnected(cameraDevice: CameraDevice) {
cameraOpenCloseLock.release()
cameraDevice.close()
this@CameraHelper.cameraDevice = null
}
override fun onError(cameraDevice: CameraDevice, error: Int) {
onDisconnected(cameraDevice)
this@CameraHelper.activity.finish()
}
}
/**
* Fetches the latest image (if any) from ImageReader and passes its HardwareBuffer to Filament.
*/
fun pushExternalImageToFilament() {
val stream = filamentStream
if (stream != null) {
imageReader.acquireLatestImage()?.also {
stream.setAcquiredImage(it.hardwareBuffer, Handler(Looper.getMainLooper())) {
it.close()
}
}
}
}
/**
* Finds the front-facing Android camera, requests permission, and sets up a listener that will
* start a capture session as soon as the camera is ready.
*/
fun openCamera() {
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
for (cameraId in manager.cameraIdList) {
val characteristics = manager.getCameraCharacteristics(cameraId)
val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)
if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) {
continue
}
this.cameraId = cameraId
Log.i(kLogTag, "Selected camera $cameraId.")
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
resolution = map.getOutputSizes(SurfaceTexture::class.java)[0]
Log.i(kLogTag, "Highest resolution is $resolution.")
}
} catch (e: CameraAccessException) {
Log.e(kLogTag, e.toString())
} catch (e: NullPointerException) {
Log.e(kLogTag, "Camera2 API is not supported on this device.")
}
val permission = ContextCompat.checkSelfPermission(this.activity, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(arrayOf(Manifest.permission.CAMERA), kRequestCameraPermission)
return
}
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw RuntimeException("Time out waiting to lock camera opening.")
}
manager.openCamera(cameraId, cameraCallback, backgroundHandler)
}
fun onResume() {
backgroundThread = HandlerThread("CameraBackground").also { it.start() }
backgroundHandler = Handler(backgroundThread?.looper!!)
}
fun onPause() {
backgroundThread?.quitSafely()
try {
backgroundThread?.join()
backgroundThread = null
backgroundHandler = null
} catch (e: InterruptedException) {
Log.e(kLogTag, e.toString())
}
}
fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray): Boolean {
if (requestCode == kRequestCameraPermission) {
if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Log.e(kLogTag, "Unable to obtain camera position.")
}
return true
}
return false
}
private fun createCaptureSession() {
filamentStream?.apply { filamentEngine.destroyStream(this) }
// [Re]create the Filament Stream object that gets bound to the Texture.
filamentStream = Stream.Builder().build(filamentEngine)
// Create the Filament Texture object if we haven't done so already.
if (filamentTexture == null) {
filamentTexture = Texture.Builder()
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
.format(Texture.InternalFormat.RGB8)
.build(filamentEngine)
}
// We are texturing a front-facing square shape so we need to generate a matrix that transforms (u, v, 0, 1)
// into a new UV coordinate according to the screen rotation and the aspect ratio of the camera image.
val aspectRatio = resolution.width.toFloat() / resolution.height.toFloat()
val textureTransform = FloatArray(16)
Matrix.setIdentityM(textureTransform, 0)
when (display.rotation) {
Surface.ROTATION_0 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 90.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f, 1.0f / aspectRatio, 1.0f)
}
Surface.ROTATION_90 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 1.0f, 0.0f)
Matrix.rotateM(textureTransform, 0, 180.0f, 0.0f, 0.0f, 1.0f)
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
Surface.ROTATION_270 -> {
Matrix.translateM(textureTransform, 0, 1.0f, 0.0f, 0.0f)
Matrix.scaleM(textureTransform, 0, -1.0f / aspectRatio, 1.0f, 1.0f)
}
}
// Connect the Stream to the Texture and the Texture to the MaterialInstance.
val sampler = TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR, TextureSampler.WrapMode.CLAMP_TO_EDGE)
filamentTexture!!.setExternalStream(filamentEngine, filamentStream!!)
filamentMaterial.setParameter("videoTexture", filamentTexture!!, sampler)
filamentMaterial.setParameter("textureTransform", MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
// Start the capture session. You could also use TEMPLATE_PREVIEW here.
val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
captureRequestBuilder.addTarget(imageReader.surface)
cameraDevice?.createCaptureSession(listOf(imageReader.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
if (cameraDevice == null) return
captureSession = cameraCaptureSession
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
captureRequest = captureRequestBuilder.build()
captureSession!!.setRepeatingRequest(captureRequest, null, backgroundHandler)
Log.i(kLogTag, "Created CaptureRequest.")
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Log.e(kLogTag, "onConfigureFailed")
}
}, null)
}
companion object {
private const val kLogTag = "CameraHelper"
private const val kRequestCameraPermission = 1
private const val kImageReaderMaxImages = 7
}
}
@@ -1,193 +0,0 @@
import android.graphics.*
import android.hardware.HardwareBuffer
import android.media.Image
import android.media.ImageReader
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.view.Surface
import androidx.annotation.RequiresApi
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@RequiresApi(Build.VERSION_CODES.O)
object CanvasToHardwareBufferUtil {
private const val TAG = "CanvasToHardwareBufferKt"
private const val IMAGE_READER_TIMEOUT_MS = 3000L // Timeout for waiting buffer
fun drawToHardwareBuffer(
width: Int,
height: Int,
): HardwareBuffer? {
if (width <= 0 || height <= 0) {
Log.e(TAG, "Invalid dimensions: width=$width, height=$height")
return null
}
var handlerThread: HandlerThread? = null
var imageReader: ImageReader? = null
var surface: Surface? = null // Keep track for logging/debugging if needed
// Use var as it's assigned within the try block after future completion
var receivedHardwareBuffer: HardwareBuffer? = null
try {
// 1. Setup HandlerThread for ImageReader callbacks
handlerThread = HandlerThread("ImageReaderThreadKt").apply { start() }
val imageReaderHandler = Handler(handlerThread.looper)
// 2. Use CompletableFuture to wait for the buffer from the listener
val bufferFuture = CompletableFuture<HardwareBuffer>()
// 3. Create ImageReader
val usageFlags =
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or
HardwareBuffer.USAGE_GPU_COLOR_OUTPUT or
HardwareBuffer.USAGE_CPU_READ_RARELY // Adjust as needed
imageReader =
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1, usageFlags)
// 4. Set Listener to capture the buffer
imageReader.setOnImageAvailableListener({ reader ->
var image: Image? = null
var hardwareBuffer: HardwareBuffer? = null
try {
// Use `use` block for automatic image.close()
image = reader.acquireLatestImage()
if (image == null) {
Log.w(TAG, "ImageReader listener fired but no image available.")
// Complete exceptionally if buffer wasn't already completed.
bufferFuture.completeExceptionally(
RuntimeException("ImageReader listener fired but no image available"),
)
return@setOnImageAvailableListener
}
hardwareBuffer = image.hardwareBuffer
if (hardwareBuffer != null) {
// IMPORTANT: Don't close the HardwareBuffer here!
// Transfer ownership via the CompletableFuture.
if (!bufferFuture.isDone) { // Avoid completing more than once
bufferFuture.complete(hardwareBuffer)
} else {
// Future was already completed (maybe exceptionally), close this buffer
Log.w(TAG, "Future already done, closing redundant HardwareBuffer")
hardwareBuffer.close()
}
} else {
Log.e(TAG, "Failed to get HardwareBuffer from Image.")
if (!bufferFuture.isDone) {
bufferFuture.completeExceptionally(
RuntimeException("Failed to get HardwareBuffer from Image"),
)
}
}
} catch (e: Exception) {
Log.e(TAG, "Error in ImageReader listener", e)
if (!bufferFuture.isDone) {
bufferFuture.completeExceptionally(e) // Propagate error
}
// If we got the buffer but failed elsewhere, ensure it's closed
hardwareBuffer?.takeUnless { it.isClosed }?.close()
} finally {
// image?.close() // Handled by acquiring reader itself or image.use{} if used
image?.close() // Close image if not using `use` or if error before `use` finishes
}
}, imageReaderHandler)
// 5. Get the Surface to draw onto
surface =
imageReader.surface
?: throw RuntimeException("Failed to get Surface from ImageReader")
// 6. Lock Canvas and Draw
val canvas: Canvas? = surface.lockHardwareCanvas() // Use hardware accelerated canvas
if (canvas != null) {
try {
// --- Your Drawing Code Here ---
val paint =
Paint().apply {
isAntiAlias = true // Good practice
}
// Blue background
paint.color = Color.BLUE
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
// White text
paint.color = Color.WHITE
paint.textSize = 40f
paint.textAlign = Paint.Align.CENTER
canvas.drawText(
"Hello HardwareBuffer! (Kotlin)",
width / 2f,
height / 2f,
paint,
)
// --- End Drawing Code ---
} finally {
// 7. Unlock Canvas and Post
surface.unlockCanvasAndPost(canvas)
}
} else {
throw RuntimeException("Failed to lock Hardware Canvas")
}
// 8. Wait for the listener to provide the HardwareBuffer
try {
// Wait for the buffer; this blocks the current thread.
receivedHardwareBuffer =
bufferFuture.get(IMAGE_READER_TIMEOUT_MS, TimeUnit.MILLISECONDS)
// Ownership of receivedHardwareBuffer is now transferred to the caller
} catch (timeout: TimeoutException) {
Log.e(TAG, "Timeout waiting for HardwareBuffer from ImageReader listener")
bufferFuture.cancel(true) // Attempt to cancel listener processing
throw timeout // Re-throw
}
} catch (e: Exception) {
Log.e(TAG, "Failed to draw to HardwareBuffer", e)
// Ensure buffer is closed if acquired but an error occurred before returning it
receivedHardwareBuffer?.takeUnless { it.isClosed }?.close()
return null // Indicate failure
} finally {
// 9. Cleanup
try {
imageReader?.close() // Also releases the Surface implicitly
} catch (e: Exception) {
Log.e(TAG, "Error closing ImageReader", e)
}
try {
handlerThread?.quitSafely()
} catch (e: Exception) {
Log.e(TAG, "Error quitting HandlerThread", e)
}
// Note: Do NOT close receivedHardwareBuffer here if returning successfully.
// The caller is responsible for closing the returned buffer.
}
// Return the buffer; caller MUST close it.
return receivedHardwareBuffer
}
// --- Example Usage (must be called from appropriate context/thread, like a coroutine) ---
/*
@RequiresApi(Build.VERSION_CODES.O)
suspend fun exampleUsage() = withContext(Dispatchers.IO) { // Run blocking code off main thread
val myBuffer: HardwareBuffer? = CanvasToHardwareBufferUtil.drawToHardwareBuffer(640, 480)
// Use the 'use' extension function for automatic closing
myBuffer?.use { buffer ->
// ... Use the buffer (e.g., create an EGLImage, pass to Vulkan, etc.) ...
Log.d(TAG, "Successfully created HardwareBuffer: $buffer. Now using it...")
// buffer.close() // 'use' handles this automatically
} ?: run {
Log.e(TAG, "Failed to create HardwareBuffer.")
}
Log.d(TAG,"HardwareBuffer processing finished (buffer closed if obtained).")
}
*/
}
@@ -1,180 +0,0 @@
import android.graphics.*
import android.hardware.HardwareBuffer
import android.media.Image
import android.media.ImageReader
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.view.Surface
import androidx.annotation.RequiresApi
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@RequiresApi(Build.VERSION_CODES.O)
object CanvasToHardwareBufferUtil {
private const val TAG = "CanvasToHardwareBufferKt"
private const val IMAGE_READER_TIMEOUT_MS = 3000L // Timeout for waiting buffer
fun drawToHardwareBuffer(width: Int, height: Int): HardwareBuffer? {
if (width <= 0 || height <= 0) {
Log.e(TAG, "Invalid dimensions: width=$width, height=$height")
return null
}
var handlerThread: HandlerThread? = null
var imageReader: ImageReader? = null
var surface: Surface? = null // Keep track for logging/debugging if needed
// Use var as it's assigned within the try block after future completion
var receivedHardwareBuffer: HardwareBuffer? = null
try {
// 1. Setup HandlerThread for ImageReader callbacks
handlerThread = HandlerThread("ImageReaderThreadKt").apply { start() }
val imageReaderHandler = Handler(handlerThread.looper)
// 2. Use CompletableFuture to wait for the buffer from the listener
val bufferFuture = CompletableFuture<HardwareBuffer>()
// 3. Create ImageReader
val usageFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or
HardwareBuffer.USAGE_GPU_COLOR_OUTPUT or
HardwareBuffer.USAGE_CPU_READ_RARELY // Adjust as needed
imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1, usageFlags)
// 4. Set Listener to capture the buffer
imageReader.setOnImageAvailableListener({ reader ->
var image: Image? = null
var hardwareBuffer: HardwareBuffer? = null
try {
// Use `use` block for automatic image.close()
image = reader.acquireLatestImage()
if (image == null) {
Log.w(TAG, "ImageReader listener fired but no image available.")
// Complete exceptionally if buffer wasn't already completed.
bufferFuture.completeExceptionally(RuntimeException("ImageReader listener fired but no image available"))
return@setOnImageAvailableListener
}
hardwareBuffer = image.hardwareBuffer
if (hardwareBuffer != null) {
// IMPORTANT: Don't close the HardwareBuffer here!
// Transfer ownership via the CompletableFuture.
if (!bufferFuture.isDone) { // Avoid completing more than once
bufferFuture.complete(hardwareBuffer)
} else {
// Future was already completed (maybe exceptionally), close this buffer
Log.w(TAG, "Future already done, closing redundant HardwareBuffer")
hardwareBuffer.close()
}
} else {
Log.e(TAG, "Failed to get HardwareBuffer from Image.")
if (!bufferFuture.isDone) {
bufferFuture.completeExceptionally(RuntimeException("Failed to get HardwareBuffer from Image"))
}
}
} catch (e: Exception) {
Log.e(TAG, "Error in ImageReader listener", e)
if (!bufferFuture.isDone) {
bufferFuture.completeExceptionally(e) // Propagate error
}
// If we got the buffer but failed elsewhere, ensure it's closed
hardwareBuffer?.takeUnless { it.isClosed }?.close()
} finally {
// image?.close() // Handled by acquiring reader itself or image.use{} if used
image?.close() // Close image if not using `use` or if error before `use` finishes
}
}, imageReaderHandler)
// 5. Get the Surface to draw onto
surface = imageReader.surface ?: throw RuntimeException("Failed to get Surface from ImageReader")
// 6. Lock Canvas and Draw
val canvas: Canvas? = surface.lockHardwareCanvas() // Use hardware accelerated canvas
if (canvas != null) {
try {
// --- Your Drawing Code Here ---
val paint = Paint().apply {
isAntiAlias = true // Good practice
}
// Blue background
paint.color = Color.BLUE
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
// White text
paint.color = Color.WHITE
paint.textSize = 40f
paint.textAlign = Paint.Align.CENTER
canvas.drawText("Hello HardwareBuffer! (Kotlin)", width / 2f, height / 2f, paint)
// --- End Drawing Code ---
} finally {
// 7. Unlock Canvas and Post
surface.unlockCanvasAndPost(canvas)
}
} else {
throw RuntimeException("Failed to lock Hardware Canvas")
}
// 8. Wait for the listener to provide the HardwareBuffer
try {
// Wait for the buffer; this blocks the current thread.
receivedHardwareBuffer = bufferFuture.get(IMAGE_READER_TIMEOUT_MS, TimeUnit.MILLISECONDS)
// Ownership of receivedHardwareBuffer is now transferred to the caller
} catch(timeout: TimeoutException) {
Log.e(TAG, "Timeout waiting for HardwareBuffer from ImageReader listener")
bufferFuture.cancel(true) // Attempt to cancel listener processing
throw timeout // Re-throw
}
} catch (e: Exception) {
Log.e(TAG, "Failed to draw to HardwareBuffer", e)
// Ensure buffer is closed if acquired but an error occurred before returning it
receivedHardwareBuffer?.takeUnless { it.isClosed }?.close()
return null // Indicate failure
} finally {
// 9. Cleanup
try {
imageReader?.close() // Also releases the Surface implicitly
} catch (e: Exception) {
Log.e(TAG, "Error closing ImageReader", e)
}
try {
handlerThread?.quitSafely()
} catch (e: Exception) {
Log.e(TAG, "Error quitting HandlerThread", e)
}
// Note: Do NOT close receivedHardwareBuffer here if returning successfully.
// The caller is responsible for closing the returned buffer.
}
// Return the buffer; caller MUST close it.
return receivedHardwareBuffer
}
// --- Example Usage (must be called from appropriate context/thread, like a coroutine) ---
/*
@RequiresApi(Build.VERSION_CODES.O)
suspend fun exampleUsage() = withContext(Dispatchers.IO) { // Run blocking code off main thread
val myBuffer: HardwareBuffer? = CanvasToHardwareBufferUtil.drawToHardwareBuffer(640, 480)
// Use the 'use' extension function for automatic closing
myBuffer?.use { buffer ->
// ... Use the buffer (e.g., create an EGLImage, pass to Vulkan, etc.) ...
Log.d(TAG, "Successfully created HardwareBuffer: $buffer. Now using it...")
// buffer.close() // 'use' handles this automatically
} ?: run {
Log.e(TAG, "Failed to create HardwareBuffer.")
}
Log.d(TAG,"HardwareBuffer processing finished (buffer closed if obtained).")
}
*/
}
@@ -1,469 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.externalimg
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 androidx.core.app.ActivityCompat
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.*
import com.google.android.filament.VertexBuffer.*
import com.google.android.filament.android.DisplayHelper
import com.google.android.filament.android.FilamentHelper
import com.google.android.filament.android.UiHelper
import com.google.android.filament.utils.ExternalImage
import com.google.android.filament.utils.Utils
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.Channels
import kotlin.math.*
import android.util.Log
class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallback {
companion object {
init {
Filament.init()
}
}
private var TAG = "filament.externalimg"
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
// This is the Filament camera, not the phone camera. :)
private lateinit var camera: Camera
private var filamentTexture: Texture? = null
// Other Filament objects:
private lateinit var material: Material
private lateinit var materialInstance: MaterialInstance
private lateinit var vertexBuffer: VertexBuffer
private lateinit var indexBuffer: IndexBuffer
// Filament entity representing a renderable object
@Entity private var renderable = 0
@Entity private var light = 0
private var myCount : Int = 0
// A swap chain is Filament's representation of a surface
private var swapChain: SwapChain? = null
// Performs the rendering and schedules new frames
private val frameScheduler = FrameCallback()
private val animator = ValueAnimator.ofFloat(0.0f, 50.0f)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Utils.init()
surfaceView = SurfaceView(this)
setContentView(surfaceView)
choreographer = Choreographer.getInstance()
displayHelper = DisplayHelper(this)
setupSurfaceView()
setupFilament()
setupView()
setupScene()
// ExternalImage.setOnTexture(engine,, texture, buffer, srgb)
// cameraHelper = CameraHelper(this, engine, materialInstance)
// cameraHelper.openCamera()
}
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())
}
private fun setupView() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
view.camera = camera
view.scene = scene
}
private fun setupScene() {
loadMaterial()
setupMaterial()
createMesh()
// To create a renderable we first create a generic entity
renderable = EntityManager.get().create()
// We then create a renderable component on that entity
// A renderable is made of several primitives; in this case we declare only 1
// If we wanted each face of the cube to have a different material, we could
// declare 6 primitives (1 per face) and give each of them a different material
// instance, setup with different parameters
RenderableManager.Builder(1)
// Overall bounding box of the renderable
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f))
// Sets the mesh data of the first primitive, 6 faces of 6 indices each
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 6 * 6)
// Sets the material of the first primitive
.material(0, materialInstance)
.build(engine, renderable)
// Add the entity to the scene to render it
scene.addEntity(renderable)
// We now need a light, let's create a directional light
light = EntityManager.get().create()
// Create a color from a temperature (5,500K)
val (r, g, b) = Colors.cct(5_500.0f)
LightManager.Builder(LightManager.Type.DIRECTIONAL)
.color(r, g, b)
// Intensity of the sun in lux on a clear day
.intensity(110_000.0f)
// The direction is normalized on our behalf
.direction(0.0f, -0.5f, -1.0f)
.castShadows(true)
.build(engine, light)
// Add the entity to the scene to light it
scene.addEntity(light)
// Set the exposure on the camera, this exposure follows the sunny f/16 rule
// Since we've defined a light that has the same intensity as the sun, it
// guarantees a proper exposure
camera.setExposure(16.0f, 1.0f / 125.0f, 100.0f)
// Move the camera back to see the object
camera.lookAt(0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
startAnimation()
}
private fun loadMaterial() {
readUncompressedAsset("materials/lit.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
}
}
private fun setupMaterial() {
materialInstance = material.createInstance()
materialInstance.setParameter("baseColor", Colors.RgbType.SRGB, 1.0f, 0.85f, 0.57f)
materialInstance.setParameter("roughness", 0.3f)
val textureTransform = FloatArray(16)
Matrix.setIdentityM(textureTransform, 0)
filamentTexture = Texture.Builder()
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
.width(400)
.height(400)
.format(Texture.InternalFormat.RGBA8)
.build(engine)
val sampler = TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR, TextureSampler.WrapMode.CLAMP_TO_EDGE)
materialInstance.setParameter("videoTexture", filamentTexture!!, sampler)
materialInstance.setParameter("textureTransform", MaterialInstance.FloatElement.MAT4, textureTransform, 0, 1)
}
private fun createMesh() {
val floatSize = 4
val shortSize = 2
// A vertex is a position + a tangent frame:
// 3 floats for XYZ position, 4 floats for normal+tangents (quaternion)
val vertexSize = 3 * floatSize + 4 * floatSize
// Define a vertex and a function to put a vertex in a ByteBuffer
@Suppress("ArrayInDataClass")
data class Vertex(val x: Float, val y: Float, val z: Float, val tangents: FloatArray)
fun ByteBuffer.put(v: Vertex): ByteBuffer {
putFloat(v.x)
putFloat(v.y)
putFloat(v.z)
v.tangents.forEach { putFloat(it) }
return this
}
// 6 faces, 4 vertices per face
val vertexCount = 6 * 4
// Create tangent frames, one per face
val tfPX = FloatArray(4)
val tfNX = FloatArray(4)
val tfPY = FloatArray(4)
val tfNY = FloatArray(4)
val tfPZ = FloatArray(4)
val tfNZ = FloatArray(4)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, tfPX)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, tfNX)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tfPY)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, tfNY)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, tfPZ)
MathUtils.packTangentFrame( 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, tfNZ)
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
// It is important to respect the native byte order
.order(ByteOrder.nativeOrder())
// Face -Z
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNZ))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNZ))
// Face +X
.put(Vertex( 1.5f, -1.5f, -1.0f, tfPX))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPX))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPX))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPX))
// Face +Z
.put(Vertex(-1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPZ))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPZ))
// Face -X
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNX))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfNX))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNX))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNX))
// Face -Y
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNY))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfNY))
// Face +Y
.put(Vertex(-1.5f, 1.5f, -1.0f, tfPY))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPY))
// Make sure the cursor is pointing in the right place in the byte buffer
.flip()
// Declare the layout of our mesh
vertexBuffer = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
// Because we interleave position and color data we must specify offset and stride
// We could use de-interleaved data by declaring two buffers and giving each
// attribute a different buffer index
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.TANGENTS, 0, AttributeType.FLOAT4, 3 * floatSize, vertexSize)
.build(engine)
// Feed the vertex data to the mesh
// We only set 1 buffer because the data is interleaved
vertexBuffer.setBufferAt(engine, 0, vertexData)
// Create the indices
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
.order(ByteOrder.nativeOrder())
repeat(6) {
val i = (it * 4).toShort()
indexData
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())
.putShort(i).putShort((i + 2).toShort()).putShort((i + 3).toShort())
}
indexData.flip()
// 6 faces, 2 triangles per face,
indexBuffer = IndexBuffer.Builder()
.indexCount(vertexCount * 2)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(engine)
indexBuffer.setBuffer(engine, indexData)
}
private fun startAnimation() {
// Animate the triangle
animator.interpolator = LinearInterpolator()
animator.duration = 6000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
val transformMatrix = FloatArray(16)
override fun onAnimationUpdate(animator: ValueAnimator) {
val t = animator.animatedValue as Float
val radians = sin(t) * 3.0f * PI.toFloat()
Matrix.setRotateM(transformMatrix, 0, radians, 0.0f, 1.0f, 0.0f)
val tcm = engine.transformManager
tcm.setTransform(tcm.getInstance(renderable), transformMatrix)
}
})
animator.start()
}
override fun onResume() {
super.onResume()
choreographer.postFrameCallback(frameScheduler)
animator.start()
// cameraHelper.onResume()
}
override fun onPause() {
super.onPause()
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
// cameraHelper.onPause()
}
override fun onDestroy() {
super.onDestroy()
// Stop the animation and any pending frame
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
// Always detach the surface before destroying the engine
uiHelper.detach()
// Cleanup all resources
engine.destroyEntity(light)
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)
engine.destroyVertexBuffer(vertexBuffer)
engine.destroyIndexBuffer(indexBuffer)
engine.destroyMaterialInstance(materialInstance)
engine.destroyMaterial(material)
engine.destroyView(view)
engine.destroyScene(scene)
engine.destroyCameraComponent(camera.entity)
// Engine.destroyEntity() destroys Filament related resources only
// (components), not the entity itself
val entityManager = EntityManager.get()
entityManager.destroy(light)
entityManager.destroy(renderable)
entityManager.destroy(camera.entity)
// Destroying the engine will free up any resource you may have forgotten
// to destroy, but it's recommended to do the cleanup properly
engine.destroy()
}
inner class FrameCallback : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
// Schedule the next frame
choreographer.postFrameCallback(this)
// This check guarantees that we have a swap chain
if (uiHelper.isReadyToRender) {
if (myCount < 1) {
val mybuffer: HardwareBuffer? = CanvasToHardwareBufferUtil.drawToHardwareBuffer(400, 400)
mybuffer?.use { buffer : HardwareBuffer ->
// ... Use the buffer (e.g., create an EGLImage, pass to Vulkan, etc.) ...
Log.d(TAG, "Successfully created HardwareBuffer: $buffer. Now using it...")
// buffer.close() // 'use' handles this automatically
ExternalImage.setOnTexture(engine, filamentTexture!!, buffer, false)
} ?: run {
Log.e(TAG, "Failed to create HardwareBuffer.")
}
}
myCount ++;
// cameraHelper.pushExternalImageToFilament()
// If beginFrame() returns false you should skip the frame
// This means you are sending frames too quickly to the GPU
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
renderer.render(view)
renderer.endFrame()
}
}
}
}
inner class SurfaceCallback : UiHelper.RendererCallback {
override fun onNativeWindowChanged(surface: Surface) {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
displayHelper.attach(renderer, surfaceView.display)
}
override fun onDetachedFromSurface() {
displayHelper.detach()
swapChain?.let {
engine.destroySwapChain(it)
// Required to ensure we don't return before Filament is done executing the
// destroySwapChain command, otherwise Android might destroy the Surface
// too early
engine.flushAndWait()
swapChain = null
}
}
override fun onResized(width: Int, height: Int) {
val aspect = width.toDouble() / height.toDouble()
camera.setProjection(45.0, aspect, 0.1, 20.0, Camera.Fov.VERTICAL)
view.viewport = Viewport(0, 0, width, height)
FilamentHelper.synchronizePendingFrames(engine)
}
}
private fun readUncompressedAsset(@Suppress("SameParameterValue") 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() }
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
// if (!cameraHelper.onRequestPermissionsResult(requestCode, grantResults)) {
// this.onRequestPermissionsResult(requestCode, permissions, grantResults)
// }
}
}
@@ -1,446 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament.externalimg
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 androidx.core.app.ActivityCompat
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.*
import com.google.android.filament.VertexBuffer.*
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.*
import android.util.Log
class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallback {
companion object {
init {
Filament.init()
}
}
private var TAG = "filament.externalimg"
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
// This is the Filament camera, not the phone camera. :)
private lateinit var camera: Camera
// Other Filament objects:
private lateinit var material: Material
private lateinit var materialInstance: MaterialInstance
private lateinit var vertexBuffer: VertexBuffer
private lateinit var indexBuffer: IndexBuffer
// Filament entity representing a renderable object
@Entity private var renderable = 0
@Entity private var light = 0
private var myCount : Int = 0
// A swap chain is Filament's representation of a surface
private var swapChain: SwapChain? = null
// Performs the rendering and schedules new frames
private val frameScheduler = FrameCallback()
private val animator = ValueAnimator.ofFloat(0.0f, 50.0f)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
surfaceView = SurfaceView(this)
setContentView(surfaceView)
choreographer = Choreographer.getInstance()
displayHelper = DisplayHelper(this)
setupSurfaceView()
setupFilament()
setupView()
setupScene()
// cameraHelper = CameraHelper(this, engine, materialInstance)
// cameraHelper.openCamera()
}
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())
}
private fun setupView() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
view.camera = camera
view.scene = scene
}
private fun setupScene() {
loadMaterial()
setupMaterial()
createMesh()
// To create a renderable we first create a generic entity
renderable = EntityManager.get().create()
// We then create a renderable component on that entity
// A renderable is made of several primitives; in this case we declare only 1
// If we wanted each face of the cube to have a different material, we could
// declare 6 primitives (1 per face) and give each of them a different material
// instance, setup with different parameters
RenderableManager.Builder(1)
// Overall bounding box of the renderable
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f))
// Sets the mesh data of the first primitive, 6 faces of 6 indices each
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 6 * 6)
// Sets the material of the first primitive
.material(0, materialInstance)
.build(engine, renderable)
// Add the entity to the scene to render it
scene.addEntity(renderable)
// We now need a light, let's create a directional light
light = EntityManager.get().create()
// Create a color from a temperature (5,500K)
val (r, g, b) = Colors.cct(5_500.0f)
LightManager.Builder(LightManager.Type.DIRECTIONAL)
.color(r, g, b)
// Intensity of the sun in lux on a clear day
.intensity(110_000.0f)
// The direction is normalized on our behalf
.direction(0.0f, -0.5f, -1.0f)
.castShadows(true)
.build(engine, light)
// Add the entity to the scene to light it
scene.addEntity(light)
// Set the exposure on the camera, this exposure follows the sunny f/16 rule
// Since we've defined a light that has the same intensity as the sun, it
// guarantees a proper exposure
camera.setExposure(16.0f, 1.0f / 125.0f, 100.0f)
// Move the camera back to see the object
camera.lookAt(0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
startAnimation()
}
private fun loadMaterial() {
readUncompressedAsset("materials/lit.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
}
}
private fun setupMaterial() {
materialInstance = material.createInstance()
materialInstance.setParameter("baseColor", Colors.RgbType.SRGB, 1.0f, 0.85f, 0.57f)
materialInstance.setParameter("roughness", 0.3f)
}
private fun createMesh() {
val floatSize = 4
val shortSize = 2
// A vertex is a position + a tangent frame:
// 3 floats for XYZ position, 4 floats for normal+tangents (quaternion)
val vertexSize = 3 * floatSize + 4 * floatSize
// Define a vertex and a function to put a vertex in a ByteBuffer
@Suppress("ArrayInDataClass")
data class Vertex(val x: Float, val y: Float, val z: Float, val tangents: FloatArray)
fun ByteBuffer.put(v: Vertex): ByteBuffer {
putFloat(v.x)
putFloat(v.y)
putFloat(v.z)
v.tangents.forEach { putFloat(it) }
return this
}
// 6 faces, 4 vertices per face
val vertexCount = 6 * 4
// Create tangent frames, one per face
val tfPX = FloatArray(4)
val tfNX = FloatArray(4)
val tfPY = FloatArray(4)
val tfNY = FloatArray(4)
val tfPZ = FloatArray(4)
val tfNZ = FloatArray(4)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, tfPX)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, tfNX)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tfPY)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, tfNY)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, tfPZ)
MathUtils.packTangentFrame( 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, tfNZ)
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
// It is important to respect the native byte order
.order(ByteOrder.nativeOrder())
// Face -Z
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNZ))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfNZ))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNZ))
// Face +X
.put(Vertex( 1.5f, -1.5f, -1.0f, tfPX))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPX))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPX))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPX))
// Face +Z
.put(Vertex(-1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPZ))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPZ))
// Face -X
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNX))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfNX))
.put(Vertex(-1.5f, 1.5f, -1.0f, tfNX))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNX))
// Face -Y
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNY))
.put(Vertex(-1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.5f, -1.5f, -1.0f, tfNY))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfNY))
// Face +Y
.put(Vertex(-1.5f, 1.5f, -1.0f, tfPY))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.5f, 1.5f, -1.0f, tfPY))
// Make sure the cursor is pointing in the right place in the byte buffer
.flip()
// Declare the layout of our mesh
vertexBuffer = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
// Because we interleave position and color data we must specify offset and stride
// We could use de-interleaved data by declaring two buffers and giving each
// attribute a different buffer index
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.TANGENTS, 0, AttributeType.FLOAT4, 3 * floatSize, vertexSize)
.build(engine)
// Feed the vertex data to the mesh
// We only set 1 buffer because the data is interleaved
vertexBuffer.setBufferAt(engine, 0, vertexData)
// Create the indices
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
.order(ByteOrder.nativeOrder())
repeat(6) {
val i = (it * 4).toShort()
indexData
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())
.putShort(i).putShort((i + 2).toShort()).putShort((i + 3).toShort())
}
indexData.flip()
// 6 faces, 2 triangles per face,
indexBuffer = IndexBuffer.Builder()
.indexCount(vertexCount * 2)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(engine)
indexBuffer.setBuffer(engine, indexData)
}
private fun startAnimation() {
// Animate the triangle
animator.interpolator = LinearInterpolator()
animator.duration = 6000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
val transformMatrix = FloatArray(16)
override fun onAnimationUpdate(animator: ValueAnimator) {
val t = animator.animatedValue as Float
val radians = sin(t) * 3.0f * PI.toFloat()
Matrix.setRotateM(transformMatrix, 0, radians, 0.0f, 1.0f, 0.0f)
val tcm = engine.transformManager
tcm.setTransform(tcm.getInstance(renderable), transformMatrix)
}
})
animator.start()
}
override fun onResume() {
super.onResume()
choreographer.postFrameCallback(frameScheduler)
animator.start()
// cameraHelper.onResume()
}
override fun onPause() {
super.onPause()
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
// cameraHelper.onPause()
}
override fun onDestroy() {
super.onDestroy()
// Stop the animation and any pending frame
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
// Always detach the surface before destroying the engine
uiHelper.detach()
// Cleanup all resources
engine.destroyEntity(light)
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)
engine.destroyVertexBuffer(vertexBuffer)
engine.destroyIndexBuffer(indexBuffer)
engine.destroyMaterialInstance(materialInstance)
engine.destroyMaterial(material)
engine.destroyView(view)
engine.destroyScene(scene)
engine.destroyCameraComponent(camera.entity)
// Engine.destroyEntity() destroys Filament related resources only
// (components), not the entity itself
val entityManager = EntityManager.get()
entityManager.destroy(light)
entityManager.destroy(renderable)
entityManager.destroy(camera.entity)
// Destroying the engine will free up any resource you may have forgotten
// to destroy, but it's recommended to do the cleanup properly
engine.destroy()
}
inner class FrameCallback : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
// Schedule the next frame
choreographer.postFrameCallback(this)
// This check guarantees that we have a swap chain
if (uiHelper.isReadyToRender) {
if (myCount < 1) {
val mybuffer: HardwareBuffer? = CanvasToHardwareBufferUtil.drawToHardwareBuffer(400, 400)
mybuffer?.use { buffer : HardwareBuffer ->
// ... Use the buffer (e.g., create an EGLImage, pass to Vulkan, etc.) ...
Log.d(TAG, "Successfully created HardwareBuffer: $buffer. Now using it...")
// buffer.close() // 'use' handles this automatically
} ?: run {
Log.e(TAG, "Failed to create HardwareBuffer.")
}
}
myCount ++;
// cameraHelper.pushExternalImageToFilament()
// If beginFrame() returns false you should skip the frame
// This means you are sending frames too quickly to the GPU
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
renderer.render(view)
renderer.endFrame()
}
}
}
}
inner class SurfaceCallback : UiHelper.RendererCallback {
override fun onNativeWindowChanged(surface: Surface) {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
displayHelper.attach(renderer, surfaceView.display)
}
override fun onDetachedFromSurface() {
displayHelper.detach()
swapChain?.let {
engine.destroySwapChain(it)
// Required to ensure we don't return before Filament is done executing the
// destroySwapChain command, otherwise Android might destroy the Surface
// too early
engine.flushAndWait()
swapChain = null
}
}
override fun onResized(width: Int, height: Int) {
val aspect = width.toDouble() / height.toDouble()
camera.setProjection(45.0, aspect, 0.1, 20.0, Camera.Fov.VERTICAL)
view.viewport = Viewport(0, 0, width, height)
FilamentHelper.synchronizePendingFrames(engine)
}
}
private fun readUncompressedAsset(@Suppress("SameParameterValue") 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() }
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
// if (!cameraHelper.onRequestPermissionsResult(requestCode, grantResults)) {
// this.onRequestPermissionsResult(requestCode, permissions, grantResults)
// }
}
}
@@ -1,73 +0,0 @@
// Simple lit material that defines 3 parameters:
// - baseColor
// - roughness
// - metallic
//
// These parameters can be used by the application to change the appearance of the material.
//
// This source material must be compiled to a binary material using the matc tool.
// The command used to compile this material is:
// matc -p mobile -a opengl -o app/src/main/assets/lit.filamat app/src/materials/lit.mat
//
// See build.gradle for an example of how to compile materials automatically
// Please refer to the documentation for more information about matc and the materials system.
material {
name : lit,
// Dynamic lighting is enabled on this material
shadingModel : lit,
// We don't need to declare a "requires" array, lit materials
// always requires the "tangents" vertex attribute (the normal
// is required for lighting, tangent/bitangent for normal mapping
// and anisotropy)
// Custom vertex shader outputs
variables : [
uv
],
// List of parameters exposed by this material
parameters : [
// The color must be passed in linear space, not sRGB
{
type : float3,
name : baseColor
},
{
type : float,
name : roughness
},
{
type : samplerExternal,
name : videoTexture
},
{
type : mat4,
name : textureTransform
}
],
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.uv = 0.5 * (getPosition() + vec4(1));
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.roughness = materialParams.roughness;
material.metallic = 0.0;
// Apply the video stream to the +Z face on the cube.
if (variable_uv.z >= 1.0) {
vec2 uv = (materialParams.textureTransform * vec4(variable_uv.xy, 0, 1)).xy;
material.baseColor.rgb = inverseTonemapSRGB(texture(materialParams_videoTexture, uv).rgb);
} else {
material.baseColor.rgb = materialParams.baseColor;
}
}
}
@@ -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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

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">External Image</string>
</resources>
@@ -1,3 +0,0 @@
<resources>
<string name="app_name">Hello Camera</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>
-1
View File
@@ -18,6 +18,5 @@ include ':samples:sample-stream-test'
include ':samples:sample-texture-view'
include ':samples:sample-textured-object'
include ':samples:sample-transparent-view'
include ':samples:sample-external-image'
rootProject.name = 'filament'
+1 -1
View File
@@ -31,7 +31,7 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
+16 -24
View File
@@ -13,7 +13,7 @@ This document is part of the [Filament project](https://github.com/google/filame
## Authors
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social)
- [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
# Overview
@@ -78,27 +78,26 @@ in table [standardProperties].
Property | Definition
-----------------------:|:---------------------
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**thickness** | Thickness of the solid volume of refractive objects
**sheenColor** | Strength of the sheen layer
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**absorption** | Absorption factor for refractive objects
**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
**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
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**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
**absorption** | Absorption factor for refractive objects
**microThickness** | Thickness of the thin layer of refractive objects
**thickness** | Thickness of the solid volume of refractive objects
[Table [standardProperties]: Properties of the standard model]
The type and range of each property is described in table [standardPropertiesTypes].
@@ -1273,9 +1272,6 @@ Description
when selecting any shading model that is not `unlit`. See the shader sections of this document
for more information on how to access these attributes from the shaders.
!!! Note: Interaction with custom variables
When the `color` attribute is specified, only four custom variables are available instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
parameters : [
@@ -1306,7 +1302,7 @@ Type
: array of `string`
Value
: Up to 5 strings, each must be a valid GLSL identifier.
: Up to 4 strings, each must be a valid GLSL identifier.
Description
: Defines custom interpolants (or variables) that are output by the material's vertex shader.
@@ -1322,10 +1318,6 @@ Description
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
!!! Warning: Interaction with required attributes
If the `color` attribute is specified in the `required` list, then only four variables can be used
instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
name : Skybox,
+4 -6
View File
@@ -177,12 +177,10 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/caching/VulkanDescriptorSetManager.cpp
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
@@ -33,7 +33,7 @@ public:
PlatformCocoaGL();
~PlatformCocoaGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -32,7 +32,7 @@ public:
PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -50,7 +50,7 @@ public:
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept;
ExternalImageHandle createExternalImage(EGLImageKHR eglImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -23,7 +23,6 @@
#include <backend/platforms/PlatformEGL.h>
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <chrono>
@@ -44,21 +43,8 @@ public:
PlatformEGLAndroid() noexcept;
~PlatformEGLAndroid() noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -71,6 +57,11 @@ protected:
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle createExternalImage(AHardwareBuffer const *buffer, bool sRGB) noexcept;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
@@ -104,26 +95,15 @@ protected:
*/
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL {
AHardwareBuffer* aHardwareBuffer = nullptr;
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
bool sRGB = false;
protected:
~ExternalImageEGLAndroid() override;
};
bool setExternalImage(ExternalImageHandleRef externalImage,
ExternalTexture* texture) noexcept override;
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
@@ -368,16 +368,8 @@ public:
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExtensionSet getSwapchainInstanceExtensions();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
@@ -394,7 +386,8 @@ private:
VkComponentMapping swizzle);
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
friend struct VulkanPlatformPrivate;
@@ -17,55 +17,27 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <backend/Platform.h>
#include <android/hardware_buffer.h>
namespace filament::backend {
namespace filament::backend::fvkandroid {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format; // Texture format
TextureUsage usage; // Texture usage flags
protected:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
~ExternalImageVulkanAndroid() override;
};
}// namespace filament::backend
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
} // namespace filament::backend::fvkandroid
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
+5 -15
View File
@@ -19,11 +19,6 @@
#include <utils/Systrace.h>
#include <utils/debug.h>
// We need to keep this up top for the linux (X11) name collisions.
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
@@ -64,11 +59,7 @@
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
#include "backend/platforms/VulkanPlatformAndroid.h"
#else
#include "backend/platforms/VulkanPlatform.h"
#endif
#include "backend/platforms/VulkanPlatform.h"
#endif
#if defined (FILAMENT_SUPPORTS_METAL)
@@ -78,6 +69,9 @@ filament::backend::Platform* createDefaultMetalPlatform();
#endif
#include "noop/PlatformNoop.h"
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
namespace filament::backend {
@@ -114,11 +108,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
}
if (*backend == Backend::VULKAN) {
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
return new VulkanPlatformAndroid();
#else
return new VulkanPlatform();
#endif
return new VulkanPlatform();
#else
return nullptr;
#endif
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformNoop::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
return NoopDriver::create();
}
+6 -6
View File
@@ -148,8 +148,8 @@ using namespace utils;
namespace filament::backend {
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* platform,
void* sharedGLContext,
OpenGLPlatform* const platform,
void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
}
@@ -159,10 +159,10 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
void* /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform);
OpenGLPlatform* ec = platform;
OpenGLPlatform* const ec = platform;
#if 0
// this is useful for development, but too verbose even for debug builds
@@ -230,7 +230,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig };
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
return driver;
}
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
delete pImpl;
}
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
@@ -19,7 +19,6 @@
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include <private/backend/VirtualMachineEnv.h>
#include "opengl/GLUtils.h"
@@ -35,8 +34,6 @@
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -234,133 +231,26 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
return driver;
}
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() {
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() = default;
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto* const p = new (std::nothrow) ExternalImageEGLAndroid;
auto hardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(hardwareBuffer);
p->aHardwareBuffer = hardwareBuffer;
p->sRGB = sRGB;
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(hardwareBuffer, &hardwareBufferDescription);
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return ExternalImageHandle{ p };
}
return Platform::ExternalImageHandle{};
}
PlatformEGLAndroid::ExternalImageDescAndroid PlatformEGLAndroid::getExternalImageDesc(
ExternalImageHandle externalImage) noexcept {
auto const* const eglExternalImage =
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
ExternalImageDescAndroid metadata = {};
if (!eglExternalImage) {
return metadata;
}
metadata.height = eglExternalImage->height;
metadata.width = eglExternalImage->width;
metadata.format = eglExternalImage->format;
metadata.usage = eglExternalImage->usage;
return metadata;
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept {
auto* const p = new(std::nothrow) ExternalImageEGLAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
return ExternalImageHandle{ p };
}
bool PlatformEGLAndroid::setExternalImage(ExternalImageHandleRef externalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
auto const* const eglExternalImage =
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
auto const* const eglExternalImage = static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
if (eglExternalImage->aHardwareBuffer) {
return PlatformEGLAndroid::setImage(eglExternalImage, texture);
// TODO: implement PlatformEGLAndroid::setExternalImage w/ AHardwareBuffer
return true;
}
// not a AHardwareBuffer, fallback to the inherited version
return PlatformEGL::setExternalImage(externalImage, texture);
}
OpenGLPlatform::ExternalTexture* PlatformEGLAndroid::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new (std::nothrow) ExternalTexture{};
glGenTextures(1, &outTexture->id);
return outTexture;
}
void PlatformEGLAndroid::destroyExternalImageTexture(ExternalTexture* texture) noexcept {
glDeleteTextures(1, &texture->id);
delete texture;
}
bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
AHardwareBuffer* hardwareBuffer = eglExternalImage->aHardwareBuffer;
// Get the EGL client buffer from AHardwareBuffer
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
EGLint imageAttrs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE // Ensure the list always ends with EGL_NONE
};
int attrIndex = 2;
if (eglExternalImage->sRGB) {
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE;
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE_SRGB;
}
if (static_cast<bool>(eglExternalImage->usage & TextureUsage::PROTECTED)) {
imageAttrs[attrIndex++] = EGL_PROTECTED_CONTENT_EXT;
imageAttrs[attrIndex++] = EGL_TRUE;
}
// Create an EGLImage from the client buffer
EGLImageKHR eglImage = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
if (eglImage == EGL_NO_IMAGE_KHR) {
// Handle error
slog.e << "Failed to create EGL image" << io::endl;
glDeleteTextures(1, &texture->id);
return false;
}
// Create and bind the OpenGL texture
GLint prevActiveTexture, prevTexture;
glGetIntegerv(GL_ACTIVE_TEXTURE, &prevActiveTexture);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->id);
GLenum error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glBindTexture: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return true;
}
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
if (currentDrawSurface != EGL_NO_SURFACE) {
@@ -128,7 +128,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformGLX::createDriver(void* sharedGLContext,
Driver* PlatformGLX::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept {
loadLibraries();
// Get the display device
@@ -112,7 +112,7 @@ private:
}// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* sharedGLContext,
Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept {
OSMesaAPI* api = new OSMesaAPI();
@@ -75,7 +75,7 @@ struct WGLSwapChain {
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
Driver* PlatformWGL::createDriver(void* sharedGLContext,
Driver* PlatformWGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
int result = 0;
int pixelFormat = 0;
@@ -20,7 +20,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
Driver* PlatformWebGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
}
@@ -93,7 +93,7 @@
#endif
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION)
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
#else
#define FVK_DEBUG_FLAGS 0
#endif
@@ -1,110 +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 "VulkanDescriptorSetLayoutCache.h"
#include "VulkanHandles.h"
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
template<typename Bitmask>
uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType type,
Bitmask const& mask) {
uint32_t count = 0;
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
void VulkanDescriptorSetLayoutCache::terminate() noexcept {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
info);
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
auto const& bitmasks = layout->bitmask;
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
vklayout = itr->second;
} else {
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmasks.sampler);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[bitmasks] = vklayout;
}
layout->setVkLayout(vklayout);
return layout;
}
} // namespace filament::backend
@@ -1,63 +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_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#include "VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
};
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
} // namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
+16 -13
View File
@@ -17,6 +17,7 @@
#include "VulkanDriver.h"
#include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
@@ -34,6 +35,7 @@
#include <backend/platforms/VulkanPlatform.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#ifndef NDEBUG
@@ -42,6 +44,8 @@
using namespace bluevk;
using utils::FixedCapacityVector;
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
@@ -215,8 +219,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -326,8 +329,7 @@ void VulkanDriver::terminate() {
mPipelineCache.terminate();
mFramebufferCache.reset();
mSamplerCache.terminate();
mDescriptorSetLayoutCache.terminate();
mDescriptorSetCache.terminate();
mDescriptorSetManager.terminate();
mPipelineLayoutCache.terminate();
// Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset.
@@ -363,7 +365,7 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
// Command buffers need to be submitted and completed before other resources can be gc'd.
mCommands.gc();
mDescriptorSetCache.clearHistory();
mDescriptorSetManager.clearHistory();
mStagePool.gc();
mFramebufferCache.gc();
mPipelineCache.gc();
@@ -406,7 +408,7 @@ void VulkanDriver::updateDescriptorSetBuffer(
FVK_SYSTRACE_SCOPE();
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
mDescriptorSetCache.updateBuffer(set, binding, buffer, offset, size);
mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size);
}
void VulkanDriver::updateDescriptorSetTexture(
@@ -419,7 +421,7 @@ void VulkanDriver::updateDescriptorSetTexture(
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
VkSampler const vksampler = mSamplerCache.getSampler(params);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
}
void VulkanDriver::flush(int) {
@@ -776,7 +778,8 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
auto layout = resource_ptr<VulkanDescriptorSetLayout>::make(&mResourceManager, dslh, info);
mDescriptorSetManager.initVkLayout(layout);
layout.inc();
}
@@ -785,7 +788,7 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout);
auto set = mDescriptorSetManager.createSet(dsh, layout);
set.inc();
}
@@ -1490,7 +1493,7 @@ void VulkanDriver::nextSubpass(int) {
if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment& subpassInput = renderTarget->getColor0();
mDescriptorSetCache.updateInputAttachment({}, subpassInput);
mDescriptorSetManager.updateInputAttachment({}, subpassInput);
}
}
@@ -1817,9 +1820,9 @@ void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
} else {
mDescriptorSetCache.unbind(setIndex);
mDescriptorSetManager.unbind(setIndex);
}
}
@@ -1827,7 +1830,7 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
+3 -5
View File
@@ -27,9 +27,8 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanQueryManager.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/caching/VulkanDescriptorSetManager.h"
#include "vulkan/caching/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h"
@@ -138,8 +137,7 @@ private:
VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanDescriptorSetManager mDescriptorSetManager;
VulkanQueryManager mQueryManager;
// This is necessary for us to write to push constants after binding a pipeline.
@@ -15,12 +15,15 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanMemory.h"
#include "caching/VulkanDescriptorSetManager.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include "VulkanTexture.h"
#include "vulkan/utils/Conversion.h"
#if defined(__clang__)
@@ -14,25 +14,35 @@
* limitations under the License.
*/
#include "VulkanDescriptorSetCache.h"
#include "VulkanDescriptorSetManager.h"
#include "VulkanCommands.h"
#include "VulkanHandles.h"
#include "VulkanConstants.h"
#include "vulkan/VulkanCommands.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanConstants.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <math.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const {
return k1 == k2;
}
};
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
@@ -189,12 +199,72 @@ struct Equal {
}
};
template<typename Bitmask>
uint32_t createBindings(VkDescriptorSetLayoutBinding* toBind, uint32_t count, VkDescriptorType type,
Bitmask const& mask) {
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
inline VkDescriptorSetLayout createLayout(VkDevice device, BitmaskGroup const& bitmaskGroup) {
// Note that the following *needs* to be static so that VkDescriptorSetLayoutCreateInfo will not
// refer to stack memory.
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmaskGroup.dynamicUbo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmaskGroup.ubo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmaskGroup.sampler);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmaskGroup.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout layout;
vkCreateDescriptorSetLayout(device, &dlinfo, VKALLOC, &layout);
return layout;
}
} // anonymous namespace
// This is an ever-expanding pool of sets where it
// 1. Keeps a list of smaller pools of different layout-dimensions.
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
class VulkanDescriptorSetCache::DescriptorInfinitePool {
class VulkanDescriptorSetManager::DescriptorInfinitePool {
private:
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
@@ -249,34 +319,61 @@ private:
std::vector<std::unique_ptr<DescriptorPool>> mPools;
};
class VulkanDescriptorSetManager::DescriptorSetLayoutManager {
public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VulkanDescriptorSetCache::VulkanDescriptorSetCache(VkDevice device,
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
return vklayout;
}
~DescriptorSetLayoutManager() {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
private:
VkDevice mDevice;
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetCache::terminate() noexcept{
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetCache::bind(uint8_t setIndex,
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
@@ -315,7 +412,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
};
}
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
@@ -341,7 +438,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
set->acquire(bufferObject);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
@@ -373,13 +470,13 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateInputAttachment(
void VulkanDescriptorSetManager::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill this in.
// TOOD: fill-in this region
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
@@ -395,7 +492,12 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
});
}
void VulkanDescriptorSetCache::clearHistory() {
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
}
void VulkanDescriptorSetManager::clearHistory() {
mStashedSets = {};
}
@@ -14,10 +14,10 @@
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#include "VulkanHandles.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
@@ -34,16 +34,20 @@
namespace filament::backend {
// Abstraction over the descriptor set pool.
class VulkanDescriptorSetCache {
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
// the methods introduced here will be obsolete, and certain logic will be generalized.
// Abstraction over the pool and the layout cache.
class VulkanDescriptorSetManager {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetManager();
void terminate() noexcept;
@@ -68,9 +72,12 @@ public:
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void clearHistory();
private:
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
using DescriptorSetArray =
@@ -78,6 +85,7 @@ private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
@@ -91,4 +99,4 @@ private:
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
@@ -17,8 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h"
#include <vulkan/VulkanHandles.h>
#include <bluevk/BlueVK.h>
#include <utils/Hash.h>
@@ -1000,15 +1000,6 @@ VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chrom
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept {
return createVkSurfaceKHRImpl(nativeWindow, instance, flags);
}
#undef SWAPCHAIN_RET_FUNC
}// namespace filament::backend
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <backend/platforms/VulkanPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
@@ -156,7 +157,7 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
imageInfo.pNext = &externalCreateInfo;
imageInfo.format = metadata.format;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
@@ -199,12 +200,14 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
return data;
}
}// namespace
} // namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
namespace fvkandroid {
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
@@ -223,23 +226,12 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
return Platform::ExternalImageHandle{};
}
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
} // namespace fvkandroid
return {
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ExternalImageMetadata metadata;
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
@@ -253,7 +245,7 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
@@ -264,11 +256,11 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
.pNext = &formatInfo,
};
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(getDevice(), buffer, &properties);
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
@@ -279,14 +271,14 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
return metadata;
}
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data;
@@ -405,14 +397,14 @@ VkSampler VulkanPlatform::createExternalSamplerImpl(
return sampler;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
};
}
VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept {
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
VkExtent2D extent;
@@ -424,26 +416,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC, (VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateAndroidSurfaceKHR with error=" << static_cast<int32_t>(result);
return { surface, extent };
return {surface, extent};
}
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
}// namespace filament::backend
@@ -52,7 +52,7 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
ExtensionSet const ret = {
#if defined(__APPLE__)
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
@@ -87,7 +87,7 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
#if defined(__APPLE__)
@@ -108,7 +108,7 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
@@ -126,7 +126,7 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
return ret;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
+2 -2
View File
@@ -651,11 +651,11 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
return MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
}
size_t WebGPUDriver::getMaxUniformBufferSize() {
size_t WebGPUDriver::getMaxUniformBufferSize(SamplerType) {
return 16384u;
}
size_t WebGPUDriver::getMaxTextureSize(SamplerType target) {
size_t WebGPUDriver::getMaxTextureSize() {
return 2048u;
}
@@ -143,7 +143,7 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
return device;
}
Driver* WebGPUPlatform::createDriver(void* sharedContext,
Driver* WebGPUPlatform::createDriver(void* const sharedContext,
const Platform::DriverConfig& /*driverConfig*/) noexcept {
if (sharedContext) {
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
@@ -15,8 +15,6 @@
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
@@ -120,9 +118,7 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags)
if (useXcb) {
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
surfaceSourceXcb.connection = g_x11.connection;
// TODO: this looks really wrong, please fix!!
surfaceSourceXcb.window = *((uint32_t*) nativeWindow);
surfaceSourceXcb.window = reinterpret_cast<uint32_t>(nativeWindow);
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceXcb,
.label = "linux_xcb_surface"
+3 -9
View File
@@ -64,8 +64,9 @@ class ColorSpace;
* Performance
* ===========
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
*
* Ordering
* ========
@@ -154,9 +155,6 @@ public:
* 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is medium.
@@ -171,8 +169,6 @@ public:
* When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER
*
* @param format The desired format of the 3D LUT.
@@ -185,8 +181,6 @@ public:
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is 32
*
* @param dim The desired dimension of the LUT. Between 16 and 64.
-31
View File
@@ -68,21 +68,6 @@ struct UTILS_PUBLIC ToneMapper {
* function applied ("linear")
*/
virtual math::float3 operator()(math::float3 c) const noexcept = 0;
/**
* If true, then this function holds that f(x) = vec3(f(x.r), f(x.g), f(x.b))
*
* This may be used to indicate that the color grading's LUT only requires a 1D texture instead
* of a 3D texture, potentially saving a significant amount of memory and generation time.
*/
virtual bool isOneDimensional() const noexcept { return false; }
/**
* True if this tonemapper only works in low-dynamic-range.
*
* This may be used to indicate that the color grading's LUT doesn't need to be log encoded.
*/
virtual bool isLDR() const noexcept { return false; }
};
/**
@@ -94,8 +79,6 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
~LinearToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept override;
bool isOneDimensional() const noexcept override { return true; }
bool isLDR() const noexcept override { return true; }
};
/**
@@ -108,8 +91,6 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
~ACESToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept override;
bool isOneDimensional() const noexcept override { return false; }
bool isLDR() const noexcept override { return false; }
};
/**
@@ -123,8 +104,6 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
~ACESLegacyToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept override;
bool isOneDimensional() const noexcept override { return false; }
bool isLDR() const noexcept override { return false; }
};
/**
@@ -138,8 +117,6 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
~FilmicToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept override;
bool isOneDimensional() const noexcept override { return true; }
bool isLDR() const noexcept override { return false; }
};
/**
@@ -152,8 +129,6 @@ struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper {
~PBRNeutralToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept override;
bool isOneDimensional() const noexcept override { return false; }
bool isLDR() const noexcept override { return false; }
};
/**
@@ -175,8 +150,6 @@ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper {
~AgxToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept override;
bool isOneDimensional() const noexcept override { return false; }
bool isLDR() const noexcept override { return false; }
AgxLook look;
};
@@ -221,8 +194,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept;
math::float3 operator()(math::float3 x) const noexcept override;
bool isOneDimensional() const noexcept override { return true; }
bool isLDR() const noexcept override { return false; }
/** Returns the contrast of the curve as a strictly positive value. */
float getContrast() const noexcept;
@@ -285,8 +256,6 @@ struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper {
~DisplayRangeToneMapper() noexcept override;
math::float3 operator()(math::float3 c) const noexcept override;
bool isOneDimensional() const noexcept override { return false; }
bool isLDR() const noexcept override { return false; }
};
} // namespace filament
+56 -72
View File
@@ -2306,11 +2306,36 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
const FColorGrading* colorGrading, ColorGradingConfig const& colorGradingConfig,
VignetteOptions const& vignetteOptions, uint32_t const width, uint32_t const height) noexcept {
auto& material = getPostProcessMaterial("colorGradingAsSubpass");
FMaterialInstance* const mi =
configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
vignetteOptions, width, height);
float4 const vignetteParameters = getVignetteParameters(vignetteOptions, width, height);
auto const& material = getPostProcessMaterial("colorGradingAsSubpass");
FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(mEngine, material);
mi->setParameter("lut", colorGrading->getHwHandle(), {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR,
.wrapS = SamplerWrapMode::CLAMP_TO_EDGE,
.wrapT = SamplerWrapMode::CLAMP_TO_EDGE,
.wrapR = SamplerWrapMode::CLAMP_TO_EDGE,
.anisotropyLog2 = 0
});
const float lutDimension = float(colorGrading->getDimension());
mi->setParameter("lutSize", float2{
0.5f / lutDimension, (lutDimension - 1.0f) / lutDimension,
});
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("dithering", colorGradingConfig.dithering);
mi->setParameter("outputLuminance", colorGradingConfig.outputLuminance);
mi->setParameter("temporalNoise", temporalNoise);
mi->commit(driver);
// load both variants
material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
material.getMaterial(mEngine, PostProcessVariant::TRANSLUCENT);
}
void PostProcessManager::colorGradingSubpass(DriverApi& driver,
@@ -2462,14 +2487,22 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
auto const& out = resources.getRenderPassInfo();
auto const& input = resources.getDescriptor(data.input);
auto const& output = resources.getDescriptor(data.output);
auto const& material = getPostProcessMaterial("colorGrading");
PostProcessVariant const variant = colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
auto& material = getPostProcessMaterial("colorGrading");
FMaterialInstance* const mi =
configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
vignetteOptions, output.width, output.height);
PostProcessMaterial::getMaterialInstance(mEngine, material, variant);
mi->setParameter("lut", colorGrading->getHwHandle(), {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
const float lutDimension = float(colorGrading->getDimension());
mi->setParameter("lutSize", float2{
0.5f / lutDimension, (lutDimension - 1.0f) / lutDimension,
});
mi->setParameter("colorBuffer", colorTexture, { /* shader uses texelFetch */ });
mi->setParameter("bloomBuffer", bloomTexture, {
.filterMag = SamplerMagFilter::LINEAR,
@@ -2501,7 +2534,19 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
bloomParameters.y = 1.0f - bloomParameters.x;
}
auto const& input = resources.getDescriptor(data.input);
auto const& output = resources.getDescriptor(data.output);
float4 const vignetteParameters = getVignetteParameters(
vignetteOptions, output.width, output.height);
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
mi->setParameter("dithering", colorGradingConfig.dithering);
mi->setParameter("bloom", bloomParameters);
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("outputLuminance", colorGradingConfig.outputLuminance);
mi->setParameter("temporalNoise", temporalNoise);
mi->setParameter("viewport", float4{
float(vp.left) / input.width,
float(vp.bottom) / input.height,
@@ -2509,9 +2554,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
float(vp.height) / input.height
});
commitAndRenderFullScreenQuad(driver, out, mi,
colorGradingConfig.translucent
? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
commitAndRenderFullScreenQuad(driver, out, mi, variant);
}
);
@@ -2649,70 +2692,11 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial(
setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma);
if (dirty) {
ma->invalidate();
// TODO: call Material::compile(), we can't do that now because it works only
// TODO: call Material::compile(), we can't si that now because it works only
// with surface materials
}
}
FMaterialInstance* PostProcessManager::configureColorGradingMaterial(
PostProcessMaterial& material, FColorGrading const* colorGrading,
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
uint32_t const width, uint32_t const height) noexcept {
FMaterial* const ma = material.getMaterial(mEngine);
bool dirty = false;
auto setConstantParameter =
[&dirty](FMaterial* const material, std::string_view const name, auto value) noexcept {
auto id = material->getSpecializationConstantId(name);
if (id.has_value()) {
if (material->setConstant(id.value(), value)) {
dirty = true;
}
}
};
setConstantParameter(ma, "isOneDimensional", colorGrading->isOneDimensional());
setConstantParameter(ma, "isLDR", colorGrading->isLDR());
if (dirty) {
ma->invalidate();
// TODO: call Material::compile(), we can't do that now because it works only
// with surface materials
}
PostProcessVariant const variant = colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
FMaterialInstance* const mi =
PostProcessMaterial::getMaterialInstance(mEngine, material, variant);
const SamplerParams params = {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR,
.wrapS = SamplerWrapMode::CLAMP_TO_EDGE,
.wrapT = SamplerWrapMode::CLAMP_TO_EDGE,
.wrapR = SamplerWrapMode::CLAMP_TO_EDGE,
.anisotropyLog2 = 0
};
mi->setParameter("lut", colorGrading->getHwHandle(), params);
const float lutDimension = float(colorGrading->getDimension());
mi->setParameter("lutSize", float2{
0.5f / lutDimension, (lutDimension - 1.0f) / lutDimension,
});
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
float4 const vignetteParameters = getVignetteParameters(vignetteOptions, width, height);
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("dithering", colorGradingConfig.dithering);
mi->setParameter("outputLuminance", colorGradingConfig.outputLuminance);
mi->setParameter("temporalNoise", temporalNoise);
return mi;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> const depth,
-7
View File
@@ -401,13 +401,6 @@ public:
FMaterialInstance const* mi,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept;
// Sets the necessary spec constants and uniforms common to both colorGrading.mat and
// colorGradingAsSubpass.mat.
FMaterialInstance* configureColorGradingMaterial(
PostProcessMaterial& material, FColorGrading const* colorGrading,
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
uint32_t const width, uint32_t const height) noexcept;
private:
backend::RenderPrimitiveHandle mFullScreenQuadRph;
backend::VertexBufferInfoHandle mFullScreenQuadVbih;
+161 -194
View File
@@ -35,6 +35,7 @@
#include <cmath>
#include <cstdlib>
#include <mutex>
#include <tuple>
namespace filament {
@@ -580,11 +581,8 @@ static float3 luminanceScaling(float3 x,
// Quality
//------------------------------------------------------------------------------
static std::tuple<TextureFormat, PixelDataFormat, PixelDataType> selectLutTextureParams(
ColorGrading::LutFormat const lutFormat, const bool isOneDimensional) noexcept {
if (isOneDimensional) {
return { TextureFormat::R16F, PixelDataFormat::R, PixelDataType::HALF };
}
static std::tuple<TextureFormat, PixelDataFormat, PixelDataType>
selectLutTextureParams(ColorGrading::LutFormat const lutFormat) noexcept {
// We use RGBA16F for high quality modes instead of RGB16F because RGB16F
// is not supported everywhere
switch (lutFormat) {
@@ -660,43 +658,27 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
DriverApi& driver = engine.getDriverApi();
// XXX: The following two conditions also only hold true as long as the input and output color
// spaces are the same, but we currently don't check that. We must revise these conditions if we
// ever handle this case.
mIsOneDimensional = !builder->hasAdjustments && !builder->luminanceScaling
&& builder->toneMapper->isOneDimensional();
mIsLDR = mIsOneDimensional && builder->toneMapper->isLDR();
const Config config = {
mIsOneDimensional ? 512u : builder->dimension,
adaptationTransform(builder->whiteBalance),
selectColorGradingTransformIn(builder->toneMapping),
selectColorGradingTransformOut(builder->toneMapping),
selectColorGradingLuminance(builder->toneMapping),
selectOETF(builder->outputColorSpace),
};
mDimension = config.lutDimension;
uint32_t width;
uint32_t height;
uint32_t depth;
if (mIsOneDimensional) {
width = config.lutDimension;
height = 1;
depth = 1;
} else {
width = config.lutDimension;
height = config.lutDimension;
depth = config.lutDimension;
Config c;
// This lock protects the data inside Config, which is written to by the Filament thread,
// and read from multiple Job threads.
Mutex configLock;
{
std::lock_guard<Mutex> const lock(configLock);
c.lutDimension = builder->dimension;
c.adaptationTransform = adaptationTransform(builder->whiteBalance);
c.colorGradingIn = selectColorGradingTransformIn(builder->toneMapping);
c.colorGradingOut = selectColorGradingTransformOut(builder->toneMapping);
c.colorGradingLuminance = selectColorGradingLuminance(builder->toneMapping);
c.oetf = selectOETF(builder->outputColorSpace);
}
size_t lutElementCount = width * height * depth;
size_t elementSize = mIsOneDimensional ? sizeof(half) : sizeof(half4);
mDimension = c.lutDimension;
size_t lutElementCount = c.lutDimension * c.lutDimension * c.lutDimension;
size_t elementSize = sizeof(half4);
void* data = malloc(lutElementCount * elementSize);
auto [textureFormat, format, type] =
selectLutTextureParams(builder->format, mIsOneDimensional);
auto [textureFormat, format, type] = selectLutTextureParams(builder->format);
assert_invariant(FTexture::isTextureFormatSupported(engine, textureFormat));
assert_invariant(FTexture::validatePixelFormatAndType(textureFormat, format, type));
@@ -706,185 +688,170 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
converted = malloc(lutElementCount * sizeof(uint32_t));
}
auto hdrColorAt = [builder, config](size_t r, size_t g, size_t b) {
float3 v = float3{r, g, b} * (1.0f / float(config.lutDimension - 1u));
// LogC encoding
v = LogC_to_linear(v);
// Kill negative values near 0.0f due to imprecision in the log conversion
v = max(v, 0.0f);
if (builder->hasAdjustments) {
// Exposure
v = adjustExposure(v, builder->exposure);
// Purkinje shift ("low-light" vision)
v = scotopicAdaptation(v, builder->nightAdaptation);
}
// Move to color grading color space
v = config.colorGradingIn * v;
if (builder->hasAdjustments) {
// White balance
v = chromaticAdaptation(v, config.adaptationTransform);
// Kill negative values before the next transforms
v = max(v, 0.0f);
// Channel mixer
v = channelMixer(v, builder->outRed, builder->outGreen, builder->outBlue);
// Shadows/mid-tones/highlights
v = tonalRanges(v, config.colorGradingLuminance,
builder->shadows, builder->midtones, builder->highlights,
builder->tonalRanges);
// The adjustments below behave better in log space
v = linear_to_LogC(v);
// ASC CDL
v = colorDecisionList(v, builder->slope, builder->offset, builder->power);
// Contrast in log space
v = contrast(v, builder->contrast);
// Back to linear space
v = LogC_to_linear(v);
// Vibrance in linear space
v = vibrance(v, config.colorGradingLuminance, builder->vibrance);
// Saturation in linear space
v = saturation(v, config.colorGradingLuminance, builder->saturation);
// Kill negative values before curves
v = max(v, 0.0f);
// RGB curves
v = curves(v,
builder->shadowGamma, builder->midPoint, builder->highlightScale);
}
// Tone mapping
if (builder->luminanceScaling) {
v = luminanceScaling(v, *builder->toneMapper, config.colorGradingLuminance);
} else {
v = (*builder->toneMapper)(v);
}
// Go back to display color space
v = config.colorGradingOut * v;
// Apply gamut mapping
if (builder->gamutMapping) {
// TODO: This should depend on the output color space
v = gamutMapping_sRGB(v);
}
// TODO: We should convert to the output color space if we use a working
// color space that's not sRGB
// TODO: Allow the user to customize the output color space
// We need to clamp for the output transfer function
v = saturate(v);
// Apply OETF
v = config.oetf(v);
return v;
};
//auto now = std::chrono::steady_clock::now();
if (mIsOneDimensional) {
half* UTILS_RESTRICT p = (half*) data;
if (mIsLDR) {
for (size_t rgb = 0; rgb < config.lutDimension; rgb++) {
float3 v = float3(rgb) * (1.0f / float(config.lutDimension - 1u));
v = (*builder->toneMapper)(float3(v));
// We need to clamp for the output transfer function
v = saturate(v);
// Apply OETF
v = config.oetf(v);
*p++ = half(v.r);
// Multithreadedly generate the tone mapping 3D look-up table using 32 jobs
// Slices are 8 KiB (128 cache lines) apart.
// This takes about 3-6ms on Android in Release
JobSystem& js = engine.getJobSystem();
auto *slices = js.createJob();
for (size_t b = 0; b < c.lutDimension; b++) {
auto *job = js.createJob(slices,
[data, converted, b, &c, &configLock, builder](JobSystem&, JobSystem::Job*) {
Config config;
{
std::lock_guard<Mutex> lock(configLock);
config = c;
}
} else {
for (size_t rgb = 0; rgb < config.lutDimension; rgb++) {
*p++ = half(hdrColorAt(rgb, rgb, rgb).r);
}
}
} else {
// Multithreadedly generate the tone mapping 3D look-up table using 32 jobs
// Slices are 8 KiB (128 cache lines) apart.
// This takes about 3-6ms on Android in Release
JobSystem& js = engine.getJobSystem();
auto *slices = js.createJob();
for (size_t b = 0; b < config.lutDimension; b++) {
auto* job = js.createJob(slices,
[data, converted, b, &config, builder, &hdrColorAt](
JobSystem&, JobSystem::Job*) {
half4* UTILS_RESTRICT p =
(half4*) data + b * config.lutDimension * config.lutDimension;
for (size_t g = 0; g < config.lutDimension; g++) {
for (size_t r = 0; r < config.lutDimension; r++) {
*p++ = half4{hdrColorAt(r, g, b), 0.0f};
half4* UTILS_RESTRICT p = (half4*) data + b * config.lutDimension * config.lutDimension;
for (size_t g = 0; g < config.lutDimension; g++) {
for (size_t r = 0; r < config.lutDimension; r++) {
float3 v = float3{r, g, b} * (1.0f / float(config.lutDimension - 1u));
// LogC encoding
v = LogC_to_linear(v);
// Kill negative values near 0.0f due to imprecision in the log conversion
v = max(v, 0.0f);
if (builder->hasAdjustments) {
// Exposure
v = adjustExposure(v, builder->exposure);
// Purkinje shift ("low-light" vision)
v = scotopicAdaptation(v, builder->nightAdaptation);
}
}
if (converted) {
uint32_t* const UTILS_RESTRICT dst = (uint32_t*) converted +
b * config.lutDimension * config.lutDimension;
half4* UTILS_RESTRICT src = (half4*) data +
b * config.lutDimension * config.lutDimension;
// we use a vectorize width of 8 because, on ARMv8 it allows the compiler to
// write eight 32-bits results in one go.
const size_t count = (config.lutDimension * config.lutDimension) & ~0x7u; // tell the compiler that we're a multiple of 8
// Move to color grading color space
v = c.colorGradingIn * v;
if (builder->hasAdjustments) {
// White balance
v = chromaticAdaptation(v, config.adaptationTransform);
// Kill negative values before the next transforms
v = max(v, 0.0f);
// Channel mixer
v = channelMixer(v, builder->outRed, builder->outGreen, builder->outBlue);
// Shadows/mid-tones/highlights
v = tonalRanges(v, c.colorGradingLuminance,
builder->shadows, builder->midtones, builder->highlights,
builder->tonalRanges);
// The adjustments below behave better in log space
v = linear_to_LogC(v);
// ASC CDL
v = colorDecisionList(v, builder->slope, builder->offset, builder->power);
// Contrast in log space
v = contrast(v, builder->contrast);
// Back to linear space
v = LogC_to_linear(v);
// Vibrance in linear space
v = vibrance(v, c.colorGradingLuminance, builder->vibrance);
// Saturation in linear space
v = saturation(v, c.colorGradingLuminance, builder->saturation);
// Kill negative values before curves
v = max(v, 0.0f);
// RGB curves
v = curves(v,
builder->shadowGamma, builder->midPoint, builder->highlightScale);
}
// Tone mapping
if (builder->luminanceScaling) {
v = luminanceScaling(v, *builder->toneMapper, c.colorGradingLuminance);
} else {
v = (*builder->toneMapper)(v);
}
// Go back to display color space
v = c.colorGradingOut * v;
// Apply gamut mapping
if (builder->gamutMapping) {
// TODO: This should depend on the output color space
v = gamutMapping_sRGB(v);
}
// TODO: We should convert to the output color space if we use a working
// color space that's not sRGB
// TODO: Allow the user to customize the output color space
// We need to clamp for the output transfer function
v = saturate(v);
// Apply OETF
v = c.oetf(v);
*p++ = half4{v, 0.0f};
}
}
if (converted) {
uint32_t* const UTILS_RESTRICT dst = (uint32_t*) converted +
b * config.lutDimension * config.lutDimension;
half4* UTILS_RESTRICT src = (half4*) data +
b * config.lutDimension * config.lutDimension;
// we use a vectorize width of 8 because, on ARMv8 it allows the compiler to write eight
// 32-bits results in one go.
const size_t count = (config.lutDimension * config.lutDimension) & ~0x7u; // tell the compiler that we're a multiple of 8
#if defined(__clang__)
#pragma clang loop vectorize_width(8)
#pragma clang loop vectorize_width(8)
#endif
for (size_t i = 0; i < count; ++i) {
float4 v{src[i]};
uint32_t pr = uint32_t(std::floor(v.x * 1023.0f + 0.5f));
uint32_t pg = uint32_t(std::floor(v.y * 1023.0f + 0.5f));
uint32_t pb = uint32_t(std::floor(v.z * 1023.0f + 0.5f));
dst[i] = (pb << 20u) | (pg << 10u) | pr;
}
for (size_t i = 0; i < count; ++i) {
float4 v{src[i]};
uint32_t pr = uint32_t(std::floor(v.x * 1023.0f + 0.5f));
uint32_t pg = uint32_t(std::floor(v.y * 1023.0f + 0.5f));
uint32_t pb = uint32_t(std::floor(v.z * 1023.0f + 0.5f));
dst[i] = (pb << 20u) | (pg << 10u) | pr;
}
});
js.run(job);
}
}
// TODO: Should we do a runAndRetain() and defer the wait() + texture creation until
// getHwHandle() is invoked?
js.runAndWait(slices);
});
js.run(job);
}
// TODO: Should we do a runAndRetain() and defer the wait() + texture creation until
// getHwHandle() is invoked?
js.runAndWait(slices);
//std::chrono::duration<float, std::milli> duration = std::chrono::steady_clock::now() - now;
//slog.d << "LUT generation time: " << duration.count() << " ms" << io::endl;
mLutHandle = driver.createTexture(
SamplerType::SAMPLER_3D,
1,
textureFormat,
1,
c.lutDimension,
c.lutDimension,
c.lutDimension,
TextureUsage::DEFAULT
);
if (converted) {
free(data);
data = converted;
elementSize = sizeof(uint32_t);
}
// Create texture.
mLutHandle = driver.createTexture(SamplerType::SAMPLER_3D, 1, textureFormat, 1,
width, height, depth, TextureUsage::DEFAULT);
driver.update3DImage(mLutHandle, 0,
0, 0, 0,
width, height, depth,
c.lutDimension, c.lutDimension, c.lutDimension,
PixelBufferDescriptor{
data, lutElementCount * elementSize, format, type,
[](void* buffer, size_t, void*) { free(buffer); }
});
data, lutElementCount * elementSize,format, type,
[](void* buffer, size_t, void*) { free(buffer); }
}
);
}
FColorGrading::~FColorGrading() noexcept = default;
+1 -4
View File
@@ -42,15 +42,12 @@ public:
void terminate(FEngine& engine);
backend::TextureHandle getHwHandle() const noexcept { return mLutHandle; }
uint32_t getDimension() const noexcept { return mDimension; }
bool isOneDimensional() const noexcept { return mIsOneDimensional; }
bool isLDR() const noexcept { return mIsLDR; }
private:
backend::TextureHandle mLutHandle;
uint32_t mDimension;
bool mIsOneDimensional;
bool mIsLDR;
};
FILAMENT_DOWNCAST(ColorGrading)
+3 -4
View File
@@ -153,10 +153,9 @@ Texture::Builder& Texture::Builder::name(const char* name, size_t const len) noe
}
Texture* Texture::Builder::build(Engine& engine) {
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
}
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
const bool isProtectedTexturesSupported =
downcast(engine).getDriverApi().isProtectedTexturesSupported();
const bool useProtectedMemory = bool(mImpl->mUsage & TextureUsage::PROTECTED);
@@ -1,29 +1,13 @@
vec3 LogC_to_linear(const vec3 x) {
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
return c * log2(a * x + b) + d;
}
vec3 logc = c * log2(a * x + b) + d;
vec3 colorGrade3D(mediump sampler3D lut, const vec3 v) {
return textureLod(lut, v, 0.0).rgb;
}
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
vec3 colorGrade1D(mediump sampler3D lut, const vec3 v) {
return vec3(
textureLod(lut, vec3(v.r, 0.5, 0.5), 0.0).r,
textureLod(lut, vec3(v.g, 0.5, 0.5), 0.0).r,
textureLod(lut, vec3(v.b, 0.5, 0.5), 0.0).r);
}
vec3 colorGrade(mediump sampler3D lut, vec3 v) {
if (!materialConstants_isLDR) {
v = LogC_to_linear(v);
}
// Remap to sample pixel centers.
v = materialParams.lutSize.x + v * materialParams.lutSize.y;
return materialConstants_isOneDimensional
? colorGrade1D(lut, v) : colorGrade3D(lut, v);
return textureLod(lut, logc, 0.0).rgb;
}
@@ -66,16 +66,6 @@ material {
precision : high
}
],
constants : [
{
type : bool,
name : isOneDimensional
},
{
type : bool,
name : isLDR
}
],
variables : [
vertex
],
@@ -164,7 +154,7 @@ void postProcess(inout PostProcessInputs postProcess) {
}
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
@@ -32,16 +32,6 @@ material {
name : vignetteColor
}
],
constants : [
{
type : bool,
name : isOneDimensional
},
{
type : bool,
name : isLDR
}
],
subpasses : [
{
type : subpassInput,
@@ -113,7 +103,7 @@ void postProcess(inout PostProcessInputs postProcess) {
}
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.58.1"
spec.version = "1.58.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.58.1/filament-v1.58.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.0/filament-v1.58.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+1 -1
View File
@@ -12,7 +12,7 @@
## Capabilities
fgviewer is a library and web application for real-time visualization of the frame graph in Filament.
fgviewer is a library and web application for real-time visualization of the frame graph in Filament.
It displays active passes and resource usage, providing insights into the rendering pipeline.
## Setup for Desktop
@@ -58,7 +58,7 @@ public:
utils::CString name;
utils::CString value;
};
Resource(ResourceId id, utils::CString name,
std::vector<Property> properties);
-8
View File
@@ -17,22 +17,14 @@
#include "ApiHandler.h"
#include <__chrono/duration.h>
#include <fgviewer/DebugServer.h>
#include <fgviewer/JsonWriter.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <CivetServer.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <string>
namespace filament::fgviewer {
using namespace std::chrono_literals;
+3 -12
View File
@@ -21,45 +21,36 @@
#include <CivetServer.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Hash.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/ostream.h>
#include <mutex>
#include <string>
#include <string_view>
namespace filament::fgviewer {
// If set to 0, this serves HTML from a resgen resource. Use 1 only during local development, which
// serves files directly from the source code tree.
#define SERVE_FROM_SOURCE_TREE 0
#if SERVE_FROM_SOURCE_TREE
namespace {
std::string const BASE_URL = "libs/fgviewer/web";
} // anonymous
#else
#include "fgviewer_resources.h"
#include <unordered_map>
namespace {
struct Asset {
std::string_view mime;
std::string_view data;
};
std::unordered_map<std::string_view, Asset> ASSET_MAP;
} // anonymous
#endif // SERVE_FROM_SOURCE_TREE
namespace filament::fgviewer {
using namespace utils;
std::string_view const DebugServer::kSuccessHeader =
-5
View File
@@ -16,11 +16,6 @@
#include <fgviewer/FrameGraphInfo.h>
#include <utils/CString.h>
#include <unordered_map>
#include <vector>
namespace filament::fgviewer {
FrameGraphInfo::FrameGraphInfo(utils::CString viewName):
+2 -6
View File
@@ -14,15 +14,11 @@
* limitations under the License.
*/
#include <fgviewer/FrameGraphInfo.h>
#include <fgviewer/JsonWriter.h>
#include <fgviewer/FrameGraphInfo.h>
#include <utils/CString.h>
#include <cstddef>
#include <ostream>
#include <iomanip>
#include <sstream>
#include <vector>
namespace filament::fgviewer {
+8 -8
View File
@@ -223,8 +223,8 @@ class FrameGraphSidePanel extends LitElement {
return html`
<menu-section title="${title}">
${this.framegraphs.map(({ fgid, name }) => html`
<div class="framegraph"
@click="${() => this._handleFrameGraphClick(fgid)}"
<div class="framegraph"
@click="${() => this._handleFrameGraphClick(fgid)}"
data-id="${fgid}">
${fgid === this.selectedFrameGraph ? '● ' : ''}${name}
</div>
@@ -440,15 +440,15 @@ class FrameGraphTable extends LitElement {
return html`
<tr id="resource-${resourceIndex}">
<th class="sticky-col resource ${selectedStyle}" @click="${onClickResource}">
${hasSubresources
${hasSubresources
? html`
<span class="toggle-icon"
@click="${(e) => { e.stopPropagation(); this._toggleCollapse(resourceIndex); }}">
${isExpanded ? '▼' : '▶'}
</span>`
</span>`
: nothing}
${resource.name}
${hasSubresources && !isExpanded ? html`(${subresourceIds.length})` : nothing}
${hasSubresources && isExpanded ? nothing : html`(${subresourceIds.length})`}
</th>
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
</tr>
@@ -592,12 +592,12 @@ class FrameGraphViewer extends LitElement {
return html`
<framegraph-sidepanel id="sidepanel"
?connected="${this.connected}"
selected-framegraph="${this.selectedFrameGraph}"
selected-framegraph="${this.selectedFrameGraph}"
selected-resource="${this.selectedResourceId}">
</framegraph-sidepanel>
<framegraph-table id="table"
<framegraph-table id="table"
?connected="${this.connected}"
selected-framegraph="${this.selectedFrameGraph}"
selected-framegraph="${this.selectedFrameGraph}"
selected-resource="${this.selectedResourceId}">
</framegraph-table>
`;
@@ -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 = 30;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 29;
enum class Property : uint8_t {
BASE_COLOR, //!< float4, all shading models
ROUGHNESS, //!< float, lit shading models only
@@ -234,7 +234,6 @@ enum class Property : uint8_t {
BENT_NORMAL, //!< float3, all shading models only, except unlit
SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth
SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth
SHADOW_STRENGTH, //!< float, [0, 1] strength of shadows received by this material
// when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT
};
@@ -217,13 +217,12 @@ public:
MaterialBuilder(MaterialBuilder&& rhs) noexcept = default;
MaterialBuilder& operator=(MaterialBuilder&& rhs) noexcept = default;
static constexpr size_t MATERIAL_VARIABLES_COUNT = 5;
static constexpr size_t MATERIAL_VARIABLES_COUNT = 4;
enum class Variable : uint8_t {
CUSTOM0,
CUSTOM1,
CUSTOM2,
CUSTOM3,
CUSTOM4, // CUSTOM4 is only available if the vertex attribute `color` is not required.
CUSTOM3
// when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT
};
+1 -2
View File
@@ -49,8 +49,7 @@ std::unordered_map<std::string, Property> Enums::mStringToProperty = {
{ "microThickness", Property::MICRO_THICKNESS },
{ "bentNormal", Property::BENT_NORMAL },
{ "specularFactor", Property::SPECULAR_FACTOR },
{ "specularColorFactor", Property::SPECULAR_COLOR_FACTOR },
{ "shadowStrength", Property::SHADOW_STRENGTH }
{ "specularColorFactor", Property::SPECULAR_COLOR_FACTOR }
};
template <>
-11
View File
@@ -252,7 +252,6 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
case Variable::CUSTOM4:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
break;
@@ -267,7 +266,6 @@ MaterialBuilder& MaterialBuilder::variable(Variable v,
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
case Variable::CUSTOM4:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), precision, true };
break;
@@ -1256,15 +1254,6 @@ error:
OutputTarget::COLOR, Precision::DEFAULT, OutputType::FLOAT4, "color");
}
if (mMaterialDomain == MaterialDomain::SURFACE) {
if (mRequiredAttributes[VertexAttribute::COLOR] &&
!mVariables[int(Variable::CUSTOM4)].name.empty()) {
// both the color attribute and the custom4 variable are present, that's not supported
slog.e << "Error: when the 'color' attribute is required 'Variable::CUSTOM4' is not supported." << io::endl;
goto error;
}
}
// TODO: maybe check MaterialDomain::COMPUTE has outputs
// Resolve all the #include directives within user code.

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