Compare commits
76
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
791f51dcb4 | ||
|
|
4621f3ec5d | ||
|
|
ab9c09d0d9 | ||
|
|
dbf51c1c7d | ||
|
|
ebd5f150c1 | ||
|
|
156dcb54e8 | ||
|
|
88ef76989c | ||
|
|
fc284af684 | ||
|
|
3a8685cd15 | ||
|
|
f035655f5b | ||
|
|
f498e08d1e | ||
|
|
db3186a65f | ||
|
|
248815a3e0 | ||
|
|
ebb62b5d55 | ||
|
|
b9a5ad205a | ||
|
|
100986d8d9 | ||
|
|
b7c9a36d57 | ||
|
|
d76551ab28 | ||
|
|
92bc0752d1 | ||
|
|
657c251463 | ||
|
|
b6b8fee483 | ||
|
|
be5b60f895 | ||
|
|
681d31758f | ||
|
|
80dc2f5c9d | ||
|
|
ce7dd7a1fb | ||
|
|
85617c96a2 | ||
|
|
58760adb6a | ||
|
|
4ef0b72773 | ||
|
|
45631c3b81 | ||
|
|
6f6dde2579 | ||
|
|
e0acbe42cf | ||
|
|
4f137ccf89 | ||
|
|
04010e7dd9 | ||
|
|
f4f9f331c0 | ||
|
|
dada291f6b | ||
|
|
18f2f8b6d4 | ||
|
|
d4117ac968 | ||
|
|
eccbc8c242 | ||
|
|
3cdf24f515 | ||
|
|
d91faf9df8 | ||
|
|
d950efa6ea | ||
|
|
861d69ea7c | ||
|
|
b2cc904fab | ||
|
|
3f87b520bb | ||
|
|
135a362176 | ||
|
|
391ebc3702 | ||
|
|
b4d1443720 | ||
|
|
c06737aca2 | ||
|
|
f847232296 | ||
|
|
89f06b8923 | ||
|
|
90a2b1f3c9 | ||
|
|
059f51338d | ||
|
|
d3cea5121e | ||
|
|
c37301d2dc | ||
|
|
aa16778fbd | ||
|
|
ee499ad2ca | ||
|
|
958dd5bb27 | ||
|
|
f988b75999 | ||
|
|
e3ccb4bb3c | ||
|
|
3f0dc6f7e6 | ||
|
|
c212dd5bc7 | ||
|
|
1eceab0040 | ||
|
|
ab9d1e5956 | ||
|
|
c906290416 | ||
|
|
d52a24b6e9 | ||
|
|
9aeec3a759 | ||
|
|
550fcfe2e3 | ||
|
|
b2cdf9f2b4 | ||
|
|
19a3fe3dce | ||
|
|
754abbf78e | ||
|
|
9b855e9a03 | ||
|
|
2534fdf92d | ||
|
|
dab02e915e | ||
|
|
68da1a25eb | ||
|
|
c67b363576 | ||
|
|
5c0e7d8211 |
+1
-1
@@ -349,7 +349,7 @@ same version that our continuous builds use.
|
||||
|
||||
```
|
||||
cd <your chosen parent folder for the emscripten SDK>
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.5.zip > emsdk.zip
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.9.zip > emsdk.zip
|
||||
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
|
||||
python ./emsdk.py install latest
|
||||
python ./emsdk.py activate latest
|
||||
|
||||
+1
-3
@@ -654,6 +654,7 @@ add_subdirectory(${LIBRARIES}/utils)
|
||||
add_subdirectory(${LIBRARIES}/viewer)
|
||||
add_subdirectory(${FILAMENT}/filament)
|
||||
add_subdirectory(${FILAMENT}/shaders)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/hat-trie/tnt)
|
||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
@@ -706,9 +707,6 @@ if (IS_HOST_PLATFORM)
|
||||
|
||||
add_subdirectory(${FILAMENT}/samples)
|
||||
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
add_subdirectory(${EXTERNAL}/astcenc/tnt)
|
||||
add_subdirectory(${EXTERNAL}/etc2comp)
|
||||
add_subdirectory(${EXTERNAL}/libassimp/tnt)
|
||||
add_subdirectory(${EXTERNAL}/libpng/tnt)
|
||||
add_subdirectory(${EXTERNAL}/libsdl2/tnt)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.21.2'
|
||||
implementation 'com.google.android.filament:filament-android:1.21.3'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.21.2'
|
||||
pod 'Filament', '~> 1.21.3'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
@@ -177,6 +177,7 @@ steps:
|
||||
- [x] KHR_materials_variants
|
||||
- [x] KHR_materials_volume
|
||||
- [x] KHR_mesh_quantization
|
||||
- [x] KHR_texture_basisu
|
||||
- [x] KHR_texture_transform
|
||||
|
||||
|
||||
|
||||
+20
-2
@@ -3,7 +3,25 @@
|
||||
This file contains one line summaries of commits that are worthy of mentioning in release notes.
|
||||
A new header is inserted each time a *tag* is created.
|
||||
|
||||
## v1.21.3 (currently main branch)
|
||||
## v1.22.1 (currently main branch)
|
||||
|
||||
## v1.22.0
|
||||
|
||||
- engine: Changed UBOs layout [⚠️ **Material breakage**].
|
||||
- engine: Improve effects relying on mipmapping
|
||||
|
||||
## v1.21.3
|
||||
|
||||
- Java: Renamed the `KTXLoader` Kotlin class to `KTX1Loader`. [⚠️ **API Change**].
|
||||
- libs: Added `Ktx2Reader` and `BasisEncoder` to support Basis-encoded KTX2 files.
|
||||
- engine: Remove deprecated `Stream` APIs, see `Texture::import()` for an alternative [⚠️ **API Change**].
|
||||
- tools: Removed KTX1 compression support from mipgen. [⚠️ **API Change**].
|
||||
- tools: Added support for KTX2 to mipgen.
|
||||
- gltfio: Added `resetBoneMatrices()` method.
|
||||
- gltfio: Introduced `TextureProvider` interface. [⚠️ **API Change**].
|
||||
- gltfio: Fix progress indicator when error occurs.
|
||||
- samples: Desktop and Web Suzanne samples now use KTX2 instead of KTX1.
|
||||
- samples: Fix regression with sample-gltf-viewer and zip files.
|
||||
|
||||
## v1.21.2
|
||||
|
||||
@@ -453,7 +471,7 @@ devices.
|
||||
- engine: Fix RenderTarget NPE when depth is not present.
|
||||
- engine: Improvements to Camera APIs. Move focus distance from DofOptions to Camera.
|
||||
- engine: VSM shadows now support `shadowMultiplier`.
|
||||
- java: Expose severla MaterialInstance APIs (setColorWrite, setDepthWrite, setDepthCulling) that
|
||||
- java: Expose several MaterialInstance APIs (setColorWrite, setDepthWrite, setDepthCulling) that
|
||||
should have been public.
|
||||
- java: fix bug with Texture::setImage buffer size calculation.
|
||||
|
||||
|
||||
+11
-1
@@ -11,6 +11,9 @@
|
||||
// com.google.android.filament.exclude-vulkan
|
||||
// When set, support for Vulkan will be excluded.
|
||||
//
|
||||
// com.google.android.filament.matdbg
|
||||
// When set, enables matdbg, disables shader optimizations
|
||||
//
|
||||
// com.google.android.filament.skip-samples
|
||||
// Exclude samples from the project. Useful to speed up compilation.
|
||||
//
|
||||
@@ -59,6 +62,11 @@ buildscript {
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def matdbg = providers
|
||||
.gradleProperty("com.google.android.filament.matdbg")
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
|
||||
def newAbis = providers
|
||||
.gradleProperty("com.google.android.filament.abis")
|
||||
@@ -102,7 +110,9 @@ buildscript {
|
||||
"-DANDROID_PLATFORM=21",
|
||||
"-DANDROID_STL=c++_static",
|
||||
"-DFILAMENT_DIST_DIR=${filamentPath}".toString(),
|
||||
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString()
|
||||
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(),
|
||||
"-DFILAMENT_ENABLE_MATDBG=${matdbg ? 'ON' : 'OFF'}".toString(),
|
||||
"-DFILAMENT_DISABLE_MATOPT=${matdbg ? 'ON' : 'OFF'}".toString()
|
||||
]
|
||||
|
||||
ext.cppFlags = [
|
||||
|
||||
@@ -98,16 +98,6 @@ Java_com_google_android_filament_Stream_nBuilderStreamSource(JNIEnv* env,
|
||||
builder->setStreamSource(env, streamSource);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Stream_nBuilderStream(JNIEnv*, jclass,
|
||||
jlong nativeStreamBuilder, jlong externalTextureId) {
|
||||
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
builder->builder()->stream(externalTextureId);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Stream_nBuilderWidth(JNIEnv*, jclass,
|
||||
jlong nativeStreamBuilder, jint width) {
|
||||
@@ -143,41 +133,6 @@ Java_com_google_android_filament_Stream_nSetDimensions(JNIEnv*, jclass, jlong na
|
||||
stream->setDimensions((uint32_t) width, (uint32_t) height);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Stream_nReadPixels(JNIEnv *env, jclass,
|
||||
jlong nativeStream, jlong nativeEngine,
|
||||
jint xoffset, jint yoffset, jint width, jint height,
|
||||
jobject storage, jint remaining,
|
||||
jint left, jint top, jint type, jint alignment, jint stride, jint format,
|
||||
jobject handler, jobject runnable) {
|
||||
Stream *stream = (Stream *) nativeStream;
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
|
||||
stride = stride ? stride : width;
|
||||
size_t sizeInBytes = PixelBufferDescriptor::computeDataSize(
|
||||
(PixelDataFormat) format, (PixelDataType) type,
|
||||
(size_t) stride, (size_t) (height + top), (size_t) alignment);
|
||||
|
||||
AutoBuffer nioBuffer(env, storage, 0);
|
||||
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
|
||||
// BufferOverflowException
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *buffer = nioBuffer.getData();
|
||||
auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
|
||||
|
||||
PixelBufferDescriptor desc(buffer, sizeInBytes, (backend::PixelDataFormat) format,
|
||||
(backend::PixelDataType) type, (uint8_t) alignment, (uint32_t) left, (uint32_t) top,
|
||||
(uint32_t) stride,
|
||||
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
|
||||
|
||||
stream->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
|
||||
std::move(desc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Stream_nGetTimestamp(JNIEnv*, jclass, jlong nativeStream) {
|
||||
Stream *stream = (Stream *) nativeStream;
|
||||
|
||||
@@ -531,6 +531,10 @@ public class Renderer {
|
||||
* Typically, this will happen after multiple calls to {@link #beginFrame},
|
||||
* {@link #render}, {@link #endFrame}.</p>
|
||||
* <br>
|
||||
* <p>OpenGL only: if issuing a <code>readPixels</code> on a {@link RenderTarget} backed by a
|
||||
* {@link Texture} that had data uploaded to it via {@link Texture#setImage}, the data returned
|
||||
* from <code>readPixels</code> will be y-flipped with respect to the {@link Texture#setImage}
|
||||
* call.</p>
|
||||
* <p><code>readPixels</code> is intended for debugging and testing.
|
||||
* It will impact performance significantly.</p>
|
||||
*
|
||||
|
||||
@@ -103,9 +103,6 @@ public class Stream {
|
||||
/** Not synchronized but copy-free. Good for video. */
|
||||
NATIVE,
|
||||
|
||||
/** Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26. */
|
||||
TEXTURE_ID,
|
||||
|
||||
/** Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+. */
|
||||
ACQUIRED,
|
||||
};
|
||||
@@ -121,7 +118,7 @@ public class Stream {
|
||||
* By default, Stream objects are {@link StreamType#ACQUIRED ACQUIRED} and must have external images pushed to them via
|
||||
* {@link #setAcquiredImage}.
|
||||
*
|
||||
* To create a {@link StreamType#NATIVE NATIVE} or {@link StreamType#TEXTURE_ID TEXTURE_ID} stream, call one of the <pre>stream</pre> methods
|
||||
* To create a {@link StreamType#NATIVE NATIVE} stream, call one of the <pre>stream</pre> methods
|
||||
* on the builder.
|
||||
*/
|
||||
public static class Builder {
|
||||
@@ -156,27 +153,6 @@ public class Stream {
|
||||
throw new IllegalArgumentException("Invalid stream source: " + streamSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamType#TEXTURE_ID TEXTURE_ID} stream. A copy stream will sample data from the supplied
|
||||
* external texture and copy it into an internal private texture.
|
||||
*
|
||||
* <p>Currently only OpenGL external texture ids are supported.</p>
|
||||
*
|
||||
* @param externalTextureId An opaque texture id (typically a GLuint created with
|
||||
* <code>glGenTextures()</code>) in a context shared with
|
||||
* filament -- in that case this texture's target must be
|
||||
* <code>GL_TEXTURE_EXTERNAL_OES.</code>
|
||||
* @return This Builder, for chaining calls.
|
||||
* @see Texture#setExternalStream
|
||||
* @deprecated this method existed only for ARCore which doesn't need this anymore, use {@link Texture.Builder#importTexture(long)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public Builder stream(long externalTextureId) {
|
||||
nBuilderStream(mNativeBuilder, externalTextureId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width initial width of the incoming stream. Whether this value is used is
|
||||
* stream dependent. On Android, it must be set when using
|
||||
@@ -276,92 +252,6 @@ public class Stream {
|
||||
nSetDimensions(getNativeObject(), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads back the content of the last frame of a <code>Stream</code> since the last call to
|
||||
* {@link Renderer#beginFrame}.
|
||||
*
|
||||
* <p>The Stream must be a copy stream, which can be checked with {@link #getStreamType()}.
|
||||
* This function is a no-op otherwise.</p>
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* Stream buffer User buffer (PixelBufferDescriptor)
|
||||
* +--------------------+
|
||||
* | | .stride .alignment
|
||||
* | | ----------------------->-->
|
||||
* | | O----------------------+--+ low addresses
|
||||
* | | | | | |
|
||||
* | w | | | .top | |
|
||||
* | <---------> | | V | |
|
||||
* | +---------+ | | +---------+ | |
|
||||
* | | ^ | | ======> | | | | |
|
||||
* | x | h| | | |.left| | | |
|
||||
* +------>| v | | +---->| | | |
|
||||
* | +.........+ | | +.........+ | |
|
||||
* | ^ | | | |
|
||||
* | y | | +----------------------+--+ high addresses
|
||||
* O------------+-------+
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p>Typically readPixels() will be called after {@link Renderer#beginFrame}.</p>
|
||||
*
|
||||
* <p>After calling this method, the callback associated with <code>buffer</code>
|
||||
* will be invoked on the main thread, indicating that the read-back has completed.
|
||||
* Typically, this will happen after multiple calls to {@link Renderer#beginFrame},
|
||||
* {@link Renderer#render}, {@link Renderer#endFrame}.</p>
|
||||
*
|
||||
* <p><code>readPixels</code> is intended for debugging and testing.
|
||||
* It will impact performance significantly.</p>
|
||||
*
|
||||
* @param xoffset left offset of the sub-region to read back
|
||||
* @param yoffset bottom offset of the sub-region to read back
|
||||
* @param width width of the sub-region to read back
|
||||
* @param height height of the sub-region to read back
|
||||
* @param buffer client-side buffer where the read-back will be written
|
||||
*
|
||||
* <p>
|
||||
* The following format are always supported:
|
||||
* <li>{@link Texture.Format#RGBA}</li>
|
||||
* <li>{@link Texture.Format#RGBA_INTEGER}</li>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The following types are always supported:
|
||||
* <li>{@link Texture.Type#UBYTE}</li>
|
||||
* <li>{@link Texture.Type#UINT}</li>
|
||||
* <li>{@link Texture.Type#INT}</li>
|
||||
* <li>{@link Texture.Type#FLOAT}</li>
|
||||
* </p>
|
||||
*
|
||||
* <p>Other combination of format/type may be supported. If a combination is
|
||||
* not supported, this operation may fail silently. Use a DEBUG build
|
||||
* to get some logs about the failure.</p>
|
||||
*
|
||||
* @exception BufferOverflowException if the specified parameters would result in reading
|
||||
* outside of <code>buffer</code>.
|
||||
*/
|
||||
public void readPixels(
|
||||
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
|
||||
@IntRange(from = 0) int width, @IntRange(from = 0) int height,
|
||||
@NonNull Texture.PixelBufferDescriptor buffer) {
|
||||
|
||||
if (buffer.storage.isReadOnly()) {
|
||||
throw new ReadOnlyBufferException();
|
||||
}
|
||||
|
||||
int result = nReadPixels(getNativeObject(), mNativeEngine,
|
||||
xoffset, yoffset, width, height,
|
||||
buffer.storage, buffer.storage.remaining(),
|
||||
buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
|
||||
buffer.stride, buffer.format.ordinal(),
|
||||
buffer.handler, buffer.callback);
|
||||
|
||||
if (result < 0) {
|
||||
throw new BufferOverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presentation time of the currently displayed frame in nanosecond.
|
||||
*
|
||||
@@ -387,7 +277,6 @@ public class Stream {
|
||||
private static native long nCreateBuilder();
|
||||
private static native void nDestroyBuilder(long nativeStreamBuilder);
|
||||
private static native void nBuilderStreamSource(long nativeStreamBuilder, Object streamSource);
|
||||
private static native void nBuilderStream(long nativeStreamBuilder, long externalTextureId);
|
||||
private static native void nBuilderWidth(long nativeStreamBuilder, int width);
|
||||
private static native void nBuilderHeight(long nativeStreamBuilder, int height);
|
||||
private static native long nBuilderBuild(long nativeStreamBuilder, long nativeEngine);
|
||||
|
||||
@@ -99,8 +99,8 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
|
||||
|
||||
int rc;
|
||||
|
||||
// KTXLoader
|
||||
jclass ktxloaderClass = env->FindClass("com/google/android/filament/utils/KTXLoader");
|
||||
// KTX1Loader
|
||||
jclass ktxloaderClass = env->FindClass("com/google/android/filament/utils/KTX1Loader");
|
||||
if (ktxloaderClass == nullptr) return JNI_ERR;
|
||||
static const JNINativeMethod ktxMethods[] = {
|
||||
{(char*)"nCreateKTXTexture", (char*)"(JLjava/nio/Buffer;IZ)J", reinterpret_cast<void*>(nCreateKTXTexture)},
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ import com.google.android.filament.Renderer;
|
||||
*
|
||||
* When executing a test, clients should call tick() after each frame is rendered, which provides an
|
||||
* opportunity to push settings to Filament, increment the current test index (if enough time has
|
||||
* elapsed), and request an asychronous screenshot.
|
||||
* elapsed), and request an asynchronous screenshot.
|
||||
*
|
||||
* The time to sleep between tests is configurable and can be set to zero. Automation also waits a
|
||||
* specified minimum number of frames between tests.
|
||||
|
||||
+2
-2
@@ -24,12 +24,12 @@ import com.google.android.filament.Texture
|
||||
import java.nio.Buffer
|
||||
|
||||
/**
|
||||
* Utilities for consuming KTX files and producing Filament textures, IBLs, and sky boxes.
|
||||
* Utilities for consuming KTX1 files and producing Filament textures, IBLs, and sky boxes.
|
||||
*
|
||||
* KTX is a simple container format that makes it easy to bundle miplevels and cubemap faces
|
||||
* into a single file.
|
||||
*/
|
||||
object KTXLoader {
|
||||
object KTX1Loader {
|
||||
class Options {
|
||||
var srgb = false
|
||||
}
|
||||
@@ -11,6 +11,18 @@ add_library(dracodec STATIC IMPORTED)
|
||||
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
|
||||
|
||||
add_library(ktxreader STATIC IMPORTED)
|
||||
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
|
||||
|
||||
add_library(stb STATIC IMPORTED)
|
||||
set_target_properties(stb PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libstb.a)
|
||||
|
||||
add_library(basis_transcoder STATIC IMPORTED)
|
||||
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
|
||||
|
||||
add_library(utils STATIC IMPORTED)
|
||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||
@@ -32,6 +44,7 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
|
||||
${GLTFIO_DIR}/include/gltfio/FilamentAsset.h
|
||||
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
|
||||
${GLTFIO_DIR}/include/gltfio/TextureProvider.h
|
||||
${GLTFIO_DIR}/include/gltfio/math.h
|
||||
|
||||
${GLTFIO_DIR}/src/Animator.cpp
|
||||
@@ -45,15 +58,16 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/src/FFilamentInstance.h
|
||||
${GLTFIO_DIR}/src/FilamentInstance.cpp
|
||||
${GLTFIO_DIR}/src/GltfEnums.h
|
||||
${GLTFIO_DIR}/src/Ktx2Provider.cpp
|
||||
${GLTFIO_DIR}/src/MaterialProvider.cpp
|
||||
${GLTFIO_DIR}/src/ResourceLoader.cpp
|
||||
${GLTFIO_DIR}/src/StbProvider.cpp
|
||||
${GLTFIO_DIR}/src/TangentsJob.h
|
||||
${GLTFIO_DIR}/src/TangentsJob.cpp
|
||||
${GLTFIO_DIR}/src/UbershaderLoader.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.h
|
||||
${GLTFIO_DIR}/src/upcast.h
|
||||
${GLTFIO_DIR}/src/Image.cpp
|
||||
|
||||
src/main/cpp/Animator.cpp
|
||||
src/main/cpp/AssetLoader.cpp
|
||||
@@ -81,6 +95,7 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../third_party/hat-trie
|
||||
../../third_party/stb
|
||||
../../libs/utils/include
|
||||
../../libs/ktxreader/include
|
||||
)
|
||||
|
||||
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
||||
@@ -90,9 +105,9 @@ set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_
|
||||
|
||||
if(GLTFIO_LITE)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_LITE=1)
|
||||
target_link_libraries(gltfio-jni filament-jni utils log gltfio_resources_lite)
|
||||
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder gltfio_resources_lite)
|
||||
else()
|
||||
target_link_libraries(gltfio-jni filament-jni utils log gltfio_resources)
|
||||
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder gltfio_resources)
|
||||
|
||||
# Enable Draco in the non-lite variant of gltfio.
|
||||
target_link_libraries(gltfio-jni dracodec)
|
||||
|
||||
@@ -36,6 +36,12 @@ Java_com_google_android_filament_gltfio_Animator_nUpdateBoneMatrices(JNIEnv*, jc
|
||||
animator->updateBoneMatrices();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_Animator_nResetBoneMatrices(JNIEnv*, jclass, jlong nativeAnimator) {
|
||||
Animator* animator = (Animator*) nativeAnimator;
|
||||
animator->resetBoneMatrices();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_gltfio_Animator_nGetAnimationCount(JNIEnv*, jclass, jlong nativeAnimator) {
|
||||
Animator* animator = (Animator*) nativeAnimator;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/TextureProvider.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
@@ -114,3 +115,34 @@ Java_com_google_android_filament_gltfio_ResourceLoader_nAsyncCancelLoad(JNIEnv*,
|
||||
ResourceLoader* loader = (ResourceLoader*) nativeLoader;
|
||||
loader->asyncCancelLoad();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateStbProvider(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) createStbProvider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateKtx2Provider(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) createKtx2Provider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nDestroyTextureProvider(JNIEnv*, jclass,
|
||||
jlong nativeProvider) {
|
||||
TextureProvider* provider = (TextureProvider*) nativeProvider;
|
||||
delete provider;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nAddTextureProvider(JNIEnv* env, jclass,
|
||||
jlong nativeLoader, jstring url, jlong nativeProvider) {
|
||||
ResourceLoader* loader = (ResourceLoader*) nativeLoader;
|
||||
TextureProvider* provider = (TextureProvider*) nativeProvider;
|
||||
const char* cstring = env->GetStringUTFChars(url, nullptr);
|
||||
loader->addTextureProvider(cstring, provider);
|
||||
env->ReleaseStringUTFChars(url, cstring);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,15 @@ public class Animator {
|
||||
nUpdateBoneMatrices(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the identity matrix into all bone nodes, useful for returning to the T pose.
|
||||
*
|
||||
* <p>NOTE: this operation is independent of <code>animation</code>.</p>
|
||||
*/
|
||||
public void resetBoneMatrices() {
|
||||
nResetBoneMatrices(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of <code>animation</code> definitions in the glTF asset.
|
||||
*/
|
||||
@@ -109,6 +118,7 @@ public class Animator {
|
||||
|
||||
private static native void nApplyAnimation(long nativeAnimator, int index, float time);
|
||||
private static native void nUpdateBoneMatrices(long nativeAnimator);
|
||||
private static native void nResetBoneMatrices(long nativeAnimator);
|
||||
private static native int nGetAnimationCount(long nativeAnimator);
|
||||
private static native float nGetAnimationDuration(long nativeAnimator, int index);
|
||||
private static native String nGetAnimationName(long nativeAnimator, int index);
|
||||
|
||||
+19
@@ -35,6 +35,8 @@ import java.nio.Buffer;
|
||||
*/
|
||||
public class ResourceLoader {
|
||||
private final long mNativeObject;
|
||||
private final long mNativeStbProvider;
|
||||
private final long mNativeKtx2Provider;
|
||||
|
||||
/**
|
||||
* Constructs a resource loader tied to the given Filament engine.
|
||||
@@ -46,6 +48,11 @@ public class ResourceLoader {
|
||||
public ResourceLoader(@NonNull Engine engine) {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +70,11 @@ public class ResourceLoader {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
|
||||
recomputeBoundingBoxes, ignoreBindTransform);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +82,8 @@ public class ResourceLoader {
|
||||
*/
|
||||
public void destroy() {
|
||||
nDestroyResourceLoader(mNativeObject);
|
||||
nDestroyTextureProvider(mNativeStbProvider);
|
||||
nDestroyTextureProvider(mNativeKtx2Provider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,4 +192,9 @@ public class ResourceLoader {
|
||||
private static native float nAsyncGetLoadProgress(long nativeLoader);
|
||||
private static native void nAsyncUpdateLoad(long nativeLoader);
|
||||
private static native void nAsyncCancelLoad(long nativeLoader);
|
||||
|
||||
private static native long nCreateStbProvider(long nativeEngine);
|
||||
private static native long nCreateKtx2Provider(long nativeEngine);
|
||||
private static native void nAddTextureProvider(long nativeLoader, String url, long nativeProvider);
|
||||
private static native void nDestroyTextureProvider(long nativeProvider);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.21.2
|
||||
VERSION_NAME=1.21.3
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+7
-6
@@ -150,12 +150,12 @@ class MainActivity : Activity() {
|
||||
val scene = modelViewer.scene
|
||||
val ibl = "default_env"
|
||||
readCompressedAsset("envs/$ibl/${ibl}_ibl.ktx").let {
|
||||
scene.indirectLight = KTXLoader.createIndirectLight(engine, it)
|
||||
scene.indirectLight = KTX1Loader.createIndirectLight(engine, it)
|
||||
scene.indirectLight!!.intensity = 30_000.0f
|
||||
viewerContent.indirectLight = modelViewer.scene.indirectLight
|
||||
}
|
||||
readCompressedAsset("envs/$ibl/${ibl}_skybox.ktx").let {
|
||||
scene.skybox = KTXLoader.createSkybox(engine, it)
|
||||
scene.skybox = KTX1Loader.createSkybox(engine, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,9 +297,10 @@ class MainActivity : Activity() {
|
||||
|
||||
val gltfBuffer = pathToBufferMapping[gltfPath]!!
|
||||
|
||||
// The gltf is often not at the root level (e.g. if a folder is zipped) so
|
||||
// we need to extract its path in order to resolve the embedded uri strings.
|
||||
var prefix = URI(gltfPath!!).resolve("..")
|
||||
// In a zip file, the gltf file might be in the same folder as resources, or in a different
|
||||
// folder. It is crucial to test against both of these cases. In any case, the resource
|
||||
// paths are all specified relative to the location of the gltf file.
|
||||
var prefix = URI(gltfPath!!).resolve(".")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (gltfPath!!.endsWith(".glb")) {
|
||||
@@ -308,7 +309,7 @@ class MainActivity : Activity() {
|
||||
modelViewer.loadModelGltf(gltfBuffer) { uri ->
|
||||
val path = prefix.resolve(uri).toString()
|
||||
if (!pathToBufferMapping.contains(path)) {
|
||||
Log.e(TAG, "Could not find '$uri' in zip using prefix '$prefix'")
|
||||
Log.e(TAG, "Could not find '$uri' in zip using prefix '$prefix' and base path '${gltfPath!!}'")
|
||||
setStatusText("Zip is missing $path")
|
||||
}
|
||||
pathToBufferMapping[path]
|
||||
|
||||
+1
-32
@@ -79,8 +79,6 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||
// Performs the rendering and schedules new frames
|
||||
private val frameScheduler = FrameCallback()
|
||||
|
||||
private var externalTextureID: Int = 0
|
||||
|
||||
@RequiresApi(30)
|
||||
class Api30Impl {
|
||||
companion object {
|
||||
@@ -103,8 +101,6 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||
setupView()
|
||||
setupScene()
|
||||
|
||||
externalTextureID = createExternalTexture()
|
||||
|
||||
@Suppress("deprecation")
|
||||
val display = if (Build.VERSION.SDK_INT >= 30) {
|
||||
Api30Impl.getDisplay(this)
|
||||
@@ -112,7 +108,7 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||
windowManager.defaultDisplay!!
|
||||
}
|
||||
|
||||
streamHelper = StreamHelper(engine, materialInstance, display, externalTextureID)
|
||||
streamHelper = StreamHelper(engine, materialInstance, display)
|
||||
this.title = streamHelper.getTestName()
|
||||
}
|
||||
|
||||
@@ -445,31 +441,4 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||
check(EGL14.eglMakeCurrent(display, surface, surface, context)) { "Error making GL context." }
|
||||
return context
|
||||
}
|
||||
|
||||
private fun createExternalTexture(): Int {
|
||||
val textures = IntArray(1)
|
||||
GLES30.glGenTextures(1, textures, 0)
|
||||
val result = textures[0]
|
||||
|
||||
val textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES
|
||||
GLES30.glBindTexture(textureTarget, result)
|
||||
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
|
||||
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
|
||||
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
|
||||
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST)
|
||||
|
||||
if (!GLES30.glIsTexture(result)) {
|
||||
throw RuntimeException("OpenGL error: $result is an invalid texture.")
|
||||
}
|
||||
|
||||
val error = GLES30.glGetError()
|
||||
if (error != GLES30.GL_NO_ERROR) {
|
||||
val errorString = GLU.gluErrorString(error)
|
||||
throw RuntimeException("OpenGL error: $errorString!")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-24
@@ -37,7 +37,6 @@ class StreamHelper(
|
||||
private val filamentEngine: Engine,
|
||||
private val filamentMaterial: MaterialInstance,
|
||||
private val display: Display,
|
||||
private val externalTextureId: Int
|
||||
) {
|
||||
/**
|
||||
* The StreamSource configures the source data for the texture.
|
||||
@@ -51,7 +50,6 @@ class StreamHelper(
|
||||
*/
|
||||
enum class StreamSource {
|
||||
CANVAS_STREAM_NATIVE, // copy-free but does not guarantee synchronization
|
||||
CANVAS_STREAM_TEXID, // synchronized but incurs a copy
|
||||
CANVAS_STREAM_ACQUIRED, // synchronized and copy-free
|
||||
}
|
||||
|
||||
@@ -127,10 +125,6 @@ class StreamHelper(
|
||||
|
||||
surface.unlockCanvasAndPost(canvas)
|
||||
|
||||
if (streamSource == StreamSource.CANVAS_STREAM_TEXID) {
|
||||
surfaceTexture!!.updateTexImage()
|
||||
}
|
||||
|
||||
if (streamSource == StreamSource.CANVAS_STREAM_ACQUIRED) {
|
||||
val image = imageReader!!.acquireLatestImage()
|
||||
filamentStream!!.setAcquiredImage(
|
||||
@@ -146,7 +140,7 @@ class StreamHelper(
|
||||
|
||||
fun nextTest() {
|
||||
stopTest()
|
||||
streamSource = StreamSource.values()[(streamSource.ordinal + 1) % 3]
|
||||
streamSource = StreamSource.values()[(streamSource.ordinal + 1) % StreamSource.values().size]
|
||||
startTest()
|
||||
}
|
||||
|
||||
@@ -213,23 +207,6 @@ class StreamHelper(
|
||||
filamentTexture.setExternalStream(filamentEngine, filamentStream!!)
|
||||
}
|
||||
|
||||
if (streamSource == StreamSource.CANVAS_STREAM_TEXID) {
|
||||
|
||||
// Create the Android surface that will hold the canvas image.
|
||||
surfaceTexture = SurfaceTexture(externalTextureId)
|
||||
surfaceTexture!!.setDefaultBufferSize(resolution.width, resolution.height)
|
||||
canvasSurface = Surface(surfaceTexture)
|
||||
|
||||
// Create the Filament Stream object that gets bound to the Texture.
|
||||
filamentStream = Stream.Builder()
|
||||
.stream(externalTextureId.toLong())
|
||||
.width(resolution.width)
|
||||
.height(resolution.height)
|
||||
.build(filamentEngine)
|
||||
|
||||
filamentTexture.setExternalStream(filamentEngine, filamentStream!!)
|
||||
}
|
||||
|
||||
if (streamSource == StreamSource.CANVAS_STREAM_ACQUIRED) {
|
||||
filamentStream = Stream.Builder()
|
||||
.width(resolution.width)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -98,9 +98,9 @@ function print_matdbg_help {
|
||||
echo ""
|
||||
echo "FOR ANDROID BUILDS:"
|
||||
echo ""
|
||||
echo "1) The most reliable way to enable matdbg is to bypass Gradle and"
|
||||
echo " directly modify the appropriate two lines in the following file:"
|
||||
echo " ./android/filament-android/CMakeLists.txt"
|
||||
echo "1) For Android Studio builds, make sure to set:"
|
||||
echo " -Pcom.google.android.filament.matdbg"
|
||||
echo " option in Preferences > Build > Compiler > Command line options."
|
||||
echo ""
|
||||
echo "2) The port number is hardcoded to 8081 so you will need to do:"
|
||||
echo " adb forward tcp:8081 tcp:8081"
|
||||
@@ -156,6 +156,7 @@ VULKAN_ANDROID_GRADLE_OPTION=""
|
||||
SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=OFF"
|
||||
|
||||
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
|
||||
MATDBG_GRADLE_OPTION=""
|
||||
|
||||
IOS_BUILD_SIMULATOR=false
|
||||
BUILD_UNIVERSAL_LIBRARIES=false
|
||||
@@ -303,7 +304,7 @@ function build_webgl_with_target {
|
||||
}
|
||||
|
||||
function build_webgl {
|
||||
# For the host tools, supress install and always use Release.
|
||||
# For the host tools, suppress install and always use Release.
|
||||
local old_install_command=${INSTALL_COMMAND}; INSTALL_COMMAND=
|
||||
local old_issue_debug_build=${ISSUE_DEBUG_BUILD}; ISSUE_DEBUG_BUILD=false
|
||||
local old_issue_release_build=${ISSUE_RELEASE_BUILD}; ISSUE_RELEASE_BUILD=true
|
||||
@@ -402,7 +403,7 @@ function ensure_android_build {
|
||||
function build_android {
|
||||
ensure_android_build
|
||||
|
||||
# Supress intermediate desktop tools install
|
||||
# Suppress intermediate desktop tools install
|
||||
local old_install_command=${INSTALL_COMMAND}
|
||||
INSTALL_COMMAND=
|
||||
|
||||
@@ -456,6 +457,7 @@ function build_android {
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${MATDBG_GRADLE_OPTION} \
|
||||
:filament-android:assembleDebug \
|
||||
:gltfio-android:assembleDebug \
|
||||
:filament-utils-android:assembleDebug
|
||||
@@ -504,6 +506,7 @@ function build_android {
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${MATDBG_GRADLE_OPTION} \
|
||||
:filament-android:assembleRelease \
|
||||
:gltfio-android:assembleRelease \
|
||||
:filament-utils-android:assembleRelease
|
||||
@@ -598,7 +601,7 @@ function archive_ios {
|
||||
}
|
||||
|
||||
function build_ios {
|
||||
# Supress intermediate desktop tools install
|
||||
# Suppress intermediate desktop tools install
|
||||
local old_install_command=${INSTALL_COMMAND}
|
||||
INSTALL_COMMAND=
|
||||
|
||||
@@ -754,6 +757,7 @@ while getopts ":hacCfijmp:q:uvslwtdk:" opt; do
|
||||
d)
|
||||
PRINT_MATDBG_HELP=true
|
||||
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON, -DFILAMENT_BUILD_FILAMAT=ON"
|
||||
MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg"
|
||||
;;
|
||||
f)
|
||||
ISSUE_CMAKE_ALWAYS=true
|
||||
|
||||
@@ -17,7 +17,7 @@ export PATH="$PWD:$PATH"
|
||||
# npm install -g typescript
|
||||
|
||||
# Install emscripten.
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.5.zip > emsdk.zip
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.9.zip > emsdk.zip
|
||||
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
|
||||
./emsdk install latest
|
||||
./emsdk activate latest
|
||||
|
||||
@@ -2169,7 +2169,7 @@ type aliases:
|
||||
|
||||
Name | Type | Description
|
||||
:-----------------------------------|:--------:|:------------------------------------
|
||||
**getResolution()** | float4 | Resolution of the view in pixels: `width`, `height`, `1 / width`, `1 / height`
|
||||
**getResolution()** | float4 | Dimensions of the view's effective viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. This can be used in conjunction with `getNormalizedViewportCoord()` to generate pixel coordinates.
|
||||
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space
|
||||
**getWorldOffset()** | float3 | The shift required to obtain API-level world space
|
||||
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
|
||||
|
||||
@@ -77,6 +77,7 @@ set(SRCS
|
||||
src/RenderTarget.cpp
|
||||
src/RenderableManager.cpp
|
||||
src/Renderer.cpp
|
||||
src/RendererUtils.cpp
|
||||
src/ResourceAllocator.cpp
|
||||
src/ResourceList.cpp
|
||||
src/Scene.cpp
|
||||
@@ -143,6 +144,7 @@ set(PRIVATE_HDRS
|
||||
src/PerViewUniforms.h
|
||||
src/PIDController.h
|
||||
src/PostProcessManager.h
|
||||
src/RendererUtils.h
|
||||
src/RenderPass.h
|
||||
src/RenderPrimitive.h
|
||||
src/ResourceAllocator.h
|
||||
|
||||
@@ -65,8 +65,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
|
||||
src/opengl/gl_headers.h
|
||||
src/opengl/GLUtils.cpp
|
||||
src/opengl/GLUtils.h
|
||||
src/opengl/OpenGLBlitter.cpp
|
||||
src/opengl/OpenGLBlitter.h
|
||||
src/opengl/OpenGLContext.cpp
|
||||
src/opengl/OpenGLContext.h
|
||||
src/opengl/OpenGLDriver.cpp
|
||||
@@ -89,7 +87,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
|
||||
endif ()
|
||||
if (ANDROID)
|
||||
list(APPEND SRCS src/opengl/platforms/ExternalStreamManagerAndroid.cpp)
|
||||
list(APPEND SRCS src/android/ExternalTextureManagerAndroid.cpp)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformEGLAndroid.cpp)
|
||||
elseif (IOS)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
|
||||
|
||||
@@ -42,22 +42,22 @@
|
||||
*/
|
||||
namespace filament::backend {
|
||||
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
|
||||
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
|
||||
|
||||
static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
|
||||
"The number of buffer objects that can be attached to a VertexBuffer must be "
|
||||
"less than or equal to the maximum number of vertex attributes.");
|
||||
|
||||
static constexpr size_t CONFIG_BINDING_COUNT = 8;
|
||||
static constexpr size_t CONFIG_BINDING_COUNT = 12; // This is guaranteed by OpenGL ES.
|
||||
|
||||
/**
|
||||
* Selects which driver a particular Engine should use.
|
||||
@@ -788,7 +788,6 @@ enum class BlendFunction : uint8_t {
|
||||
//! Stream for external textures
|
||||
enum class StreamType {
|
||||
NATIVE, //!< Not synchronized but copy-free. Good for video.
|
||||
TEXTURE_ID, //!< Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26.
|
||||
ACQUIRED, //!< Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+.
|
||||
};
|
||||
|
||||
@@ -913,7 +912,7 @@ struct ShaderStageFlags {
|
||||
(fragment && type == ShaderType::FRAGMENT);
|
||||
}
|
||||
};
|
||||
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { .vertex = true, .fragment = true };
|
||||
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { true, true };
|
||||
|
||||
/**
|
||||
* Selects which buffers to clear at the beginning of the render pass, as well as which buffers
|
||||
|
||||
@@ -246,11 +246,6 @@ DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless,
|
||||
uint32_t, height,
|
||||
uint64_t, flags)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::StreamHandle, createStreamFromTextureId,
|
||||
intptr_t, externalTextureId,
|
||||
uint32_t, width,
|
||||
uint32_t, height)
|
||||
|
||||
DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
|
||||
|
||||
|
||||
@@ -461,14 +456,6 @@ DECL_DRIVER_API_N(readPixels,
|
||||
uint32_t, height,
|
||||
backend::PixelBufferDescriptor&&, data)
|
||||
|
||||
DECL_DRIVER_API_N(readStreamPixels,
|
||||
backend::StreamHandle, sh,
|
||||
uint32_t, x,
|
||||
uint32_t, y,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
backend::PixelBufferDescriptor&&, data)
|
||||
|
||||
/*
|
||||
* Rendering operations
|
||||
* --------------------
|
||||
|
||||
@@ -82,15 +82,6 @@ public:
|
||||
virtual void detach(Stream* stream) noexcept = 0;
|
||||
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept = 0;
|
||||
|
||||
// external texture storage
|
||||
virtual ExternalTexture* createExternalTextureStorage() noexcept = 0;
|
||||
|
||||
// this is called synchronously in the application thread (NOT the Driver thread)
|
||||
virtual void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept = 0;
|
||||
|
||||
virtual void destroyExternalTextureStorage(ExternalTexture* ets) noexcept = 0;
|
||||
|
||||
// The method allows platforms to convert a user-supplied external image object into a new type
|
||||
// (e.g. HardwareBuffer => EGLImage). It makes sense for the default implementation to do nothing.
|
||||
virtual AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept { return source; }
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "ExternalTextureManagerAndroid.h"
|
||||
|
||||
#include <utils/api_level.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
template <typename T>
|
||||
static void loadSymbol(T*& pfn, const char *symbol) noexcept {
|
||||
pfn = (T*)dlsym(RTLD_DEFAULT, symbol);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ndk {
|
||||
/*
|
||||
* This mimics the GraphicBuffer class for pre-API 26
|
||||
*/
|
||||
template<typename NATIVE_TYPE, typename REF>
|
||||
struct ANativeObjectBase : public NATIVE_TYPE, public REF {
|
||||
};
|
||||
|
||||
struct android_native_base_t {
|
||||
unsigned int magic;
|
||||
unsigned int version;
|
||||
void* reserved[4];
|
||||
void (* incRef)(struct android_native_base_t* base);
|
||||
void (* decRef)(struct android_native_base_t* base);
|
||||
};
|
||||
|
||||
struct ANativeWindowBuffer {
|
||||
struct android_native_base_t common;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int stride;
|
||||
unsigned int format;
|
||||
unsigned int usage_deprecated;
|
||||
uintptr_t layerCount;
|
||||
void* reserved[1];
|
||||
const void* handle;
|
||||
uint64_t usage;
|
||||
void* reserved_proc[8 - (sizeof(uint64_t) / sizeof(void*))];
|
||||
};
|
||||
|
||||
struct RefBase {
|
||||
virtual ~RefBase() = default;
|
||||
void* ref;
|
||||
};
|
||||
|
||||
struct GraphicBuffer : public ANativeObjectBase<ANativeWindowBuffer, RefBase> {
|
||||
};
|
||||
struct GraphicBufferWrapper {
|
||||
GraphicBuffer* graphicBuffer;
|
||||
};
|
||||
} // namespace ndk
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
using namespace ndk;
|
||||
|
||||
struct EGLExternalTexture : public ExternalTextureManagerAndroid::ExternalTexture {
|
||||
GraphicBufferWrapper* graphicBufferWrapper = nullptr;
|
||||
};
|
||||
|
||||
ExternalTextureManagerAndroid& ExternalTextureManagerAndroid::create() noexcept {
|
||||
return *(new ExternalTextureManagerAndroid{});
|
||||
}
|
||||
|
||||
void ExternalTextureManagerAndroid::destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept {
|
||||
delete pExternalTextureManager;
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
ExternalTextureManagerAndroid::ExternalTextureManagerAndroid() noexcept
|
||||
: mVm(VirtualMachineEnv::get()) {
|
||||
}
|
||||
|
||||
// not quite sure on which thread this is going to be called
|
||||
ExternalTextureManagerAndroid::~ExternalTextureManagerAndroid() noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
} else {
|
||||
if (mGraphicBufferClass) {
|
||||
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
|
||||
if (env) {
|
||||
env->DeleteGlobalRef(mGraphicBufferClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
Platform::ExternalTexture* ExternalTextureManagerAndroid::createExternalTexture() noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
} else {
|
||||
// initialize java stuff on-demand
|
||||
if (!mGraphicBufferClass) {
|
||||
JNIEnv* env = mVm.getEnvironment();
|
||||
mGraphicBufferClass = env->FindClass("android/view/GraphicBuffer");
|
||||
mGraphicBuffer_nCreateGraphicBuffer = env->GetStaticMethodID(
|
||||
mGraphicBufferClass, "nCreateGraphicBuffer", "(IIII)J");
|
||||
mGraphicBuffer_nDestroyGraphicBuffer = env->GetStaticMethodID(
|
||||
mGraphicBufferClass, "nDestroyGraphicBuffer", "(J)V");
|
||||
|
||||
mGraphicBufferClass = static_cast<jclass>(env->NewGlobalRef(mGraphicBufferClass));
|
||||
}
|
||||
}
|
||||
EGLExternalTexture* ets = new EGLExternalTexture;
|
||||
return ets;
|
||||
}
|
||||
|
||||
// called on app thread
|
||||
void ExternalTextureManagerAndroid::reallocate(
|
||||
Platform::ExternalTexture* ets, uint32_t w, uint32_t h,
|
||||
TextureFormat format, uint64_t usage) noexcept {
|
||||
destroyStorage(ets);
|
||||
alloc(ets, w, h, format, usage);
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
void ExternalTextureManagerAndroid::destroy(Platform::ExternalTexture* ets) noexcept {
|
||||
destroyStorage(ets);
|
||||
delete static_cast<EGLExternalTexture*>(ets);
|
||||
}
|
||||
|
||||
// called on app thread
|
||||
void ExternalTextureManagerAndroid::alloc(
|
||||
Platform::ExternalTexture* t,
|
||||
uint32_t w, uint32_t h, TextureFormat format, uint64_t usage) noexcept {
|
||||
|
||||
EGLExternalTexture* ets = static_cast<EGLExternalTexture*>(t);
|
||||
|
||||
AHardwareBuffer_Desc desc = { w, h, 1, 0, usage, 0, 0, 0 };
|
||||
switch (format) {
|
||||
case TextureFormat::RGB8:
|
||||
// don't use R8G8B8 here because some drivers produce garbled images
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
|
||||
break;
|
||||
case TextureFormat::RGBA8:
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
|
||||
break;
|
||||
case TextureFormat::RGB565:
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
|
||||
break;
|
||||
case TextureFormat::RGB10_A2:
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
|
||||
break;
|
||||
default:
|
||||
slog.e << "Unsupported format " << (int)format << ", use RGBA8 or RGB8 only."
|
||||
<< io::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (__builtin_available(android 26, *)) {
|
||||
// allocate new storage...
|
||||
AHardwareBuffer* buffer = nullptr;
|
||||
if (AHardwareBuffer_allocate(&desc, &buffer) < 0) {
|
||||
slog.e << "AHardwareBuffer_allocate() failed" << io::endl;
|
||||
return;
|
||||
}
|
||||
ets->hardwareBuffer = buffer;
|
||||
} else {
|
||||
// note: This is called on the application thread (not the GL thread)
|
||||
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
|
||||
if (!env) {
|
||||
return; // this should not happen
|
||||
}
|
||||
|
||||
jlong graphicBufferWrapperJni = env->CallStaticLongMethod(mGraphicBufferClass,
|
||||
mGraphicBuffer_nCreateGraphicBuffer, w, h, desc.format, usage);
|
||||
VirtualMachineEnv::handleException(env);
|
||||
|
||||
if (graphicBufferWrapperJni) {
|
||||
GraphicBuffer* const gb = ((GraphicBufferWrapper*)graphicBufferWrapperJni)->graphicBuffer;
|
||||
ets->graphicBufferWrapper = (GraphicBufferWrapper*)graphicBufferWrapperJni;
|
||||
ets->clientBuffer = static_cast<ANativeWindowBuffer*>(gb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called on gl thread
|
||||
void ExternalTextureManagerAndroid::destroyStorage(Platform::ExternalTexture* t) noexcept {
|
||||
EGLExternalTexture* ets = static_cast<EGLExternalTexture*>(t);
|
||||
if (__builtin_available(android 26, *)) {
|
||||
// destroy the current storage if any
|
||||
if (ets->hardwareBuffer) {
|
||||
AHardwareBuffer_release(ets->hardwareBuffer);
|
||||
ets->hardwareBuffer = nullptr;
|
||||
}
|
||||
} else {
|
||||
if (ets->graphicBufferWrapper) {
|
||||
JNIEnv* env = VirtualMachineEnv::get().getEnvironment();
|
||||
env->CallStaticVoidMethod(mGraphicBufferClass,
|
||||
mGraphicBuffer_nDestroyGraphicBuffer, (jlong)ets->graphicBufferWrapper);
|
||||
VirtualMachineEnv::handleException(env);
|
||||
ets->graphicBufferWrapper = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
|
||||
#define TNT_FILAMENT_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
|
||||
|
||||
#include "private/backend/VirtualMachineEnv.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/*
|
||||
* ExternalTextureManagerAndroid::ExternalTexture is basically a wrapper for AHardwareBuffer.
|
||||
*
|
||||
* This class doesn't rely on GL or EGL, and could be used for other Android platform if needed
|
||||
* (e.g. Vulkan).
|
||||
*
|
||||
* ExternalTextureManagerAndroid handle allocation/destruction using either Java or the NDK,
|
||||
* whichever is available.
|
||||
*/
|
||||
class ExternalTextureManagerAndroid {
|
||||
public:
|
||||
|
||||
struct ExternalTexture : public Platform::ExternalTexture {
|
||||
void* clientBuffer = nullptr;
|
||||
AHardwareBuffer* hardwareBuffer = nullptr;
|
||||
};
|
||||
|
||||
// must be called on backend thread
|
||||
static ExternalTextureManagerAndroid& create() noexcept;
|
||||
|
||||
// must be called on backend thread
|
||||
static void destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept;
|
||||
|
||||
// must be called on backend thread (only because we don't synchronize
|
||||
Platform::ExternalTexture* createExternalTexture() noexcept;
|
||||
|
||||
// called on app thread
|
||||
void reallocate(
|
||||
Platform::ExternalTexture* ets, uint32_t w, uint32_t h,
|
||||
TextureFormat format, uint64_t usage) noexcept;
|
||||
|
||||
// must be called on backend thread
|
||||
void destroy(Platform::ExternalTexture* ets) noexcept;
|
||||
|
||||
private:
|
||||
ExternalTextureManagerAndroid() noexcept;
|
||||
~ExternalTextureManagerAndroid() noexcept;
|
||||
|
||||
// called on app thread
|
||||
void alloc(Platform::ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format, uint64_t usage) noexcept;
|
||||
|
||||
// called on gl thread
|
||||
void destroyStorage(Platform::ExternalTexture* ets) noexcept;
|
||||
|
||||
VirtualMachineEnv& mVm;
|
||||
|
||||
jclass mGraphicBufferClass = nullptr;
|
||||
jmethodID mGraphicBuffer_nCreateGraphicBuffer = nullptr;
|
||||
jmethodID mGraphicBuffer_nDestroyGraphicBuffer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
|
||||
@@ -359,10 +359,6 @@ void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
construct_handle<MetalSwapChain>(sch, *mContext, width, height, flags);
|
||||
}
|
||||
|
||||
void MetalDriver::createStreamFromTextureIdR(Handle<HwStream>, intptr_t externalTextureId,
|
||||
uint32_t width, uint32_t height) {
|
||||
}
|
||||
|
||||
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
@@ -431,10 +427,6 @@ Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
|
||||
return alloc_handle<MetalSwapChain>();
|
||||
}
|
||||
|
||||
Handle<HwStream> MetalDriver::createStreamFromTextureIdS() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
|
||||
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
|
||||
// before createTimerQueryR is executed.
|
||||
@@ -1053,11 +1045,6 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
}];
|
||||
}
|
||||
|
||||
void MetalDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& data) {
|
||||
|
||||
}
|
||||
|
||||
void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
|
||||
@@ -367,7 +367,7 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
|
||||
*shaderFunctions[i] = [library newFunctionWithName:@"main0"];
|
||||
}
|
||||
|
||||
// All stages of the program have compiled successfuly, this is a valid program.
|
||||
// All stages of the program have compiled successfully, this is a valid program.
|
||||
isValid = true;
|
||||
|
||||
// This calculates Metal resource binding indices. Filament's sampler bindings range from 0-31
|
||||
|
||||
@@ -170,7 +170,7 @@ private:
|
||||
};
|
||||
|
||||
// StateTracker keeps track of state changes made to a Metal command encoder.
|
||||
// Different kinds of state, like pipeline state, uniform buffer state, etc, are passed to the
|
||||
// Different kinds of state, like pipeline state, uniform buffer state, etc., are passed to the
|
||||
// current Metal command encoder and persist throughout the lifetime of the encoder (a frame).
|
||||
// StateTracker is used to prevent calling redundant state change methods.
|
||||
template<typename StateType>
|
||||
|
||||
@@ -308,11 +308,6 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& p) {
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::blit(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
|
||||
@@ -42,9 +42,11 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define CHECK_GL_ERROR(out)
|
||||
# define CHECK_GL_ERROR_NON_FATAL(out)
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target)
|
||||
#else
|
||||
# define CHECK_GL_ERROR(out) { GLUtils::assertGLError(out, __func__, __LINE__); }
|
||||
# define CHECK_GL_ERROR_NON_FATAL(out) { GLUtils::checkGLError(out, __func__, __LINE__); }
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "OpenGLBlitter.h"
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
static const char s_vertexES[] = R"SHADER(#version 300 es
|
||||
in vec4 pos;
|
||||
void main() {
|
||||
gl_Position = pos;
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
static const char s_vertexGL[] = R"SHADER(#version 410 core
|
||||
in vec4 pos;
|
||||
void main() {
|
||||
gl_Position = pos;
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
static const char s_fragmentES[] = R"SHADER(#version 300 es
|
||||
#extension GL_OES_EGL_image_external_essl3 : enable
|
||||
precision mediump float;
|
||||
uniform samplerExternalOES sampler;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texelFetch(sampler, ivec2(gl_FragCoord.xy), 0);
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
static const char s_fragmentGL[] = R"SHADER(#version 410 core
|
||||
precision mediump float;
|
||||
uniform sampler2D sampler;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texelFetch(sampler, ivec2(gl_FragCoord.xy), 0);
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
void OpenGLBlitter::init() noexcept {
|
||||
glGenSamplers(1, &mSampler);
|
||||
glSamplerParameteri(mSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(mSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
|
||||
GLint status;
|
||||
char const* vsource[2] = { s_vertexES, s_vertexGL };
|
||||
char const* fsource[2] = { s_fragmentES, s_fragmentGL };
|
||||
const size_t index = BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES ? 0 : 1;
|
||||
|
||||
mVertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(mVertexShader, 1, vsource + index, nullptr);
|
||||
glCompileShader(mVertexShader);
|
||||
glGetShaderiv(mVertexShader, GL_COMPILE_STATUS, &status);
|
||||
assert_invariant(status == GL_TRUE);
|
||||
|
||||
mFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(mFragmentShader, 1, fsource + index, nullptr);
|
||||
glCompileShader(mFragmentShader);
|
||||
glGetShaderiv(mFragmentShader, GL_COMPILE_STATUS, &status);
|
||||
assert_invariant(status == GL_TRUE);
|
||||
|
||||
mProgram = glCreateProgram();
|
||||
glAttachShader(mProgram, mVertexShader);
|
||||
glAttachShader(mProgram, mFragmentShader);
|
||||
glLinkProgram(mProgram);
|
||||
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
|
||||
assert_invariant(status == GL_TRUE);
|
||||
|
||||
glUseProgram(mProgram);
|
||||
GLint loc = glGetUniformLocation(mProgram, "sampler");
|
||||
GLuint tmu = 0;
|
||||
glUniform1i(loc, tmu);
|
||||
|
||||
glGenFramebuffers(1, &mFBO);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLBlitter::terminate() noexcept {
|
||||
glDeleteSamplers(1, &mSampler);
|
||||
glDetachShader(mProgram, mVertexShader);
|
||||
glDetachShader(mProgram, mFragmentShader);
|
||||
glDeleteShader(mVertexShader);
|
||||
glDeleteShader(mFragmentShader);
|
||||
glDeleteProgram(mProgram);
|
||||
glDeleteFramebuffers(1, &mFBO);
|
||||
}
|
||||
|
||||
void OpenGLBlitter::blit(GLuint srcTextureExternal, GLuint dstTexture2d, GLuint w, GLuint h) noexcept {
|
||||
const float2 vtx[3] = {{ -1.0f, 3.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ 3.0f, -1.0f }};
|
||||
|
||||
// we're using tmu 0 as the source texture
|
||||
GLuint tmu = 0;
|
||||
|
||||
// source texture
|
||||
glBindSampler(tmu, mSampler);
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, srcTextureExternal);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
// destination texture
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture2d, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
|
||||
// geometry
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vtx);
|
||||
|
||||
// blit...
|
||||
glViewport(0, 0, w, h);
|
||||
glUseProgram(mProgram);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLBlitter::State::save() noexcept {
|
||||
mHasState = true;
|
||||
// TODO: technically we should also save glVertexAttribPointer
|
||||
GLuint tmu = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &framebuffer);
|
||||
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &array);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &program);
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
glGetIntegerv(GL_COLOR_WRITEMASK, writeMask);
|
||||
scissorTest = glIsEnabled(GL_SCISSOR_TEST);
|
||||
stencilTest = glIsEnabled(GL_STENCIL_TEST);
|
||||
cullFace = glIsEnabled(GL_CULL_FACE);
|
||||
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttrib);
|
||||
|
||||
// save the current active texture first
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
|
||||
|
||||
// we're using tmu 0 as the source texture
|
||||
glActiveTexture(GL_TEXTURE0 + tmu);
|
||||
|
||||
// save what depends on glActiveTexture
|
||||
glGetIntegerv(GL_SAMPLER_BINDING, &sampler);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
|
||||
|
||||
/*
|
||||
* Set the state
|
||||
*/
|
||||
|
||||
// we're using tmu 0 as the source texture
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLBlitter::State::restore() noexcept {
|
||||
GLuint tmu = 0;
|
||||
glColorMask(
|
||||
(GLboolean)writeMask[0],
|
||||
(GLboolean)writeMask[1],
|
||||
(GLboolean)writeMask[2],
|
||||
(GLboolean)writeMask[3]);
|
||||
if (cullFace) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
if (stencilTest) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
if (scissorTest) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
glUseProgram(static_cast<GLuint>(program));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, static_cast<GLuint>(array));
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(framebuffer));
|
||||
if (!vertexAttrib) {
|
||||
glDisableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(texture));
|
||||
glBindSampler(tmu, static_cast<GLuint>(sampler));
|
||||
glActiveTexture(static_cast<GLenum>(activeTexture));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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_OPENGL_OPENGLBLITTER_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLBLITTER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include "gl_headers.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLContext;
|
||||
|
||||
class OpenGLBlitter {
|
||||
public:
|
||||
explicit OpenGLBlitter(OpenGLContext& context) noexcept : mContext(context) {}
|
||||
|
||||
void init() noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
void blit(GLuint srcTextureExternal, GLuint dstTexture2d, GLuint w, GLuint h) noexcept;
|
||||
|
||||
class State {
|
||||
bool mHasState = false;
|
||||
GLint activeTexture, sampler, texture, framebuffer, array, vertexAttrib, program;
|
||||
GLboolean stencilTest, scissorTest, cullFace;
|
||||
GLint viewport[4], writeMask[4];
|
||||
void save() noexcept;
|
||||
void restore() noexcept;
|
||||
public:
|
||||
void setup() noexcept {
|
||||
if (UTILS_UNLIKELY(!mHasState)) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
~State() noexcept {
|
||||
if (UTILS_UNLIKELY(mHasState)) {
|
||||
restore();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
UTILS_UNUSED OpenGLContext& mContext;
|
||||
GLuint mSampler{};
|
||||
GLuint mVertexShader{};
|
||||
GLuint mFragmentShader{};
|
||||
GLuint mProgram{};
|
||||
GLuint mFBO{};
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGLBLITTER_H
|
||||
@@ -81,75 +81,87 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
* Figure out which driver bugs we need to workaround
|
||||
*/
|
||||
|
||||
if (strstr(state.renderer, "Adreno")) {
|
||||
// Qualcomm GPU
|
||||
bugs.invalidate_end_only_if_invalidate_start = true;
|
||||
const bool isAngle = strstr(state.renderer, "ANGLE");
|
||||
if (!isAngle) {
|
||||
if (strstr(state.renderer, "Adreno")) {
|
||||
// Qualcomm GPU
|
||||
bugs.invalidate_end_only_if_invalidate_start = true;
|
||||
|
||||
// On Adreno (As of 3/20) timer query seem to return the CPU time, not the GPU time.
|
||||
bugs.dont_use_timer_query = true;
|
||||
|
||||
// Blits to texture arrays are failing
|
||||
// This bug continues to reproduce, though at times we've seen it appear to "go away". The
|
||||
// standalone sample app that was written to show this problem still reproduces.
|
||||
// The working hypthesis is that some other state affects this behavior.
|
||||
bugs.disable_sidecar_blit_into_texture_array = true;
|
||||
|
||||
// early exit condition is flattened in EASU code
|
||||
bugs.split_easu = true;
|
||||
|
||||
int maj, min, driverMajor, driverMinor;
|
||||
int c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
|
||||
&maj, &min, &driverMajor, &driverMinor);
|
||||
if (c == 4) {
|
||||
// workarounds based on version here.
|
||||
// notes:
|
||||
// bugs.invalidate_end_only_if_invalidate_start
|
||||
// - appeared at least in
|
||||
// "OpenGL ES 3.2 V@0490.0 (GIT@85da404, I46ff5fc46f, 1606794520) (Date:11/30/20)"
|
||||
// - wasn't present in
|
||||
// "OpenGL ES 3.2 V@0490.0 (GIT@0905e9f, Ia11ce2d146, 1599072951) (Date:09/02/20)"
|
||||
// - has been confirmed fixed in V@570.1 by Qualcomm
|
||||
if (driverMajor < 490 || driverMajor > 570 ||
|
||||
(driverMajor == 570 && driverMinor >= 1)) {
|
||||
bugs.invalidate_end_only_if_invalidate_start = false;
|
||||
}
|
||||
}
|
||||
|
||||
// qualcomm seems to have no problem with this (which is good for us)
|
||||
bugs.allow_read_only_ancillary_feedback_loop = true;
|
||||
} else if (strstr(state.renderer, "Mali")) {
|
||||
// ARM GPU
|
||||
bugs.vao_doesnt_store_element_array_buffer_binding = true;
|
||||
if (strstr(state.renderer, "Mali-T")) {
|
||||
bugs.disable_glFlush = true;
|
||||
bugs.disable_shared_context_draws = true;
|
||||
bugs.texture_external_needs_rebind = true;
|
||||
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
|
||||
// On Adreno (As of 3/20) timer query seem to return the CPU time, not the GPU time.
|
||||
bugs.dont_use_timer_query = true;
|
||||
|
||||
// Blits to texture arrays are failing
|
||||
// This bug continues to reproduce, though at times we've seen it appear to "go away".
|
||||
// The standalone sample app that was written to show this problem still reproduces.
|
||||
// The working hypthesis is that some other state affects this behavior.
|
||||
bugs.disable_sidecar_blit_into_texture_array = true;
|
||||
|
||||
// early exit condition is flattened in EASU code
|
||||
bugs.split_easu = true;
|
||||
|
||||
int maj, min, driverMajor, driverMinor;
|
||||
int c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
|
||||
&maj, &min, &driverMajor, &driverMinor);
|
||||
if (c == 4) {
|
||||
// workarounds based on version here.
|
||||
// notes:
|
||||
// bugs.invalidate_end_only_if_invalidate_start
|
||||
// - appeared at least in
|
||||
// "OpenGL ES 3.2 V@0490.0 (GIT@85da404, I46ff5fc46f, 1606794520) (Date:11/30/20)"
|
||||
// - wasn't present in
|
||||
// "OpenGL ES 3.2 V@0490.0 (GIT@0905e9f, Ia11ce2d146, 1599072951) (Date:09/02/20)"
|
||||
// - has been confirmed fixed in V@570.1 by Qualcomm
|
||||
if (driverMajor < 490 || driverMajor > 570 ||
|
||||
(driverMajor == 570 && driverMinor >= 1)) {
|
||||
bugs.invalidate_end_only_if_invalidate_start = false;
|
||||
}
|
||||
}
|
||||
|
||||
// qualcomm seems to have no problem with this (which is good for us)
|
||||
bugs.allow_read_only_ancillary_feedback_loop = true;
|
||||
} else if (strstr(state.renderer, "Mali")) {
|
||||
// ARM GPU
|
||||
bugs.vao_doesnt_store_element_array_buffer_binding = true;
|
||||
if (strstr(state.renderer, "Mali-T")) {
|
||||
bugs.disable_glFlush = true;
|
||||
bugs.disable_shared_context_draws = true;
|
||||
bugs.texture_external_needs_rebind = true;
|
||||
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
|
||||
bugs.dont_use_timer_query = true;
|
||||
}
|
||||
if (strstr(state.renderer, "Mali-G")) {
|
||||
// note: We have verified that timer queries work well at least on some Mali-G.
|
||||
}
|
||||
// Mali seems to have no problem with this (which is good for us)
|
||||
bugs.allow_read_only_ancillary_feedback_loop = true;
|
||||
} else if (strstr(state.renderer, "Intel")) {
|
||||
// Intel GPU
|
||||
bugs.vao_doesnt_store_element_array_buffer_binding = true;
|
||||
} else if (strstr(state.renderer, "PowerVR")) {
|
||||
// PowerVR GPU
|
||||
} else if (strstr(state.renderer, "Apple")) {
|
||||
// Apple GPU
|
||||
} else if (strstr(state.renderer, "Tegra") ||
|
||||
strstr(state.renderer, "GeForce") ||
|
||||
strstr(state.renderer, "NV")) {
|
||||
// NVIDIA GPU
|
||||
} else if (strstr(state.renderer, "Vivante")) {
|
||||
// Vivante GPU
|
||||
} else if (strstr(state.renderer, "AMD") ||
|
||||
strstr(state.renderer, "ATI")) {
|
||||
// AMD/ATI GPU
|
||||
} else if (strstr(state.renderer, "Mozilla")) {
|
||||
bugs.disable_invalidate_framebuffer = true;
|
||||
}
|
||||
if (strstr(state.renderer, "Mali-G")) {
|
||||
// note: We have verified that timer queries work well at least on some Mali-G.
|
||||
} else {
|
||||
// When running under ANGLE, it's a different set of workaround that we need.
|
||||
if (strstr(state.renderer, "Adreno")) {
|
||||
// Qualcomm GPU
|
||||
// early exit condition is flattened in EASU code
|
||||
// (that should be regardless of ANGLE, but we should double check)
|
||||
bugs.split_easu = true;
|
||||
}
|
||||
// Mali seems to have no problem with this (which is good for us)
|
||||
bugs.allow_read_only_ancillary_feedback_loop = true;
|
||||
} else if (strstr(state.renderer, "Intel")) {
|
||||
// Intel GPU
|
||||
bugs.vao_doesnt_store_element_array_buffer_binding = true;
|
||||
} else if (strstr(state.renderer, "PowerVR")) {
|
||||
// PowerVR GPU
|
||||
} else if (strstr(state.renderer, "Apple")) {
|
||||
// Apple GPU
|
||||
} else if (strstr(state.renderer, "Tegra") ||
|
||||
strstr(state.renderer, "GeForce") ||
|
||||
strstr(state.renderer, "NV")) {
|
||||
// NVIDIA GPU
|
||||
} else if (strstr(state.renderer, "Vivante")) {
|
||||
// Vivante GPU
|
||||
} else if (strstr(state.renderer, "AMD") ||
|
||||
strstr(state.renderer, "ATI")) {
|
||||
// AMD/ATI GPU
|
||||
} else if (strstr(state.renderer, "Mozilla")) {
|
||||
bugs.disable_invalidate_framebuffer = true;
|
||||
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
|
||||
}
|
||||
|
||||
slog.v << "Active workarounds: " << '\n';
|
||||
@@ -294,7 +306,9 @@ void OpenGLContext::initExtensionsGLES() noexcept {
|
||||
ext.EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
|
||||
ext.EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
|
||||
ext.EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
ext.EXT_texture_compression_etc2 = true;
|
||||
#endif
|
||||
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
|
||||
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
|
||||
ext.KHR_debug = exts.has("GL_KHR_debug"sv);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "private/backend/OpenGLPlatform.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "OpenGLBlitter.h"
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "OpenGLProgram.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
@@ -168,13 +167,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform) noexcept
|
||||
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
|
||||
#endif
|
||||
|
||||
// Initialize the blitter only if we have OES_EGL_image_external_essl3
|
||||
if (mContext.ext.OES_EGL_image_external_essl3) {
|
||||
mOpenGLBlitter = new OpenGLBlitter(mContext);
|
||||
mOpenGLBlitter->init();
|
||||
mContext.resetProgram();
|
||||
}
|
||||
|
||||
if (mContext.ext.EXT_disjoint_timer_query ||
|
||||
BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
|
||||
// timer queries are available
|
||||
@@ -197,7 +189,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform) noexcept
|
||||
}
|
||||
|
||||
OpenGLDriver::~OpenGLDriver() noexcept {
|
||||
delete mOpenGLBlitter;
|
||||
}
|
||||
|
||||
Dispatcher OpenGLDriver::getDispatcher() const noexcept {
|
||||
@@ -223,9 +214,6 @@ void OpenGLDriver::terminate() {
|
||||
glDeleteSamplers(1, &item.second);
|
||||
}
|
||||
mSamplerMap.clear();
|
||||
if (mOpenGLBlitter) {
|
||||
mOpenGLBlitter->terminate();
|
||||
}
|
||||
|
||||
delete mTimerQueryImpl;
|
||||
|
||||
@@ -381,10 +369,6 @@ Handle<HwSwapChain> OpenGLDriver::createSwapChainHeadlessS() noexcept {
|
||||
return initHandle<HwSwapChain>();
|
||||
}
|
||||
|
||||
Handle<HwStream> OpenGLDriver::createStreamFromTextureIdS() noexcept {
|
||||
return initHandle<GLStream>();
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
|
||||
return initHandle<GLTimerQuery>();
|
||||
}
|
||||
@@ -1137,24 +1121,6 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createStreamFromTextureIdR(Handle<HwStream> sh,
|
||||
intptr_t externalTextureId, uint32_t width, uint32_t height) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
GLStream* s = handle_cast<GLStream*>(sh);
|
||||
// It would be better if we could query the externalTextureId size, unfortunately
|
||||
// this is not supported in GL for GL_TEXTURE_EXTERNAL_OES targets
|
||||
s->width = width;
|
||||
s->height = height;
|
||||
s->gl.externalTextureId = static_cast<GLuint>(externalTextureId);
|
||||
s->streamType = StreamType::TEXTURE_ID;
|
||||
glGenTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.read);
|
||||
glGenTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.write);
|
||||
for (auto& info : s->user_thread.infos) {
|
||||
info.ets = mPlatform.createExternalTextureStorage();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
@@ -1301,15 +1267,6 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
|
||||
}
|
||||
if (s->streamType == StreamType::NATIVE) {
|
||||
mPlatform.destroyStream(s->stream);
|
||||
} else if (s->streamType == StreamType::TEXTURE_ID) {
|
||||
glDeleteTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.read);
|
||||
glDeleteTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.write);
|
||||
if (s->gl.fbo) {
|
||||
glDeleteFramebuffers(1, &s->gl.fbo);
|
||||
}
|
||||
for (auto const& info : s->user_thread.infos) {
|
||||
mPlatform.destroyExternalTextureStorage(info.ets);
|
||||
}
|
||||
}
|
||||
destruct(sh, s);
|
||||
}
|
||||
@@ -1368,7 +1325,6 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer,
|
||||
|
||||
void OpenGLDriver::updateStreams(DriverApi* driver) {
|
||||
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
|
||||
OpenGLBlitter::State state;
|
||||
for (GLTexture* t : mExternalStreams) {
|
||||
assert_invariant(t);
|
||||
|
||||
@@ -1379,11 +1335,6 @@ void OpenGLDriver::updateStreams(DriverApi* driver) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s->streamType == StreamType::TEXTURE_ID) {
|
||||
state.setup();
|
||||
updateStreamTexId(t, driver);
|
||||
}
|
||||
|
||||
if (s->streamType == StreamType::ACQUIRED) {
|
||||
updateStreamAcquired(t, driver);
|
||||
}
|
||||
@@ -2080,20 +2031,12 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
|
||||
|
||||
UTILS_NOINLINE
|
||||
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
|
||||
auto& gl = mContext;
|
||||
mExternalStreams.push_back(t);
|
||||
|
||||
switch (hwStream->streamType) {
|
||||
case StreamType::NATIVE:
|
||||
mPlatform.attach(hwStream->stream, t->gl.id);
|
||||
break;
|
||||
case StreamType::TEXTURE_ID:
|
||||
assert_invariant(t->target == SamplerType::SAMPLER_EXTERNAL);
|
||||
// The texture doesn't need a texture name anymore, get rid of it
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
glDeleteTextures(1, &t->gl.id);
|
||||
t->gl.id = hwStream->user_thread.read[hwStream->user_thread.cur];
|
||||
break;
|
||||
case StreamType::ACQUIRED:
|
||||
break;
|
||||
}
|
||||
@@ -2115,8 +2058,6 @@ void OpenGLDriver::detachStream(GLTexture* t) noexcept {
|
||||
mPlatform.detach(t->hwStream->stream);
|
||||
// ^ this deletes the texture id
|
||||
break;
|
||||
case StreamType::TEXTURE_ID:
|
||||
break;
|
||||
case StreamType::ACQUIRED:
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
glDeleteTextures(1, &t->gl.id);
|
||||
@@ -2141,7 +2082,6 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
|
||||
mPlatform.detach(texture->hwStream->stream);
|
||||
// ^ this deletes the texture id
|
||||
break;
|
||||
case StreamType::TEXTURE_ID:
|
||||
case StreamType::ACQUIRED:
|
||||
break;
|
||||
}
|
||||
@@ -2151,10 +2091,6 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
|
||||
glGenTextures(1, &texture->gl.id);
|
||||
mPlatform.attach(newStream->stream, texture->gl.id);
|
||||
break;
|
||||
case StreamType::TEXTURE_ID:
|
||||
assert_invariant(texture->target == SamplerType::SAMPLER_EXTERNAL);
|
||||
texture->gl.id = newStream->user_thread.read[newStream->user_thread.cur];
|
||||
break;
|
||||
case StreamType::ACQUIRED:
|
||||
// Just re-use the old texture id.
|
||||
break;
|
||||
@@ -2524,179 +2460,6 @@ void OpenGLDriver::updateStreamAcquired(GLTexture* gltexture, DriverApi* driver)
|
||||
});
|
||||
}
|
||||
|
||||
#define DEBUG_NO_EXTERNAL_STREAM_COPY false
|
||||
|
||||
void OpenGLDriver::updateStreamTexId(GLTexture* t, DriverApi* driver) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
auto& gl = mContext;
|
||||
|
||||
GLStream* s = static_cast<GLStream*>(t->hwStream);
|
||||
assert_invariant(s);
|
||||
assert_invariant(s->streamType == StreamType::TEXTURE_ID);
|
||||
|
||||
// round-robin to the next texture name
|
||||
if (UTILS_UNLIKELY(DEBUG_NO_EXTERNAL_STREAM_COPY ||
|
||||
gl.bugs.disable_shared_context_draws || !mOpenGLBlitter)) {
|
||||
driver->queueCommand([this, t, s]() {
|
||||
// the stream may have been destroyed since we enqueued the command
|
||||
// also make sure that this texture is still associated with the same stream
|
||||
auto& streams = mExternalStreams;
|
||||
if (UTILS_LIKELY(std::find(streams.begin(), streams.end(), t) != streams.end()) &&
|
||||
(t->hwStream == s)) {
|
||||
t->gl.id = s->gl.externalTextureId;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
s->user_thread.cur = uint8_t(
|
||||
(s->user_thread.cur + 1) % GLStream::ROUND_ROBIN_TEXTURE_COUNT);
|
||||
GLuint writeTexture = s->user_thread.write[s->user_thread.cur];
|
||||
GLuint readTexture = s->user_thread.read[s->user_thread.cur];
|
||||
|
||||
// Make sure we're using the proper size
|
||||
GLStream::Info& info = s->user_thread.infos[s->user_thread.cur];
|
||||
if (UTILS_UNLIKELY(info.width != s->width || info.height != s->height)) {
|
||||
|
||||
// nothing guarantees that this buffer is free (i.e. has been consumed by the
|
||||
// GL thread), so we could potentially cause a glitch by reallocating the
|
||||
// texture here. This should be very rare though.
|
||||
// This could be fixed by always using a new temporary texture here, and
|
||||
// replacing it in the queueCommand() below. imho, not worth it.
|
||||
|
||||
info.width = s->width;
|
||||
info.height = s->height;
|
||||
|
||||
Platform::ExternalTexture* ets = s->user_thread.infos[s->user_thread.cur].ets;
|
||||
mPlatform.reallocateExternalStorage(ets, info.width, info.height, TextureFormat::RGB8);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, writeTexture);
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, readTexture);
|
||||
#ifdef GL_OES_EGL_image
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)ets->image);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)ets->image);
|
||||
#endif
|
||||
}
|
||||
|
||||
// copy the texture...
|
||||
#ifndef NDEBUG
|
||||
if (t->gl.fence) {
|
||||
// we're about to overwrite a buffer that hasn't been consumed
|
||||
slog.d << "OpenGLDriver::updateStream(): about to overwrite buffer " <<
|
||||
int(s->user_thread.cur) << " of Texture at " << t << " of Stream at " << s
|
||||
<< io::endl;
|
||||
}
|
||||
#endif
|
||||
mOpenGLBlitter->blit(s->gl.externalTextureId, writeTexture, s->width, s->height);
|
||||
|
||||
// We need a fence to guarantee that this copy has happened when we need the texture
|
||||
// in OpenGLProgram::updateSamplers(), i.e. when we bind textures just before use.
|
||||
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
// Per https://www.khronos.org/opengl/wiki/Sync_Object, flush to make sure that the
|
||||
// sync object is in the driver's command queue.
|
||||
glFlush();
|
||||
|
||||
// Update the stream timestamp. It's not clear to me that this is correct; which
|
||||
// timestamp do we really want? Here we use "now" because we have nothing else we
|
||||
// can use.
|
||||
s->user_thread.timestamp = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
|
||||
driver->queueCommand([this, t, s, fence, readTexture, writeTexture]() {
|
||||
// the stream may have been destroyed since we enqueued the command
|
||||
// also make sure that this texture is still associated with the same stream
|
||||
auto& streams = mExternalStreams;
|
||||
if (UTILS_LIKELY(std::find(streams.begin(), streams.end(), t) != streams.end()) &&
|
||||
(t->hwStream == s)) {
|
||||
if (UTILS_UNLIKELY(t->gl.fence)) {
|
||||
// if the texture still has a fence set, destroy it now, so it's not leaked.
|
||||
glDeleteSync(t->gl.fence);
|
||||
}
|
||||
t->gl.id = readTexture;
|
||||
t->gl.fence = fence;
|
||||
s->gl.externalTexture2DId = writeTexture;
|
||||
} else {
|
||||
glDeleteSync(fence);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::readStreamPixels(Handle<HwStream> sh,
|
||||
uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& p) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
|
||||
GLStream* s = handle_cast<GLStream*>(sh);
|
||||
|
||||
if (UTILS_UNLIKELY(s->streamType == StreamType::ACQUIRED)) {
|
||||
PANIC_LOG("readStreamPixels with ACQUIRED streams is not yet implemented.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(s->streamType == StreamType::TEXTURE_ID)) {
|
||||
GLuint tid = s->gl.externalTexture2DId;
|
||||
if (tid == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLenum glFormat = getFormat(p.format);
|
||||
GLenum glType = getType(p.type);
|
||||
|
||||
gl.pixelStore(GL_PACK_ROW_LENGTH, p.stride);
|
||||
gl.pixelStore(GL_PACK_ALIGNMENT, p.alignment);
|
||||
gl.pixelStore(GL_PACK_SKIP_PIXELS, p.left);
|
||||
gl.pixelStore(GL_PACK_SKIP_ROWS, p.top);
|
||||
|
||||
if (s->gl.fbo == 0) {
|
||||
glGenFramebuffers(1, &s->gl.fbo);
|
||||
}
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tid, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
/*
|
||||
* It looks like glReadPixels() behaves differently, or even wrongly,
|
||||
* when the FBO is backed by an EXTERNAL texture...
|
||||
*
|
||||
*
|
||||
* External texture FBO User buffer
|
||||
*
|
||||
* O---+----------------+
|
||||
* | | | .stride .alignment
|
||||
* | | | ----------------------->-->
|
||||
* | | y | O----------------------+--+ low adresses
|
||||
* | | | | | | |
|
||||
* | | w | | | .bottom | |
|
||||
* | V <---------> | | V | |
|
||||
* | +---------+ | | +---------+ | |
|
||||
* | | ^ | | ======> | | | | |
|
||||
* | x | h| | | |.left| | | |
|
||||
* +------>| v | | +---->| | | |
|
||||
* | +.........+ | | +.........+ | |
|
||||
* | | | | |
|
||||
* | | +----------------------+--+ high adresses
|
||||
* +--------------------+
|
||||
*
|
||||
* Origin is at the The image is NOT y-reversed
|
||||
* top-left corner and bottom is counted from
|
||||
* the top! "bottom" is in fact treated
|
||||
* as "top".
|
||||
*/
|
||||
|
||||
// The filament API provides yoffset as the "bottom" offset, therefore it needs to
|
||||
// be corrected to match glReadPixels()'s behavior.
|
||||
y = (s->height - height) - y;
|
||||
|
||||
// TODO: we could use a PBO to make this asynchronous
|
||||
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, p.buffer);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Setting rendering state
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -3223,7 +2986,11 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
|
||||
instanceCount);
|
||||
}
|
||||
|
||||
#ifdef FILAMENT_ENABLE_MATDBG
|
||||
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
|
||||
#else
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
#endif
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
|
||||
@@ -145,7 +145,6 @@ public:
|
||||
};
|
||||
|
||||
struct GLStream : public HwStream {
|
||||
static constexpr size_t ROUND_ROBIN_TEXTURE_COUNT = 3; // 3 maximum
|
||||
using HwStream::HwStream;
|
||||
struct Info {
|
||||
// storage for the read/write textures below
|
||||
@@ -153,29 +152,11 @@ public:
|
||||
GLuint width = 0;
|
||||
GLuint height = 0;
|
||||
};
|
||||
struct {
|
||||
// id of the texture where the external frames are streamed (i.e. the texture
|
||||
// used by SurfaceTexture on Android)
|
||||
GLuint externalTextureId = 0;
|
||||
|
||||
/*
|
||||
* This is for making a cpu copy of the camera frame
|
||||
*/
|
||||
GLuint externalTexture2DId = 0;
|
||||
GLuint fbo = 0;
|
||||
} gl; // 20 bytes
|
||||
|
||||
|
||||
/*
|
||||
* The fields below are accessed from the main application thread
|
||||
* (not the GL thread)
|
||||
*/
|
||||
struct {
|
||||
// texture id used to texture from, always used in the GL thread
|
||||
GLuint read[ROUND_ROBIN_TEXTURE_COUNT]; // 12 bytes
|
||||
// texture id to write into, always used from the user thread
|
||||
GLuint write[ROUND_ROBIN_TEXTURE_COUNT]; // 12 bytes
|
||||
Info infos[ROUND_ROBIN_TEXTURE_COUNT]; // 48 bytes
|
||||
int64_t timestamp = 0;
|
||||
uint8_t cur = 0;
|
||||
AcquiredImage acquired;
|
||||
@@ -364,7 +345,7 @@ private:
|
||||
void setViewportScissor(Viewport const& viewportScissor) noexcept;
|
||||
|
||||
// sampler buffer binding points (nullptr if not used)
|
||||
std::array<HwSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 8 pointers
|
||||
std::array<HwSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 12 pointers
|
||||
|
||||
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
|
||||
mutable std::vector<GLTexture*> mExternalStreams;
|
||||
@@ -375,8 +356,6 @@ private:
|
||||
|
||||
OpenGLPlatform& mPlatform;
|
||||
|
||||
OpenGLBlitter* mOpenGLBlitter = nullptr;
|
||||
void updateStreamTexId(GLTexture* t, DriverApi* driver) noexcept;
|
||||
void updateStreamAcquired(GLTexture* t, DriverApi* driver) noexcept;
|
||||
void updateBuffer(GLBufferObject* buffer, BufferDescriptor const& p,
|
||||
uint32_t byteOffset, uint32_t alignment = 16) noexcept;
|
||||
|
||||
@@ -264,7 +264,7 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
|
||||
mValid = OpenGLProgram::checkProgramStatus(name.c_str_safe(),
|
||||
gl.program, gl.shaders, initializationData->shaderSourceCode);
|
||||
|
||||
if (mValid) {
|
||||
if (UTILS_LIKELY(mValid)) {
|
||||
initializeProgramState(context, gl.program,
|
||||
initializationData->uniformBlockInfo,
|
||||
initializationData->samplerGroupInfo);
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
|
||||
// turns out the former might be relatively cheap to check, the later requires
|
||||
// a bit less. Compared to what updateSamplers() actually does, which is
|
||||
// pretty little, I'm not sure we'll get ahead.
|
||||
// pretty little, I'm not sure if we'll get ahead.
|
||||
|
||||
updateSamplers(gld);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ private:
|
||||
union {
|
||||
// when mInitialized == true:
|
||||
// information about each USED sampler buffer per binding (no gaps)
|
||||
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 8 bytes
|
||||
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 12 bytes
|
||||
// when mInitialized == false:
|
||||
// lazy initialization data pointer
|
||||
LazyInitializationData* mLazyInitializationData;
|
||||
|
||||
@@ -55,11 +55,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final { }
|
||||
|
||||
int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
bool pumpEvents() noexcept override;
|
||||
|
||||
@@ -56,11 +56,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final { }
|
||||
|
||||
int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
bool setExternalImage(void* externalImage, void* texture) noexcept final;
|
||||
|
||||
@@ -51,11 +51,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final override {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
|
||||
|
||||
int getOSVersion() const noexcept final override { return 0; }
|
||||
};
|
||||
|
||||
|
||||
@@ -62,11 +62,6 @@ public:
|
||||
void detach(Stream* stream) noexcept override {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept override { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept override {}
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept override {}
|
||||
|
||||
protected:
|
||||
static void logEglError(const char* name) noexcept;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "opengl/OpenGLDriver.h"
|
||||
#include "opengl/OpenGLContext.h"
|
||||
|
||||
#include "android/ExternalTextureManagerAndroid.h"
|
||||
#include "ExternalStreamManagerAndroid.h"
|
||||
#include "private/backend/VirtualMachineEnv.h"
|
||||
|
||||
@@ -73,8 +72,7 @@ using EGLStream = Platform::Stream;
|
||||
|
||||
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
||||
: PlatformEGL(),
|
||||
mExternalStreamManager(ExternalStreamManagerAndroid::create()),
|
||||
mExternalTextureManager(ExternalTextureManagerAndroid::create()) {
|
||||
mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
|
||||
|
||||
char scratch[PROP_VALUE_MAX + 1];
|
||||
int length = __system_property_get("ro.build.version.release", scratch);
|
||||
@@ -85,6 +83,18 @@ PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
||||
length = __system_property_get("ro.build.version.sdk", scratch);
|
||||
mOSVersion = length >= 0 ? atoi(scratch) : 1;
|
||||
}
|
||||
|
||||
// This disables an ANGLE optimization on ARM, which turns out to be more costly for us
|
||||
// see b/229017581
|
||||
// We need to do this before we create the GL context.
|
||||
// An alternative solution is use a system property:
|
||||
// __system_property_set(
|
||||
// "debug.angle.feature_overrides_disabled",
|
||||
// "preferSubmitAtFBOBoundary");
|
||||
// but that would outlive this process, so the environment variable is better.
|
||||
// We also make sure to not update the variable if it already exists.
|
||||
// There is no harm setting this if we're not on ANGLE or ARM.
|
||||
setenv("ANGLE_FEATURE_OVERRIDES_DISABLED", "preferSubmitAtFBOBoundary", false);
|
||||
}
|
||||
|
||||
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
|
||||
@@ -92,7 +102,6 @@ PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
|
||||
|
||||
void PlatformEGLAndroid::terminate() noexcept {
|
||||
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
||||
ExternalTextureManagerAndroid::destroy(&mExternalTextureManager);
|
||||
PlatformEGL::terminate();
|
||||
}
|
||||
|
||||
@@ -155,58 +164,6 @@ void PlatformEGLAndroid::updateTexImage(Stream* stream, int64_t* timestamp) noex
|
||||
mExternalStreamManager.updateTexImage(stream, timestamp);
|
||||
}
|
||||
|
||||
Platform::ExternalTexture* PlatformEGLAndroid::createExternalTextureStorage() noexcept {
|
||||
return mExternalTextureManager.createExternalTexture();
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::reallocateExternalStorage(
|
||||
Platform::ExternalTexture* externalTexture,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept {
|
||||
if (externalTexture) {
|
||||
if ((EGLImageKHR)externalTexture->image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mEGLDisplay, (EGLImageKHR)externalTexture->image);
|
||||
externalTexture->image = (uintptr_t)EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
|
||||
mExternalTextureManager.reallocate(externalTexture, w, h, format,
|
||||
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE);
|
||||
|
||||
auto ets = (ExternalTextureManagerAndroid::ExternalTexture*)externalTexture;
|
||||
EGLClientBuffer clientBuffer;
|
||||
if (ets->hardwareBuffer) {
|
||||
clientBuffer = eglGetNativeClientBufferANDROID(ets->hardwareBuffer);
|
||||
if (UTILS_UNLIKELY(!clientBuffer)) {
|
||||
logEglError("eglGetNativeClientBufferANDROID");
|
||||
return;
|
||||
}
|
||||
} else if (ets->clientBuffer) {
|
||||
clientBuffer = (EGLClientBuffer)ets->clientBuffer;
|
||||
} else {
|
||||
// woops, reallocate failed.
|
||||
return;
|
||||
}
|
||||
|
||||
const EGLint attr[] = { EGL_NONE };
|
||||
EGLImageKHR image = eglCreateImageKHR(mEGLDisplay,
|
||||
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attr);
|
||||
if (UTILS_UNLIKELY(!image)) {
|
||||
logEglError("eglCreateImageKHR");
|
||||
}
|
||||
ets->image = (uintptr_t)image;
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::destroyExternalTextureStorage(
|
||||
Platform::ExternalTexture* externalTexture) noexcept {
|
||||
if (externalTexture) {
|
||||
mExternalTextureManager.destroy(externalTexture);
|
||||
if ((EGLImageKHR)externalTexture->image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mEGLDisplay, (EGLImageKHR)externalTexture->image);
|
||||
externalTexture->image = (uintptr_t)EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PlatformEGLAndroid::getOSVersion() const noexcept {
|
||||
return mOSVersion;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
namespace filament::backend {
|
||||
|
||||
class ExternalStreamManagerAndroid;
|
||||
class ExternalTextureManagerAndroid;
|
||||
|
||||
class PlatformEGLAndroid final : public PlatformEGL {
|
||||
public:
|
||||
@@ -44,17 +43,11 @@ public:
|
||||
void detach(Stream* stream) noexcept final;
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final;
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final;
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final;
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final;
|
||||
|
||||
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept final;
|
||||
|
||||
private:
|
||||
int mOSVersion;
|
||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||
ExternalTextureManagerAndroid& mExternalTextureManager;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -55,11 +55,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final override {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
|
||||
|
||||
int getOSVersion() const noexcept final override { return 0; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -51,11 +51,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final override {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
|
||||
|
||||
int getOSVersion() const noexcept final override { return 0; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -52,11 +52,6 @@ public:
|
||||
void detach(Stream* stream) noexcept final override {}
|
||||
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
|
||||
|
||||
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
|
||||
void reallocateExternalStorage(ExternalTexture* ets,
|
||||
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
|
||||
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
|
||||
|
||||
int getOSVersion() const noexcept final override { return 0; }
|
||||
};
|
||||
|
||||
|
||||
@@ -608,10 +608,6 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
construct<VulkanSwapChain>(sch, mContext, mStagePool, width, height);
|
||||
}
|
||||
|
||||
void VulkanDriver::createStreamFromTextureIdR(Handle<HwStream> sh, intptr_t externalTextureId,
|
||||
uint32_t width, uint32_t height) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
@@ -678,10 +674,6 @@ Handle<HwSwapChain> VulkanDriver::createSwapChainHeadlessS() noexcept {
|
||||
return allocHandle<VulkanSwapChain>();
|
||||
}
|
||||
|
||||
Handle<HwStream> VulkanDriver::createStreamFromTextureIdS() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> VulkanDriver::createTimerQueryS() noexcept {
|
||||
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
|
||||
// before createTimerQueryR is executed.
|
||||
@@ -1608,14 +1600,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
|
||||
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
|
||||
srcPixels += subResourceLayout.offset;
|
||||
|
||||
// NOTE: the reasons for this are unclear, but issuing ReadPixels on a VkImage that has been
|
||||
// extracted from a swap chain does not need a Y flip, but explicitly created VkImages do. (The
|
||||
// former can be tested with "Export Screenshots" in gltf_viewer, the latter can be tested with
|
||||
// test_ReadPixels.cpp). We've seen this behavior with both SwiftShader and MoltenVK.
|
||||
const bool flipY = !srcTarget->isSwapChain();
|
||||
|
||||
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), srcPixels,
|
||||
subResourceLayout.rowPitch, width, height, swizzle, flipY)) {
|
||||
subResourceLayout.rowPitch, width, height, swizzle, false)) {
|
||||
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
}
|
||||
|
||||
@@ -1629,11 +1615,6 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
|
||||
scheduleDestroy(std::move(pbd));
|
||||
}
|
||||
|
||||
void VulkanDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& p) {
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
|
||||
assert_invariant(mContext.currentRenderPass.renderPass == VK_NULL_HANDLE);
|
||||
|
||||
@@ -289,18 +289,17 @@ private:
|
||||
|
||||
// Represents all the Vulkan state that comprises a bound descriptor set.
|
||||
struct DescriptorKey {
|
||||
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 0
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 64
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 832
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 1024
|
||||
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 1056
|
||||
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 96 0
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 96
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 864
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 48 1056
|
||||
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 48 1104
|
||||
};
|
||||
|
||||
static_assert(offsetof(DescriptorKey, samplers) == 64);
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 832);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1024);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1056);
|
||||
static_assert(sizeof(DescriptorKey) == 1088, "DescriptorKey must not have implicit padding.");
|
||||
static_assert(offsetof(DescriptorKey, samplers) == 96);
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 864);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1056);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1104);
|
||||
static_assert(sizeof(DescriptorKey) == 1152, "DescriptorKey must not have implicit padding.");
|
||||
|
||||
using DescHashFn = utils::hash::MurmurHashFn<DescriptorKey>;
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace test {
|
||||
/**
|
||||
* A wrapper class that manages the necessary resources for a simple triangle renderable used for
|
||||
* test cases.
|
||||
*
|
||||
* The default vertices orient the triangle as such:
|
||||
* o
|
||||
* . .
|
||||
* . .
|
||||
* . .
|
||||
* . .
|
||||
* o . . . . o
|
||||
*/
|
||||
class TrianglePrimitive {
|
||||
public:
|
||||
|
||||
@@ -41,6 +41,10 @@ layout(location = 0) in vec4 mesh_position;
|
||||
uniform Params { highp vec4 color; highp vec4 scale; } params;
|
||||
void main() {
|
||||
gl_Position = vec4((mesh_position.xy + 0.5) * params.scale.xy, params.scale.z, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})";
|
||||
|
||||
static const char* const triangleFs = R"(#version 450 core
|
||||
@@ -106,7 +110,7 @@ static uint32_t toUintColor(float4 color) {
|
||||
}
|
||||
|
||||
static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWidth, int baseHeight,
|
||||
int level, int layer, float3 color) {
|
||||
int level, int layer, float3 color, bool flipY) {
|
||||
auto cb = [](void* buffer, size_t size, void* user) { free(buffer); };
|
||||
const int width = baseWidth >> level;
|
||||
const int height = baseHeight >> level;
|
||||
@@ -116,17 +120,35 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
|
||||
|
||||
const float3 foreground = color;
|
||||
const float3 background = float3(1, 1, 0);
|
||||
const float radius = 0.25f;
|
||||
|
||||
// Draw a circle on a yellow background.
|
||||
// Draw a triangle on a yellow background.
|
||||
//
|
||||
// The triangle is oriented as such:
|
||||
// low addresses
|
||||
// | .
|
||||
// | ...
|
||||
// | .....
|
||||
// v ........
|
||||
// high addresses
|
||||
//
|
||||
// If flipY is specified (e.g., for OpenGL) we flip the image:
|
||||
// high addresses
|
||||
// ^ .
|
||||
// | ...
|
||||
// | .....
|
||||
// | ........
|
||||
// low addresses
|
||||
// This is because OpenGL automatically flips image data when uploading into the texture.
|
||||
uint32_t* texels = (uint32_t*) buffer0;
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
float2 uv = { (col - width / 2.0f) / width, (row - height / 2.0f) / height };
|
||||
const float d = distance(uv, float2(0));
|
||||
const float t = d < radius ? 1.0 : 0.0;
|
||||
const float d = abs(uv.x);
|
||||
const float triangleWidth = uv.y >= -.3 && uv.y <= .3 ? (.4f / .6f * uv.y + .2f) : 0;
|
||||
const float t = d < triangleWidth ? 1.0 : 0.0;
|
||||
const float3 color = mix(foreground, background, t);
|
||||
texels[row * width + col] = toUintColor(float4(color, 1.0f));
|
||||
int rowFlipped = flipY ? (height - 1) - row : row;
|
||||
texels[rowFlipped * width + col] = toUintColor(float4(color, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +157,8 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
|
||||
}
|
||||
|
||||
static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWidth, int baseHeight,
|
||||
int level, float3 color) {
|
||||
createBitmap(dapi, texture, baseWidth, baseHeight, level, 0u, color);
|
||||
int level, float3 color, bool flipY) {
|
||||
createBitmap(dapi, texture, baseWidth, baseHeight, level, 0u, color, flipY);
|
||||
}
|
||||
|
||||
static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidth, int baseHeight,
|
||||
@@ -246,8 +268,9 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0));
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = api.createTexture(
|
||||
@@ -288,7 +311,7 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xb830a36a;
|
||||
const uint32_t expected = 0x410bdd31;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
@@ -320,8 +343,9 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0));
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = api.createTexture(
|
||||
@@ -362,7 +386,7 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xe2353ca6;
|
||||
const uint32_t expected = 0x7739bef5;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
@@ -788,7 +812,8 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = api.createTexture(
|
||||
@@ -826,7 +851,7 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xe734bc44;
|
||||
const uint32_t expected = 0x8de7d55b;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ layout(location = 0) out uvec4 indices;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
@@ -152,16 +156,6 @@ struct MaterialParams {
|
||||
float blue;
|
||||
};
|
||||
|
||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
||||
MaterialParams* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
MaterialParams* sp = (MaterialParams*) buffer;
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
@@ -115,6 +115,9 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
|
||||
}
|
||||
|
||||
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
||||
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
|
||||
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
||||
TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef IOS
|
||||
#include <imageio/BlockCompression.h>
|
||||
using namespace image;
|
||||
#endif
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
@@ -45,7 +40,7 @@ layout(location = 0) in vec4 mesh_position;
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
//gl_Position.y = 1.0f - gl_Position.y;
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y *= -1.0f;
|
||||
#endif
|
||||
}
|
||||
@@ -137,28 +132,6 @@ static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t co
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IOS
|
||||
static PixelBufferDescriptor compressedCheckerboardPixelBuffer(size_t size) {
|
||||
LinearImage uncompressed(size, size, 4);
|
||||
fillCheckerboard<float>(uncompressed.getPixelRef(), size, size, 4, 1.0f);
|
||||
|
||||
S3tcConfig config {
|
||||
.format = CompressedFormat::RGBA_S3TC_DXT1,
|
||||
.srgb = false
|
||||
};
|
||||
CompressedTexture compressed = s3tcCompress(uncompressed, config);
|
||||
|
||||
void* buffer = malloc(compressed.size);
|
||||
memcpy(buffer, compressed.data.get(), compressed.size);
|
||||
|
||||
PixelBufferDescriptor descriptor(buffer, compressed.size, CompressedPixelDataType::DXT1_RGBA,
|
||||
compressed.size, [](void* buffer, size_t size, void* user) {
|
||||
free(buffer);
|
||||
}, nullptr);
|
||||
return descriptor;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type,
|
||||
size_t size, size_t bufferPadding = 0) {
|
||||
size_t components; int bpp;
|
||||
@@ -355,11 +328,6 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
|
||||
testCases.emplace_back("RGB, FLOAT -> RGB32F (subregions, buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u, true);
|
||||
|
||||
// Test compresseed format upload.
|
||||
#ifndef IOS
|
||||
testCases.emplace_back("RGBA, DXT1_RGBA -> DXT1_RGBA", PixelDataFormat::RGBA, CompressedPixelDataType::DXT1_RGBA, TextureFormat::DXT1_RGBA);
|
||||
#endif
|
||||
|
||||
auto& api = getDriverApi();
|
||||
|
||||
api.startCapture();
|
||||
@@ -388,31 +356,21 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
t.textureFormat, 1, 512, 512, 1u, usage);
|
||||
|
||||
// Upload some pixel data.
|
||||
if (t.compressed) {
|
||||
#ifdef IOS
|
||||
assert_invariant(false);
|
||||
#else
|
||||
assert_invariant(!t.uploadSubregions);
|
||||
PixelBufferDescriptor descriptor = compressedCheckerboardPixelBuffer(512);
|
||||
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
|
||||
#endif
|
||||
if (t.uploadSubregions) {
|
||||
const auto& pf = t.pixelFormat;
|
||||
const auto& pt = t.pixelType;
|
||||
PixelBufferDescriptor subregion1 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion2 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion3 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion4 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
api.update2DImage(texture, 0, 0, 0, 256, 256, std::move(subregion1));
|
||||
api.update2DImage(texture, 0, 256, 0, 256, 256, std::move(subregion2));
|
||||
api.update2DImage(texture, 0, 0, 256, 256, 256, std::move(subregion3));
|
||||
api.update2DImage(texture, 0, 256, 256, 256, 256, std::move(subregion4));
|
||||
} else {
|
||||
if (t.uploadSubregions) {
|
||||
const auto& pf = t.pixelFormat;
|
||||
const auto& pt = t.pixelType;
|
||||
PixelBufferDescriptor subregion1 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion2 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion3 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
PixelBufferDescriptor subregion4 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
|
||||
api.update2DImage(texture, 0, 0, 0, 256, 256, std::move(subregion1));
|
||||
api.update2DImage(texture, 0, 256, 0, 256, 256, std::move(subregion2));
|
||||
api.update2DImage(texture, 0, 0, 256, 256, 256, std::move(subregion3));
|
||||
api.update2DImage(texture, 0, 256, 256, 256, 256, std::move(subregion4));
|
||||
} else {
|
||||
PixelBufferDescriptor descriptor
|
||||
= checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding);
|
||||
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
|
||||
}
|
||||
PixelBufferDescriptor descriptor
|
||||
= checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding);
|
||||
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
|
||||
}
|
||||
|
||||
SamplerGroup samplers(1);
|
||||
|
||||
@@ -33,6 +33,10 @@ layout(location = 0) out uvec4 indices;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
@@ -123,6 +127,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
// The offset and stride set on the pixel buffer.
|
||||
size_t left = 0, top = 0, alignment = 1;
|
||||
|
||||
// If true, use the default RT to render into the SwapChain as opposed to a texture.
|
||||
bool useDefaultRT = false;
|
||||
|
||||
size_t getPixelBufferStride() const {
|
||||
return bufferDimension;
|
||||
}
|
||||
@@ -219,7 +226,13 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
t7.textureFormat = TextureFormat::RG16F;
|
||||
t7.hash = 3726805703;
|
||||
|
||||
TestCase testCases[] = { t, t2, t3, t4, t5, t6, t7 };
|
||||
// Check that readPixels works when rendering into the SwapChain.
|
||||
// This requires that the test runner's native window size is 512x512.
|
||||
TestCase t8;
|
||||
t8.testName = "readPixels_swapchain";
|
||||
t8.useDefaultRT = true;
|
||||
|
||||
TestCase testCases[] = { t, t2, t3, t4, t5, t6, t7, t8 };
|
||||
|
||||
// Create programs.
|
||||
Handle<HwProgram> programFloat, programUint;
|
||||
@@ -234,11 +247,18 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
programUint = getDriverApi().createProgram(std::move(p));
|
||||
}
|
||||
|
||||
|
||||
for (const auto& t : testCases)
|
||||
{
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), 0);
|
||||
Handle<HwSwapChain> swapChain;
|
||||
if (t.useDefaultRT) {
|
||||
swapChain = createSwapChain();
|
||||
} else {
|
||||
swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), 0);
|
||||
}
|
||||
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a Texture and RenderTarget to render into.
|
||||
@@ -247,12 +267,19 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1,
|
||||
usage);
|
||||
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), t.samples, {{ texture, uint8_t(t.mipLevel) }}, {},
|
||||
{});
|
||||
Handle<HwRenderTarget> renderTarget;
|
||||
if (t.useDefaultRT) {
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
renderTarget = getDriverApi().createDefaultRenderTarget();
|
||||
} else {
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), t.samples, {{ texture, uint8_t(t.mipLevel) }}, {},
|
||||
{});
|
||||
}
|
||||
|
||||
TrianglePrimitive triangle(getDriverApi());
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ public:
|
||||
* range can be adjusted with this method.
|
||||
*
|
||||
* @param intensity Scale factor applied to the environment and irradiance such that
|
||||
* the result is in lux, or <i>lumen/m^2(default = 30000)
|
||||
* the result is in lux, or <i>lumen/m^2</i> (default = 30000)
|
||||
*/
|
||||
void setIntensity(float intensity) noexcept;
|
||||
|
||||
|
||||
@@ -444,6 +444,10 @@ public:
|
||||
*
|
||||
* It is also possible to use a Fence to wait for the read-back.
|
||||
*
|
||||
* OpenGL only: if issuing a readPixels on a RenderTarget backed by a Texture that had data
|
||||
* uploaded to it via setImage, the data returned from readPixels will be y-flipped with respect
|
||||
* to the setImage call.
|
||||
*
|
||||
* @remark
|
||||
* readPixels() is intended for debugging and testing. It will impact performance significantly.
|
||||
*
|
||||
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
*
|
||||
* Ignored if an environment is set.
|
||||
*
|
||||
* @param color
|
||||
* @param color the constant color
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
|
||||
@@ -35,10 +35,9 @@ class Engine;
|
||||
/**
|
||||
* Stream is used to attach a video stream to a Filament `Texture`.
|
||||
*
|
||||
* Note that the `Stream` class is fairly Android centric. It supports three different
|
||||
* Note that the `Stream` class is fairly Android centric. It supports two different
|
||||
* configurations:
|
||||
*
|
||||
* - TEXTURE_ID...takes an OpenGL texture ID and incurs a copy
|
||||
* - ACQUIRED.....connects to an Android AHardwareBuffer
|
||||
* - NATIVE.......connects to an Android SurfaceTexture
|
||||
*
|
||||
@@ -67,10 +66,6 @@ class Engine;
|
||||
* - Filament invokes low-level graphics commands on the \em{driver thread}.
|
||||
* - The thread that calls `beginFrame` is called the \em{main thread}.
|
||||
*
|
||||
* The TEXTURE_ID configuration achieves synchronization automatically. In this mode, Filament
|
||||
* performs a copy on the main thread during `beginFrame` by blitting the external image into
|
||||
* an internal round-robin queue of images. This copy has a run-time cost.
|
||||
*
|
||||
* For ACQUIRED streams, there is no need to perform the copy because Filament explictly acquires
|
||||
* the stream, then releases it later via a callback function. This configuration is especially
|
||||
* useful when the Vulkan backend is enabled.
|
||||
@@ -97,7 +92,7 @@ public:
|
||||
* By default, Stream objects are ACQUIRED and must have external images pushed to them via
|
||||
* <pre>Stream::setAcquiredImage</pre>.
|
||||
*
|
||||
* To create a NATIVE or TEXTURE_ID stream, call one of the <pre>stream</pre> methods
|
||||
* To create a NATIVE stream, call one of the <pre>stream</pre> methods
|
||||
* on the builder.
|
||||
*/
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
@@ -122,23 +117,6 @@ public:
|
||||
*/
|
||||
Builder& stream(void* stream) noexcept;
|
||||
|
||||
/**
|
||||
* Creates a TEXTURE_ID stream. This will sample data from the supplied
|
||||
* external texture and copy it into an internal private texture.
|
||||
*
|
||||
* @param externalTextureId An opaque texture id (typically a GLuint created with glGenTextures)
|
||||
* In a context shared with filament. In that case this texture's
|
||||
* target must be GL_TEXTURE_EXTERNAL_OES and the wrap mode must
|
||||
* be CLAMP_TO_EDGE.
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*
|
||||
* @see Texture::setExternalStream()
|
||||
* @deprecated this method existed only for ARCore which doesn't need this anymore, use Texture::import() instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& stream(intptr_t externalTextureId) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param width initial width of the incoming stream. Whether this value is used is
|
||||
@@ -173,7 +151,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether this stream is a NATIVE stream, TEXTURE_ID stream, or ACQUIRED stream.
|
||||
* Indicates whether this stream is a NATIVE stream or ACQUIRED stream.
|
||||
*/
|
||||
StreamType getStreamType() const noexcept;
|
||||
|
||||
@@ -190,7 +168,7 @@ public:
|
||||
* also where the callback is invoked. This method can only be used for streams that were
|
||||
* constructed without calling the `stream` method on the builder.
|
||||
*
|
||||
* @see Stream for more information about NATIVE, TEXTURE_ID, and ACQUIRED configurations.
|
||||
* @see Stream for more information about NATIVE and ACQUIRED configurations.
|
||||
*
|
||||
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
|
||||
* @param callback This is triggered by Filament when it wishes to release the image.
|
||||
@@ -222,63 +200,6 @@ public:
|
||||
*/
|
||||
void setDimensions(uint32_t width, uint32_t height) noexcept;
|
||||
|
||||
/**
|
||||
* Read-back the content of the last frame of a Stream since the last call to
|
||||
* Renderer.beginFrame().
|
||||
*
|
||||
* The Stream must be of type externalTextureId. This function is a no-op otherwise.
|
||||
*
|
||||
* @param xoffset Left offset of the sub-region to read back.
|
||||
* @param yoffset Bottom offset of the sub-region to read back.
|
||||
* @param width Width of the sub-region to read back.
|
||||
* @param height Height of the sub-region to read back.
|
||||
* @param buffer Client-side buffer where the read-back will be written.
|
||||
*
|
||||
* The following format are always supported:
|
||||
* - PixelBufferDescriptor::PixelDataFormat::RGBA
|
||||
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
|
||||
*
|
||||
* The following types are always supported:
|
||||
* - PixelBufferDescriptor::PixelDataType::UBYTE
|
||||
* - PixelBufferDescriptor::PixelDataType::UINT
|
||||
* - PixelBufferDescriptor::PixelDataType::INT
|
||||
* - PixelBufferDescriptor::PixelDataType::FLOAT
|
||||
*
|
||||
* Other combination of format/type may be supported. If a combination is
|
||||
* not supported, this operation may fail silently. Use a DEBUG build
|
||||
* to get some logs about the failure.
|
||||
*
|
||||
* Stream buffer User buffer (PixelBufferDescriptor&)
|
||||
* +--------------------+
|
||||
* | | .stride .alignment
|
||||
* | | ----------------------->-->
|
||||
* | | O----------------------+--+ low addresses
|
||||
* | | | | | |
|
||||
* | w | | | .top | |
|
||||
* | <---------> | | V | |
|
||||
* | +---------+ | | +---------+ | |
|
||||
* | | ^ | | ======> | | | | |
|
||||
* | x | h| | | |.left| | | |
|
||||
* +------>| v | | +---->| | | |
|
||||
* | +.........+ | | +.........+ | |
|
||||
* | ^ | | | |
|
||||
* | y | | +----------------------+--+ high addresses
|
||||
* O------------+-------+
|
||||
*
|
||||
* Typically readPixels() will be called after Renderer.beginFrame().
|
||||
*
|
||||
* After issuing this method, the callback associated with `buffer` will be invoked on the
|
||||
* main thread, indicating that the read-back has completed. Typically, this will happen
|
||||
* after multiple calls to beginFrame(), render(), endFrame().
|
||||
*
|
||||
* It is also possible to use a Fence to wait for the read-back.
|
||||
*
|
||||
* @remark
|
||||
* readPixels() is intended for debugging and testing. It will impact performance significantly.
|
||||
*/
|
||||
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the presentation time of the currently displayed frame in nanosecond.
|
||||
*
|
||||
|
||||
@@ -134,12 +134,12 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
|
||||
* constructor parameters approximate an ACES tone mapping curve
|
||||
* and the maximum input value is set to 10.0.
|
||||
*
|
||||
* @param contrast: controls the contrast of the curve, must be > 0.0, values
|
||||
* in the range 0.5..2.0 are recommended.
|
||||
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
|
||||
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
|
||||
* @param hdrMax: defines the maximum input value that will be mapped to
|
||||
* output white. Must be >= 1.0.
|
||||
* @param contrast controls the contrast of the curve, must be > 0.0, values
|
||||
* in the range 0.5..2.0 are recommended.
|
||||
* @param midGrayIn sets the input middle gray, between 0.0 and 1.0.
|
||||
* @param midGrayOut sets the output middle gray, between 0.0 and 1.0.
|
||||
* @param hdrMax defines the maximum input value that will be mapped to
|
||||
* output white. Must be >= 1.0.
|
||||
*/
|
||||
explicit GenericToneMapper(
|
||||
float contrast = 1.55f,
|
||||
|
||||
@@ -25,7 +25,7 @@ const uint16_t DFG::DFG_LUT[] = {
|
||||
#include "generated/data/dfg.inc"
|
||||
};
|
||||
|
||||
DFG::DFG(FEngine& engine) noexcept : mEngine(engine) {
|
||||
void DFG::init(FEngine& engine) noexcept {
|
||||
constexpr size_t fp16Count = DFG_LUT_SIZE * DFG_LUT_SIZE * 3;
|
||||
constexpr size_t byteCount = fp16Count * sizeof(uint16_t);
|
||||
|
||||
@@ -35,19 +35,19 @@ DFG::DFG(FEngine& engine) noexcept : mEngine(engine) {
|
||||
.width(DFG_LUT_SIZE)
|
||||
.height(DFG_LUT_SIZE)
|
||||
.format(backend::TextureFormat::RGB16F)
|
||||
.build(mEngine);
|
||||
.build(engine);
|
||||
|
||||
Texture::PixelBufferDescriptor buffer(DFG_LUT, byteCount,
|
||||
Texture::Format::RGB, Texture::Type::HALF);
|
||||
|
||||
lut->setImage(mEngine, 0, std::move(buffer));
|
||||
lut->setImage(engine, 0, std::move(buffer));
|
||||
|
||||
mLUT = upcast(lut);
|
||||
}
|
||||
|
||||
void DFG::terminate() {
|
||||
void DFG::terminate(FEngine& engine) noexcept {
|
||||
if (mLUT) {
|
||||
mEngine.destroy(mLUT);
|
||||
engine.destroy(mLUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -33,7 +33,14 @@ class FEngine;
|
||||
|
||||
class DFG {
|
||||
public:
|
||||
explicit DFG(FEngine& engine) noexcept;
|
||||
explicit DFG() noexcept = default;
|
||||
|
||||
DFG(DFG const& rhs) = delete;
|
||||
DFG(DFG&& rhs) = delete;
|
||||
DFG& operator=(DFG const& rhs) = delete;
|
||||
DFG& operator=(DFG&& rhs) = delete;
|
||||
|
||||
void init(FEngine& engine) noexcept;
|
||||
|
||||
size_t getLutSize() const noexcept {
|
||||
return DFG_LUT_SIZE;
|
||||
@@ -47,15 +54,9 @@ public:
|
||||
return mLUT->getHwHandle();
|
||||
}
|
||||
|
||||
void terminate();
|
||||
|
||||
DFG(DFG const& rhs) = delete;
|
||||
DFG(DFG&& rhs) = delete;
|
||||
DFG& operator=(DFG const& rhs) = delete;
|
||||
DFG& operator=(DFG&& rhs) = delete;
|
||||
void terminate(FEngine& engine) noexcept;
|
||||
|
||||
private:
|
||||
FEngine& mEngine;
|
||||
FTexture* mLUT = nullptr;
|
||||
|
||||
// make sure to use the right size here
|
||||
|
||||
@@ -244,7 +244,6 @@ bool Froxelizer::update() noexcept {
|
||||
mClipToFroxelX = (0.5f * viewport.width) / froxelDimension.x;
|
||||
mClipToFroxelY = (0.5f * viewport.height) / froxelDimension.y;
|
||||
|
||||
mOneOverDimension = 1.0f / float2(froxelDimension);
|
||||
uniformsNeedUpdating = true;
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -513,10 +512,10 @@ void Froxelizer::commit(backend::DriverApi& driverApi) {
|
||||
}
|
||||
|
||||
void Froxelizer::froxelizeLights(FEngine& engine,
|
||||
CameraInfo const& UTILS_RESTRICT camera,
|
||||
mat4f const& UTILS_RESTRICT viewMatrix,
|
||||
const FScene::LightSoa& UTILS_RESTRICT lightData) noexcept {
|
||||
// note: this is called asynchronously
|
||||
froxelizeLoop(engine, camera, lightData);
|
||||
froxelizeLoop(engine, viewMatrix, lightData);
|
||||
froxelizeAssignRecordsCompress();
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -544,7 +543,7 @@ void Froxelizer::froxelizeLights(FEngine& engine,
|
||||
}
|
||||
|
||||
void Froxelizer::froxelizeLoop(FEngine& engine,
|
||||
const CameraInfo& UTILS_RESTRICT camera,
|
||||
const mat4f& UTILS_RESTRICT viewMatrix,
|
||||
const FScene::LightSoa& UTILS_RESTRICT lightData) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
@@ -557,11 +556,11 @@ void Froxelizer::froxelizeLoop(FEngine& engine,
|
||||
auto const* UTILS_RESTRICT instances = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
|
||||
auto process = [ this, &froxelThreadData,
|
||||
spheres, directions, instances, &camera, &lcm ]
|
||||
spheres, directions, instances, &viewMatrix, &lcm ]
|
||||
(size_t count, size_t offset, size_t stride) {
|
||||
|
||||
const mat4f& projection = mProjection;
|
||||
const mat3f& vn = camera.view.upperLeft();
|
||||
const mat3f& vn = viewMatrix.upperLeft();
|
||||
|
||||
// We use minimum cone angle of 0.5 degrees because too small angles cause issues in the
|
||||
// sphere/cone intersection test, due to floating-point precision.
|
||||
@@ -572,7 +571,7 @@ void Froxelizer::froxelizeLoop(FEngine& engine,
|
||||
const size_t j = i + FScene::DIRECTIONAL_LIGHTS_COUNT;
|
||||
FLightManager::Instance li = instances[j];
|
||||
LightParams light = {
|
||||
.position = (camera.view * float4{ spheres[j].xyz, 1 }).xyz, // to view-space
|
||||
.position = (viewMatrix * float4{ spheres[j].xyz, 1 }).xyz, // to view-space
|
||||
.cosSqr = std::min(maxCosSquared, lcm.getCosOuterSquared(li)), // spot only
|
||||
.axis = vn * directions[j], // spot only
|
||||
.invSin = lcm.getSinInverse(li), // spot only
|
||||
|
||||
@@ -120,15 +120,13 @@ public:
|
||||
float getLightFar() const noexcept { return mZLightFar; }
|
||||
|
||||
// update Records and Froxels texture with lights data. this is thread-safe.
|
||||
void froxelizeLights(FEngine& engine, CameraInfo const& camera,
|
||||
void froxelizeLights(FEngine& engine, math::mat4f const& viewMatrix,
|
||||
const FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
void updateUniforms(PerViewUib& s) {
|
||||
s.zParams = mParamsZ;
|
||||
s.fParams = mParamsF.yz;
|
||||
s.fParamsX = mParamsF.x;
|
||||
s.oneOverFroxelDimensionX = mOneOverDimension.x;
|
||||
s.oneOverFroxelDimensionY = mOneOverDimension.y;
|
||||
s.fParams = mParamsF;
|
||||
s.froxelCountXY = math::float2{ mViewport.width, mViewport.height } / mFroxelDimension;
|
||||
}
|
||||
|
||||
// send froxel data to GPU
|
||||
@@ -194,7 +192,7 @@ private:
|
||||
bool update() noexcept;
|
||||
|
||||
void froxelizeLoop(FEngine& engine,
|
||||
const CameraInfo& camera, const FScene::LightSoa& lightData) noexcept;
|
||||
math::mat4f const& viewMatrix, const FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
void froxelizeAssignRecordsCompress() noexcept;
|
||||
|
||||
@@ -242,7 +240,6 @@ private:
|
||||
float mLinearizer = 0.0f;
|
||||
float mClipToFroxelX = 0.0f;
|
||||
float mClipToFroxelY = 0.0f;
|
||||
math::float2 mOneOverDimension = {};
|
||||
backend::BufferObjectHandle mRecordsBuffer;
|
||||
backend::Handle<backend::HwTexture> mFroxelTexture;
|
||||
|
||||
|
||||
@@ -52,10 +52,10 @@ PerViewUniforms::PerViewUniforms(FEngine& engine) noexcept
|
||||
// with a clip-space of [0, w] ==> z' = (w - z)/2
|
||||
mClipControl = driver.getClipSpaceParams();
|
||||
|
||||
if (engine.getDFG()->isValid()) {
|
||||
if (engine.getDFG().isValid()) {
|
||||
TextureSampler sampler(TextureSampler::MagFilter::LINEAR);
|
||||
mSamplers.setSampler(PerViewSib::IBL_DFG_LUT,
|
||||
engine.getDFG()->getTexture(), sampler.getSamplerParams());
|
||||
engine.getDFG().getTexture(), sampler.getSamplerParams());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
|
||||
s.clipFromWorldMatrix = clipFromWorld; // projection * view
|
||||
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
|
||||
s.cameraPosition = float3{ camera.getPosition() };
|
||||
s.worldOffset = camera.worldOffset;
|
||||
s.worldOffset = camera.getWorldOffset();
|
||||
s.cameraFar = camera.zf;
|
||||
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
|
||||
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
|
||||
@@ -105,12 +105,14 @@ void PerViewUniforms::prepareExposure(float ev100) noexcept {
|
||||
s.ev100 = ev100;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareViewport(const filament::Viewport &viewport) noexcept {
|
||||
void PerViewUniforms::prepareViewport(const filament::Viewport& viewport,
|
||||
uint32_t xoffset, uint32_t yoffset) noexcept {
|
||||
const float w = float(viewport.width);
|
||||
const float h = float(viewport.height);
|
||||
auto& s = mUniforms.edit();
|
||||
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
|
||||
s.origin = float2{ viewport.left, viewport.bottom };
|
||||
s.offset = float2{ xoffset, yoffset };
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareTime(math::float4 const& userTime) noexcept {
|
||||
@@ -127,7 +129,7 @@ void PerViewUniforms::prepareTemporalNoise(TemporalAntiAliasingOptions const& op
|
||||
s.temporalNoise = options.enabled ? temporalNoise : 0.0f;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareFog(const CameraInfo& camera, FogOptions const& options) noexcept {
|
||||
void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const& options) noexcept {
|
||||
// this can't be too high because we need density / heightFalloff to produce something
|
||||
// close to fogOptions.density in the fragment shader which use 16-bits floats.
|
||||
constexpr float epsilon = 0.001f;
|
||||
@@ -135,7 +137,7 @@ void PerViewUniforms::prepareFog(const CameraInfo& camera, FogOptions const& opt
|
||||
|
||||
// precalculate the constant part of density integral and correct for exp2() in the shader
|
||||
const float density = ((options.density / heightFalloff) *
|
||||
std::exp(-heightFalloff * (camera.getPosition().y - options.height)))
|
||||
std::exp(-heightFalloff * (cameraPosition.y - options.height)))
|
||||
* float(1.0f / F_LN2);
|
||||
|
||||
auto& s = mUniforms.edit();
|
||||
@@ -283,23 +285,24 @@ void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
|
||||
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept {
|
||||
void PerViewUniforms::prepareShadowMapping() noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
|
||||
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
|
||||
s.vsmLightBleedReduction = options.lightBleedReduction;
|
||||
}
|
||||
|
||||
s.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
|
||||
s.cascadeSplits = shadowMappingUniforms.cascadeSplits;
|
||||
s.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
|
||||
s.shadowBias = shadowMappingUniforms.shadowBias;
|
||||
s.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
|
||||
s.directionalShadows = shadowMappingUniforms.directionalShadows;
|
||||
s.cascades = shadowMappingUniforms.cascades;
|
||||
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
|
||||
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
|
||||
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
|
||||
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
|
||||
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
|
||||
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
|
||||
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
|
||||
uniforms.cascades = shadowMappingUniforms.cascades;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept {
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
|
||||
if (options.anisotropy > 0 || options.mipmapping) {
|
||||
@@ -313,9 +316,14 @@ void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
|
||||
}});
|
||||
auto& s = mUniforms.edit();
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_VSM;
|
||||
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
|
||||
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
|
||||
s.vsmLightBleedReduction = options.lightBleedReduction;
|
||||
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture) noexcept {
|
||||
void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
|
||||
mSamplers.setSampler(PerViewSib::SHADOW_MAP, {
|
||||
texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
@@ -325,22 +333,27 @@ void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture) noexcept {
|
||||
}});
|
||||
auto& s = mUniforms.edit();
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
|
||||
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowDPCF(Handle <HwTexture> texture,
|
||||
void PerViewUniforms::prepareShadowDPCF(Handle<HwTexture> texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
SoftShadowOptions const& options) noexcept {
|
||||
mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}});
|
||||
auto& s = mUniforms.edit();
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_DPCF;
|
||||
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
|
||||
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowPCSS(Handle <HwTexture> texture,
|
||||
void PerViewUniforms::prepareShadowPCSS(Handle<HwTexture> texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
SoftShadowOptions const& options) noexcept {
|
||||
mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}});
|
||||
auto& s = mUniforms.edit();
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCSS;
|
||||
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
|
||||
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
|
||||
}
|
||||
|
||||
void PerViewUniforms::commit(backend::DriverApi& driver) noexcept {
|
||||
|
||||
@@ -65,11 +65,20 @@ public:
|
||||
|
||||
void prepareCamera(const CameraInfo& camera) noexcept;
|
||||
void prepareUpscaler(math::float2 scale, DynamicResolutionOptions const& options) noexcept;
|
||||
void prepareViewport(const filament::Viewport& viewport) noexcept;
|
||||
|
||||
/*
|
||||
* @param viewport viewport (should be same as RenderPassParams::viewport)
|
||||
* @param xoffset horizontal rendering offset *within* the viewport.
|
||||
* Non-zero when we have guard bands.
|
||||
* @param yoffset vertical rendering offset *within* the viewport.
|
||||
* Non-zero when we have guard bands.
|
||||
*/
|
||||
void prepareViewport(const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
|
||||
|
||||
void prepareTime(math::float4 const& userTime) noexcept;
|
||||
void prepareTemporalNoise(TemporalAntiAliasingOptions const& options) noexcept;
|
||||
void prepareExposure(float ev100) noexcept;
|
||||
void prepareFog(const CameraInfo& camera, FogOptions const& options) noexcept;
|
||||
void prepareFog(math::float3 const& cameraPosition, FogOptions const& options) noexcept;
|
||||
void prepareStructure(TextureHandle structure) noexcept;
|
||||
void prepareSSAO(TextureHandle ssao, AmbientOcclusionOptions const& options) noexcept;
|
||||
void prepareBlending(bool needsAlphaChannel) noexcept;
|
||||
@@ -84,8 +93,7 @@ public:
|
||||
math::mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
|
||||
void prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept;
|
||||
void prepareShadowMapping() noexcept;
|
||||
|
||||
void prepareDirectionalLight(float exposure,
|
||||
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
|
||||
@@ -94,11 +102,20 @@ public:
|
||||
|
||||
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
|
||||
|
||||
// maybe these should have their own UBO, they're needed only when GENERATING the shadowmaps
|
||||
void prepareShadowVSM(TextureHandle texture, VsmShadowOptions const& options) noexcept;
|
||||
void prepareShadowPCF(TextureHandle texture) noexcept;
|
||||
void prepareShadowDPCF(TextureHandle texture, SoftShadowOptions const& options) noexcept;
|
||||
void prepareShadowPCSS(TextureHandle texture, SoftShadowOptions const& options) noexcept;
|
||||
void prepareShadowVSM(TextureHandle texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept;
|
||||
|
||||
void prepareShadowPCF(TextureHandle texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
|
||||
|
||||
void prepareShadowDPCF(TextureHandle texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
SoftShadowOptions const& options) noexcept;
|
||||
|
||||
void prepareShadowPCSS(TextureHandle texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
SoftShadowOptions const& options) noexcept;
|
||||
|
||||
// update local data into GPU UBO
|
||||
void commit(backend::DriverApi& driver) noexcept;
|
||||
@@ -116,6 +133,8 @@ private:
|
||||
backend::Handle<backend::HwBufferObject> mUniformBufferHandle;
|
||||
backend::Handle<backend::HwSamplerGroup> mSamplerGroupHandle;
|
||||
std::uniform_real_distribution<float> mUniformDistribution{ 0.0f, 1.0f };
|
||||
static void prepareShadowSampling(PerViewUib& uniforms,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+209
-339
@@ -531,22 +531,23 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
data.history = builder.sample(history);
|
||||
}
|
||||
},
|
||||
[=, &uniforms, renderPass = pass](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) mutable {
|
||||
|
||||
[this, projection = cameraInfo.projection,
|
||||
userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection,
|
||||
options, &uniforms, renderPass = pass]
|
||||
(FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable {
|
||||
// set structure sampler
|
||||
uniforms.prepareStructure(data.structure ?
|
||||
resources.getTexture(data.structure) : getOneTexture());
|
||||
|
||||
// set screen-space reflections and screen-space refractions
|
||||
mat4f uvFromViewMatrix = uvFromClipMatrix * cameraInfo.projection;
|
||||
mat4f reprojection = uvFromClipMatrix * historyProjection
|
||||
* inverse(cameraInfo.view * cameraInfo.worldOrigin);
|
||||
mat4f uvFromViewMatrix = uvFromClipMatrix * projection;
|
||||
mat4f reprojection = mat4f{ uvFromClipMatrix * historyProjection
|
||||
* inverse(userViewMatrix) };
|
||||
|
||||
// the history sampler is a regular texture2D
|
||||
TextureHandle history = data.history ?
|
||||
resources.getTexture(data.history) : getZeroTexture();
|
||||
uniforms.prepareHistorySSR(history, reprojection, uvFromViewMatrix,options);
|
||||
uniforms.prepareHistorySSR(history, reprojection, uvFromViewMatrix, options);
|
||||
|
||||
uniforms.commit(driver);
|
||||
|
||||
@@ -804,11 +805,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->setParameter("ssctConeAngleTangeant", std::tan(options.ssct.lightConeRad * 0.5f));
|
||||
mi->setParameter("ssctContactDistanceMaxInv", 1.0f / options.ssct.contactDistanceMax);
|
||||
// light direction in view space
|
||||
// (note: this is actually equivalent to using the camera view matrix -- before the
|
||||
// world matrix is accounted for)
|
||||
auto m = cameraInfo.view * cameraInfo.worldOrigin;
|
||||
const mat4f view{ cameraInfo.getUserViewMatrix() };
|
||||
const float3 l = normalize(
|
||||
mat3f::getTransformForNormals(m.upperLeft())
|
||||
mat3f::getTransformForNormals(view.upperLeft())
|
||||
* options.ssct.lightDirection);
|
||||
mi->setParameter("ssctIntensity",
|
||||
options.ssct.enabled ? options.ssct.intensity : 0.0f);
|
||||
@@ -972,8 +971,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateGaussianMipmap(Frame
|
||||
auto from = input;
|
||||
for (size_t i = 0; i < levels - 1; i++) {
|
||||
auto output = mipmapPass->out[i];
|
||||
gaussianBlurPass(fg, from, output, reinhard, kernelWidth, sigma);
|
||||
from = output;
|
||||
from = gaussianBlurPass(fg, from, output, reinhard, kernelWidth, sigma);
|
||||
reinhard = false; // only do the reinhard filtering on the first level
|
||||
}
|
||||
|
||||
@@ -1034,7 +1032,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
// and because it's a separable filter, the effective 2D filter kernel size is 17*17
|
||||
// The total number of samples needed over the two passes is 18.
|
||||
|
||||
fg.addPass<BlurPassData>("Gaussian Blur Pass (separable)",
|
||||
auto& blurPass = fg.addPass<BlurPassData>("Gaussian Blur Pass (separable)",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto inDesc = builder.getDescriptor(input);
|
||||
|
||||
@@ -1142,7 +1140,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
render(hwOutRT, separableGaussianBlur.getPipelineState(mEngine), driver);
|
||||
});
|
||||
|
||||
return output;
|
||||
return blurPass->out;
|
||||
}
|
||||
|
||||
PostProcessManager::ScreenSpaceRefConfig PostProcessManager::prepareMipmapSSR(FrameGraph& fg,
|
||||
@@ -1341,7 +1339,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateMipmapSSR(
|
||||
input = ppm.resolveBaseLevel(fg, "ssr", input);
|
||||
// then blit into an appropriate texture
|
||||
// this handles scaling, format conversion and mipmaping
|
||||
input = ppm.opaqueBlit(fg, input, outDesc);
|
||||
input = ppm.opaqueBlit(fg, input, { 0, 0, desc.width, desc.height }, outDesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1465,18 +1463,21 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
};
|
||||
|
||||
/*
|
||||
* dofResolution is used (at compile time for now) to chose between full- or quarter-resolution
|
||||
* dofResolution is used to chose between full- or quarter-resolution
|
||||
* for the DoF calculations. Set to [1] for full resolution or [2] for quarter-resolution.
|
||||
*/
|
||||
const uint32_t dofResolution = dofOptions.nativeResolution ? 1u : 2u;
|
||||
|
||||
constexpr const uint32_t maxMipLevels = 4u; // mip levels at full-resolution
|
||||
constexpr const uint32_t maxMipLevelsMask = (1u << maxMipLevels) - 1u;
|
||||
auto const& colorDesc = fg.getDescriptor(input);
|
||||
const uint32_t width = ((colorDesc.width + maxMipLevelsMask) & ~maxMipLevelsMask) / dofResolution;
|
||||
const uint32_t height = ((colorDesc.height + maxMipLevelsMask) & ~maxMipLevelsMask) / dofResolution;
|
||||
const uint8_t maxLevelCount = FTexture::maxLevelCount(width, height);
|
||||
uint8_t mipmapCount = min(maxLevelCount, uint8_t(maxMipLevels));
|
||||
const uint32_t width = colorDesc.width / dofResolution;
|
||||
const uint32_t height = colorDesc.height / dofResolution;
|
||||
|
||||
// at full resolution, 4 "safe" levels are guaranteed
|
||||
constexpr const uint32_t maxMipLevels = 4u;
|
||||
|
||||
// compute numbers of "safe" levels (should be 4, but can be 3 at half res)
|
||||
const uint8_t mipmapCount = std::min(maxMipLevels, ctz(width | height));
|
||||
assert_invariant(mipmapCount == maxMipLevels || mipmapCount == maxMipLevels-1);
|
||||
|
||||
/*
|
||||
* Setup:
|
||||
@@ -1894,22 +1895,14 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, TextureFormat outFormat,
|
||||
BloomOptions& inoutBloomOptions, float2 scale) noexcept {
|
||||
// Chrome does not support feedback loops in WebGL 2.0. See also:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1066201
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
constexpr bool isWebGL = true;
|
||||
#else
|
||||
constexpr bool isWebGL = false;
|
||||
#endif
|
||||
|
||||
// Figure out a good size for the bloom buffer.
|
||||
auto const& desc = fg.getDescriptor(input);
|
||||
|
||||
// width and height after dynamic resolution upscaling
|
||||
const float aspect = (desc.width * scale.y) / (desc.height * scale.x);
|
||||
const float aspect = (float(desc.width) * scale.y) / (float(desc.height) * scale.x);
|
||||
|
||||
// compute the desired bloom buffer size
|
||||
float bloomHeight = inoutBloomOptions.resolution;
|
||||
float bloomHeight = float(inoutBloomOptions.resolution);
|
||||
float bloomWidth = bloomHeight * aspect;
|
||||
|
||||
// Anamorphic bloom by always scaling down one of the dimension -- we do this (as opposed
|
||||
@@ -1923,11 +1916,11 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
|
||||
}
|
||||
|
||||
// convert back to integer width/height
|
||||
const uint32_t width = std::max(1.0f, std::floor(bloomWidth));
|
||||
const uint32_t height = std::max(1.0f, std::floor(bloomHeight));
|
||||
const uint32_t width = std::max(1u, uint32_t(std::floor(bloomWidth)));
|
||||
const uint32_t height = std::max(1u, uint32_t(std::floor(bloomHeight)));
|
||||
|
||||
// we might need to adjust the max # of levels
|
||||
const uint32_t major = std::max(bloomWidth, bloomHeight);
|
||||
const uint32_t major = uint32_t(std::max(bloomWidth, bloomHeight));
|
||||
const uint8_t maxLevels = FTexture::maxLevelCount(major);
|
||||
inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, maxLevels);
|
||||
inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, kMaxBloomLevels);
|
||||
@@ -1935,295 +1928,161 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
|
||||
if (2 * width < desc.width || 2 * height < desc.height) {
|
||||
// if we're scaling down by more than 2x, prescale the image with a blit to improve
|
||||
// performance. This is important on mobile/tilers.
|
||||
input = opaqueBlit(fg, input, {
|
||||
input = opaqueBlit(fg, input, { 0, 0, desc.width, desc.height }, {
|
||||
.width = std::max(1u, desc.width / 2),
|
||||
.height = std::max(1u, desc.height / 2),
|
||||
.format = outFormat
|
||||
});
|
||||
}
|
||||
|
||||
if constexpr(!isWebGL) {
|
||||
struct BloomPassData {
|
||||
FrameGraphId<FrameGraphTexture> in;
|
||||
FrameGraphId<FrameGraphTexture> out;
|
||||
FrameGraphId<FrameGraphTexture> stage;
|
||||
uint32_t outRT[kMaxBloomLevels];
|
||||
uint32_t stageRT[kMaxBloomLevels];
|
||||
};
|
||||
|
||||
struct BloomPassData {
|
||||
FrameGraphId<FrameGraphTexture> in;
|
||||
FrameGraphId<FrameGraphTexture> out;
|
||||
};
|
||||
// downsample phase
|
||||
auto& bloomDownsamplePass = fg.addPass<BloomPassData>("Bloom Downsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.in = builder.sample(input);
|
||||
data.out = builder.createTexture("Bloom Out Texture", {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = inoutBloomOptions.levels,
|
||||
.format = outFormat
|
||||
});
|
||||
data.out = builder.sample(data.out);
|
||||
|
||||
// downsample phase
|
||||
auto& bloomDownsamplePass = fg.addPass<BloomPassData>("Bloom Downsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.in = builder.sample(input);
|
||||
data.out = builder.createTexture("Bloom Texture", {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = inoutBloomOptions.levels,
|
||||
.format = outFormat
|
||||
});
|
||||
data.stage = builder.createTexture("Bloom Stage Texture", {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = inoutBloomOptions.levels,
|
||||
.format = outFormat
|
||||
});
|
||||
data.stage = builder.sample(data.stage);
|
||||
|
||||
data.out = builder.sample(data.out);
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
auto stage = builder.createSubresource(data.stage,
|
||||
"Bloom Stage Texture mip", { .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out, &data.outRT[i]);
|
||||
builder.declareRenderPass(stage, &data.stageRT[i]);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
auto const& material = getPostProcessMaterial("bloomDownsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
auto const& material = getPostProcessMaterial("bloomDownsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
|
||||
const PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
const PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto hwOut = resources.getTexture(data.out);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto hwOut = resources.getTexture(data.out);
|
||||
auto hwStage = resources.getTexture(data.stage);
|
||||
|
||||
mi->use(driver);
|
||||
mi->setParameter("source", hwIn, {
|
||||
mi->use(driver);
|
||||
mi->setParameter("source", hwIn, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR /* level is always 0 */
|
||||
});
|
||||
mi->setParameter("level", 0.0f);
|
||||
mi->setParameter("threshold", inoutBloomOptions.threshold ? 1.0f : 0.0f);
|
||||
mi->setParameter("invHighlight",
|
||||
std::isinf(inoutBloomOptions.highlight) ? 0.0f : 1.0f
|
||||
/ inoutBloomOptions.highlight);
|
||||
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
const bool parity = (i % 2) == 0;
|
||||
auto hwDstRT = resources.getRenderPassInfo(
|
||||
parity ? data.outRT[i] : data.stageRT[i]);
|
||||
|
||||
mi->commit(driver);
|
||||
|
||||
hwDstRT.params.flags.discardStart = TargetBufferFlags::COLOR;
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
render(hwDstRT, pipeline, driver);
|
||||
|
||||
// prepare the next level
|
||||
mi->setParameter("source", parity ? hwOut : hwStage, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR /* level is always 0 */
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", 0.0f);
|
||||
mi->setParameter("threshold", inoutBloomOptions.threshold ? 1.0f : 0.0f);
|
||||
mi->setParameter("invHighlight",
|
||||
std::isinf(inoutBloomOptions.highlight) ? 0.0f : 1.0f
|
||||
/ inoutBloomOptions.highlight);
|
||||
mi->setParameter("level", float(i));
|
||||
}
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto hwOutRT = resources.getRenderPassInfo(i);
|
||||
FrameGraphId<FrameGraphTexture> output = bloomDownsamplePass->out;
|
||||
FrameGraphId<FrameGraphTexture> stage = bloomDownsamplePass->stage;
|
||||
|
||||
auto w = FTexture::valueForLevel(i, outDesc.width);
|
||||
auto h = FTexture::valueForLevel(i, outDesc.height);
|
||||
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
|
||||
mi->commit(driver);
|
||||
// flare pass
|
||||
auto flare = flarePass(fg, bloomDownsamplePass->out, width, height, outFormat, inoutBloomOptions);
|
||||
|
||||
hwOutRT.params.flags.discardStart = TargetBufferFlags::COLOR;
|
||||
hwOutRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
render(hwOutRT, pipeline, driver);
|
||||
// upsample phase
|
||||
auto& bloomUpsamplePass = fg.addPass<BloomPassData>("Bloom Upsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.out = builder.sample(output);
|
||||
data.stage = builder.sample(stage);
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
auto stage = builder.createSubresource(data.stage,
|
||||
"Bloom Stage Texture mip", { .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out, &data.outRT[i]);
|
||||
builder.declareRenderPass(stage, &data.stageRT[i]);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
|
||||
// prepare the next level
|
||||
mi->setParameter("source", hwOut, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", float(i));
|
||||
driver.setMinMaxLevels(hwOut, i,
|
||||
i); // safe because we're using LINEAR_MIPMAP_NEAREST
|
||||
}
|
||||
driver.setMinMaxLevels(hwOut, 0, inoutBloomOptions.levels - 1);
|
||||
});
|
||||
auto hwOut = resources.getTexture(data.out);
|
||||
auto hwStage = resources.getTexture(data.stage);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
|
||||
input = bloomDownsamplePass->out;
|
||||
auto const& material = getPostProcessMaterial("bloomUpsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
|
||||
// flare pass
|
||||
auto flare = flarePass(fg, input, width, height, outFormat, inoutBloomOptions);
|
||||
mi->use(driver);
|
||||
|
||||
// upsample phase
|
||||
auto& bloomUpsamplePass = fg.addPass<BloomPassData>("Bloom Upsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.in = builder.sample(input);
|
||||
data.out = input;
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) {
|
||||
const bool parity = (j % 2) == 0;
|
||||
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
auto hwDstRT = resources.getRenderPassInfo(
|
||||
parity ? data.outRT[i - 1] : data.stageRT[i - 1]);
|
||||
hwDstRT.params
|
||||
.flags
|
||||
.discardStart = TargetBufferFlags::NONE; // because we'll blend
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
auto const& material = getPostProcessMaterial("bloomUpsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
|
||||
mi->use(driver);
|
||||
|
||||
for (size_t i = inoutBloomOptions.levels - 1; i >= 1; i--) {
|
||||
auto hwDstRT = resources.getRenderPassInfo(i - 1);
|
||||
hwDstRT.params
|
||||
.flags
|
||||
.discardStart = TargetBufferFlags::NONE; // because we'll blend
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
auto w = FTexture::valueForLevel(i - 1, outDesc.width);
|
||||
auto h = FTexture::valueForLevel(i - 1, outDesc.height);
|
||||
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
|
||||
mi->setParameter("source", hwIn, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", float(i));
|
||||
driver.setMinMaxLevels(hwIn, i, i);
|
||||
mi->commit(driver);
|
||||
render(hwDstRT, pipeline, driver);
|
||||
}
|
||||
|
||||
driver.setMinMaxLevels(hwIn, 0, inoutBloomOptions.levels - 1);
|
||||
});
|
||||
|
||||
return { bloomUpsamplePass->out, flare };
|
||||
|
||||
} else { // !isWebGL
|
||||
|
||||
struct BloomPassData {
|
||||
FrameGraphId<FrameGraphTexture> in;
|
||||
FrameGraphId<FrameGraphTexture> out;
|
||||
FrameGraphId<FrameGraphTexture> stage;
|
||||
uint32_t outRT[kMaxBloomLevels];
|
||||
uint32_t stageRT[kMaxBloomLevels];
|
||||
};
|
||||
|
||||
// downsample phase
|
||||
auto& bloomDownsamplePass = fg.addPass<BloomPassData>("Bloom Downsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.in = builder.sample(input);
|
||||
data.out = builder.createTexture("Bloom Out Texture", {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = inoutBloomOptions.levels,
|
||||
.format = outFormat
|
||||
});
|
||||
data.out = builder.sample(data.out);
|
||||
|
||||
data.stage = builder.createTexture("Bloom Stage Texture", {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.levels = inoutBloomOptions.levels,
|
||||
.format = outFormat
|
||||
});
|
||||
data.stage = builder.sample(data.stage);
|
||||
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
auto stage = builder.createSubresource(data.stage,
|
||||
"Bloom Stage Texture mip", { .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out, &data.outRT[i]);
|
||||
builder.declareRenderPass(stage, &data.stageRT[i]);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
auto const& material = getPostProcessMaterial("bloomDownsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
|
||||
const PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto hwOut = resources.getTexture(data.out);
|
||||
auto hwStage = resources.getTexture(data.stage);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
|
||||
mi->use(driver);
|
||||
mi->setParameter("source", hwIn, {
|
||||
auto w = FTexture::valueForLevel(i - 1, outDesc.width);
|
||||
auto h = FTexture::valueForLevel(i - 1, outDesc.height);
|
||||
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
|
||||
mi->setParameter("source", parity ? hwStage : hwOut, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR /* level is always 0 */
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", 0.0f);
|
||||
mi->setParameter("threshold", inoutBloomOptions.threshold ? 1.0f : 0.0f);
|
||||
mi->setParameter("invHighlight",
|
||||
std::isinf(inoutBloomOptions.highlight) ? 0.0f : 1.0f
|
||||
/ inoutBloomOptions.highlight);
|
||||
mi->setParameter("level", float(i));
|
||||
mi->commit(driver);
|
||||
render(hwDstRT, pipeline, driver);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
const bool parity = (i % 2) == 0;
|
||||
auto hwDstRT = resources.getRenderPassInfo(
|
||||
parity ? data.outRT[i] : data.stageRT[i]);
|
||||
// Every other level is missing from the out texture, so we need to do
|
||||
// blits to complete the chain.
|
||||
const SamplerMagFilter filter = SamplerMagFilter::NEAREST;
|
||||
for (size_t i = 1; i < inoutBloomOptions.levels; i += 2) {
|
||||
auto in = resources.getRenderPassInfo(data.stageRT[i]);
|
||||
auto out = resources.getRenderPassInfo(data.outRT[i]);
|
||||
driver.blit(TargetBufferFlags::COLOR, out.target, out.params.viewport,
|
||||
in.target, in.params.viewport, filter);
|
||||
}
|
||||
});
|
||||
|
||||
auto w = FTexture::valueForLevel(i, outDesc.width);
|
||||
auto h = FTexture::valueForLevel(i, outDesc.height);
|
||||
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
|
||||
mi->commit(driver);
|
||||
|
||||
hwDstRT.params.flags.discardStart = TargetBufferFlags::COLOR;
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
render(hwDstRT, pipeline, driver);
|
||||
|
||||
// prepare the next level
|
||||
mi->setParameter("source", parity ? hwOut : hwStage, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", float(i));
|
||||
}
|
||||
});
|
||||
|
||||
FrameGraphId<FrameGraphTexture> output = bloomDownsamplePass->out;
|
||||
FrameGraphId<FrameGraphTexture> stage = bloomDownsamplePass->stage;
|
||||
|
||||
// flare pass
|
||||
auto flare = flarePass(fg, bloomDownsamplePass->out, width, height, outFormat, inoutBloomOptions);
|
||||
|
||||
// upsample phase
|
||||
auto& bloomUpsamplePass = fg.addPass<BloomPassData>("Bloom Upsample",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.out = builder.sample(output);
|
||||
data.stage = builder.sample(stage);
|
||||
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
|
||||
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
|
||||
{ .level = uint8_t(i) });
|
||||
auto stage = builder.createSubresource(data.stage,
|
||||
"Bloom Stage Texture mip", { .level = uint8_t(i) });
|
||||
builder.declareRenderPass(out, &data.outRT[i]);
|
||||
builder.declareRenderPass(stage, &data.stageRT[i]);
|
||||
}
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
|
||||
auto hwOut = resources.getTexture(data.out);
|
||||
auto hwStage = resources.getTexture(data.stage);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
|
||||
auto const& material = getPostProcessMaterial("bloomUpsample");
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
|
||||
mi->use(driver);
|
||||
|
||||
for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) {
|
||||
const bool parity = (j % 2) == 0;
|
||||
|
||||
auto hwDstRT = resources.getRenderPassInfo(
|
||||
parity ? data.outRT[i - 1] : data.stageRT[i - 1]);
|
||||
hwDstRT.params
|
||||
.flags
|
||||
.discardStart = TargetBufferFlags::NONE; // because we'll blend
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
auto w = FTexture::valueForLevel(i - 1, outDesc.width);
|
||||
auto h = FTexture::valueForLevel(i - 1, outDesc.height);
|
||||
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
|
||||
mi->setParameter("source", parity ? hwStage : hwOut, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
mi->setParameter("level", float(i));
|
||||
mi->commit(driver);
|
||||
render(hwDstRT, pipeline, driver);
|
||||
}
|
||||
|
||||
// Every other level is missing from the out texture, so we need to do
|
||||
// blits to complete the chain.
|
||||
const SamplerMagFilter filter = SamplerMagFilter::NEAREST;
|
||||
for (size_t i = 1; i < inoutBloomOptions.levels; i += 2) {
|
||||
auto in = resources.getRenderPassInfo(data.stageRT[i]);
|
||||
auto out = resources.getRenderPassInfo(data.outRT[i]);
|
||||
driver.blit(TargetBufferFlags::COLOR, out.target, out.params.viewport,
|
||||
in.target, in.params.viewport, filter);
|
||||
}
|
||||
});
|
||||
|
||||
return { bloomUpsamplePass->out, flare };
|
||||
}
|
||||
return { bloomUpsamplePass->out, flare };
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
@@ -2406,14 +2265,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphId<FrameGraphTexture> bloom,
|
||||
FrameGraphId<FrameGraphTexture> flare,
|
||||
FColorGrading const* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig,
|
||||
BloomOptions const& bloomOptions,
|
||||
VignetteOptions const& vignetteOptions,
|
||||
float2 scale) noexcept
|
||||
VignetteOptions const& vignetteOptions) noexcept
|
||||
{
|
||||
FrameGraphId<FrameGraphTexture> bloomDirt;
|
||||
FrameGraphId<FrameGraphTexture> starburst;
|
||||
@@ -2450,11 +2308,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
|
||||
|
||||
auto& ppColorGrading = fg.addPass<PostProcessColorGrading>("colorGrading",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto const& inputDesc = fg.getDescriptor(input);
|
||||
data.input = builder.sample(input);
|
||||
data.output = builder.createTexture("colorGrading output", {
|
||||
.width = inputDesc.width,
|
||||
.height = inputDesc.height,
|
||||
.width = vp.width,
|
||||
.height = vp.height,
|
||||
.format = colorGradingConfig.ldrFormat
|
||||
});
|
||||
data.output = builder.declareRenderPass(data.output);
|
||||
@@ -2531,6 +2388,7 @@ 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 vignetteParameters = getVignetteParameters(
|
||||
vignetteOptions, output.width, output.height);
|
||||
@@ -2543,6 +2401,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
|
||||
mi->setParameter("vignetteColor", vignetteOptions.color);
|
||||
mi->setParameter("fxaa", colorGradingConfig.fxaa);
|
||||
mi->setParameter("temporalNoise", temporalNoise);
|
||||
mi->setParameter("viewport", float4{
|
||||
(float)vp.left / input.width,
|
||||
(float)vp.bottom / input.height,
|
||||
(float)vp.width / input.width,
|
||||
(float)vp.height / input.height
|
||||
});
|
||||
|
||||
const uint8_t variant = uint8_t(colorGradingConfig.translucent ?
|
||||
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
|
||||
@@ -2555,7 +2419,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
TextureFormat outFormat, bool translucent) noexcept {
|
||||
|
||||
struct PostProcessFXAA {
|
||||
@@ -2565,17 +2429,16 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
|
||||
|
||||
auto& ppFXAA = fg.addPass<PostProcessFXAA>("fxaa",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto const& inputDesc = fg.getDescriptor(input);
|
||||
data.input = builder.sample(input);
|
||||
data.output = builder.createTexture("fxaa output", {
|
||||
.width = inputDesc.width,
|
||||
.height = inputDesc.height,
|
||||
.width = vp.width,
|
||||
.height = vp.height,
|
||||
.format = outFormat
|
||||
});
|
||||
data.output = builder.declareRenderPass(data.output);
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto const& inDesc = resources.getDescriptor(data.input);
|
||||
auto const& texture = resources.getTexture(data.input);
|
||||
auto const& out = resources.getRenderPassInfo();
|
||||
|
||||
@@ -2585,6 +2448,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("viewport", float4{
|
||||
(float)vp.left / inDesc.width,
|
||||
(float)vp.bottom / inDesc.height,
|
||||
(float)vp.width / inDesc.width,
|
||||
(float)vp.height / inDesc.height
|
||||
});
|
||||
mi->setParameter("texelSize", 1.0f / float2{ inDesc.width, inDesc.height });
|
||||
|
||||
const uint8_t variant = uint8_t(translucent ?
|
||||
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
|
||||
@@ -2604,7 +2474,7 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
|
||||
auto& current = frameHistory.getCurrent().*pTaa;
|
||||
|
||||
// compute projection
|
||||
current.projection = inoutCameraInfo->projection * (inoutCameraInfo->view * inoutCameraInfo->worldOrigin);
|
||||
current.projection = mat4f{ inoutCameraInfo->projection * inoutCameraInfo->getUserViewMatrix() };
|
||||
current.frameId = previous.frameId + 1;
|
||||
|
||||
// sample position within a pixel [-0.5, 0.5]
|
||||
@@ -2701,7 +2571,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
float2 d = sampleOffsets[i] - current.jitter;
|
||||
d *= 1.0f / taaOptions.filterWidth;
|
||||
// this is a gaussian fit of a 3.3 Blackman Harris window
|
||||
// see: "High Quality Temporal Supersampling" by Bruan Karis
|
||||
// see: "High Quality Temporal Supersampling" by Brian Karis
|
||||
weights[i] = std::exp2(-3.3f * (d.x * d.x + d.y * d.y));
|
||||
sum += weights[i];
|
||||
}
|
||||
@@ -2769,7 +2639,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor const& outDesc,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphTexture::Descriptor const& outDesc,
|
||||
SamplerMagFilter filter) noexcept {
|
||||
|
||||
struct PostProcessScaling {
|
||||
@@ -2795,7 +2666,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
|
||||
// HwTexture handle. Using a RenderPass works because data.input will resolve
|
||||
// to the actual imported render target and will have the correct viewport.
|
||||
builder.declareRenderPass("opaque blit input", {
|
||||
.attachments = { .color = { data.input }}});
|
||||
.attachments = { .color = { data.input }},
|
||||
.viewport = vp
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderPassInfo(0);
|
||||
@@ -2812,7 +2685,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|
||||
FrameGraph& fg, bool translucent, DynamicResolutionOptions dsrOptions,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphTexture::Descriptor const& outDesc) noexcept {
|
||||
|
||||
Handle<HwRenderPrimitive> fullScreenRenderPrimitive = mEngine.getFullScreenRenderPrimitive();
|
||||
@@ -2829,13 +2702,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|
||||
|| dsrOptions.quality == QualityLevel::HIGH);
|
||||
|
||||
// helper to set the EASU uniforms
|
||||
auto setEasuUniforms = [](FMaterialInstance* mi,
|
||||
auto setEasuUniforms = [vp](FMaterialInstance* mi,
|
||||
FrameGraphTexture::Descriptor const& inputDesc,
|
||||
FrameGraphTexture::Descriptor const& outputDesc) {
|
||||
FSRUniforms uniforms{};
|
||||
FSR_ScalingSetup(&uniforms, {
|
||||
.viewportWidth = inputDesc.width,
|
||||
.viewportHeight = inputDesc.height,
|
||||
.input = vp,
|
||||
.inputWidth = inputDesc.width,
|
||||
.inputHeight = inputDesc.height,
|
||||
.outputWidth = outputDesc.width,
|
||||
@@ -2908,6 +2780,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|
||||
float4{ outputDesc.width, outputDesc.height,
|
||||
1.0f / outputDesc.width, 1.0f / outputDesc.height });
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
{ // just a scope to not leak local variables
|
||||
@@ -2926,7 +2799,14 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|
||||
mi->setParameter("resolution",
|
||||
float4{ outputDesc.width, outputDesc.height,
|
||||
1.0f / outputDesc.width, 1.0f / outputDesc.height });
|
||||
mi->setParameter("viewport", float4{
|
||||
(float)vp.left / inputDesc.width,
|
||||
(float)vp.bottom / inputDesc.height,
|
||||
(float)vp.width / inputDesc.width,
|
||||
(float)vp.height / inputDesc.height
|
||||
});
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -2934,37 +2814,27 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|
||||
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
if (twoPassesEASU) {
|
||||
auto* mi = splitEasuMaterial->getMaterialInstance(mEngine);
|
||||
mi->use(driver);
|
||||
PipelineState pipeline(splitEasuMaterial->getPipelineState(mEngine));
|
||||
if (UTILS_UNLIKELY(twoPassesEASU)) {
|
||||
PipelineState pipeline0(splitEasuMaterial->getPipelineState(mEngine));
|
||||
PipelineState pipeline1(easuMaterial->getPipelineState(mEngine));
|
||||
pipeline1.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
|
||||
if (translucent) {
|
||||
enableTranslucentBlending(pipeline);
|
||||
enableTranslucentBlending(pipeline0);
|
||||
enableTranslucentBlending(pipeline1);
|
||||
}
|
||||
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive, 1);
|
||||
}
|
||||
|
||||
{ // scope to not leak local variables
|
||||
auto* mi = easuMaterial->getMaterialInstance(mEngine);
|
||||
mi->use(driver);
|
||||
|
||||
driver.draw(pipeline0, fullScreenRenderPrimitive, 1);
|
||||
driver.draw(pipeline1, fullScreenRenderPrimitive, 1);
|
||||
driver.endRenderPass();
|
||||
} else {
|
||||
PipelineState pipeline(easuMaterial->getPipelineState(mEngine));
|
||||
if (translucent) {
|
||||
enableTranslucentBlending(pipeline);
|
||||
}
|
||||
if (twoPassesEASU) {
|
||||
pipeline.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
|
||||
}
|
||||
|
||||
if (!twoPassesEASU) {
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
}
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive, 1);
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
driver.endRenderPass();
|
||||
});
|
||||
|
||||
auto output = ppQuadBlit->output;
|
||||
|
||||
@@ -162,14 +162,13 @@ public:
|
||||
|
||||
// Color grading, tone mapping, dithering and bloom
|
||||
FrameGraphId<FrameGraphTexture> colorGrading(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphId<FrameGraphTexture> bloom,
|
||||
FrameGraphId<FrameGraphTexture> flare,
|
||||
const FColorGrading* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig,
|
||||
BloomOptions const& bloomOptions,
|
||||
VignetteOptions const& vignetteOptions,
|
||||
math::float2 scale) noexcept;
|
||||
VignetteOptions const& vignetteOptions) noexcept;
|
||||
|
||||
void colorGradingPrepareSubpass(backend::DriverApi& driver, const FColorGrading* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig,
|
||||
@@ -188,8 +187,8 @@ public:
|
||||
|
||||
// Anti-aliasing
|
||||
FrameGraphId<FrameGraphTexture> fxaa(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
|
||||
bool translucent) noexcept;
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
backend::TextureFormat outFormat, bool translucent) noexcept;
|
||||
|
||||
// Temporal Anti-aliasing
|
||||
void prepareTaa(FrameGraph& fg, filament::Viewport const& svp,
|
||||
@@ -208,12 +207,13 @@ public:
|
||||
|
||||
// Blit/rescaling/resolves
|
||||
FrameGraphId<FrameGraphTexture> opaqueBlit(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor const& outDesc,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphTexture::Descriptor const& outDesc,
|
||||
backend::SamplerMagFilter filter = backend::SamplerMagFilter::LINEAR) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> blendBlit(
|
||||
FrameGraph& fg, bool translucent, DynamicResolutionOptions dsrOptions,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
|
||||
FrameGraphTexture::Descriptor const& outDesc) noexcept;
|
||||
|
||||
// resolve base level of input and outputs a 1-level texture
|
||||
|
||||
@@ -74,6 +74,11 @@ void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range<uint32_t> v
|
||||
mUboHandle = uboHandle;
|
||||
}
|
||||
|
||||
void RenderPass::setCamera(const CameraInfo& camera) noexcept {
|
||||
mCameraPosition = camera.getPosition();
|
||||
mCameraForwardVector = camera.getForwardVector();
|
||||
}
|
||||
|
||||
void RenderPass::overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept {
|
||||
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
|
||||
mPolygonOffset = *polygonOffset;
|
||||
@@ -97,7 +102,6 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
|
||||
const RenderFlags renderFlags = mFlags;
|
||||
const Variant variant = mVariant;
|
||||
const FScene::VisibleMaskType visibilityMask = mVisibilityMask;
|
||||
CameraInfo const& camera = mCamera;
|
||||
|
||||
// up-to-date summed primitive counts needed for generateCommands()
|
||||
FScene::RenderableSoa const& soa = *mRenderableSoa;
|
||||
@@ -112,9 +116,8 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
|
||||
commandCount += 1; // for the sentinel
|
||||
Command* const curr = append(commandCount);
|
||||
|
||||
// we extract camera position/forward outside of the loop, because these are not cheap.
|
||||
const float3 cameraPosition(camera.getPosition());
|
||||
const float3 cameraForwardVector(camera.getForwardVector());
|
||||
const float3 cameraPosition(mCameraPosition);
|
||||
const float3 cameraForwardVector(mCameraForwardVector);
|
||||
auto work = [commandTypeFlags, curr, &soa, variant, renderFlags, visibilityMask, cameraPosition,
|
||||
cameraForwardVector]
|
||||
(uint32_t startIndex, uint32_t indexCount) {
|
||||
@@ -135,6 +138,15 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
|
||||
// "eof" command. these commands are guaranteed to be sorted last in the
|
||||
// command buffer.
|
||||
curr[commandCount - 1].key = uint64_t(Pass::SENTINEL);
|
||||
|
||||
// Go over all the commands and call prepareProgram().
|
||||
// This must be done from the main thread.
|
||||
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
|
||||
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
|
||||
auto ma = first->primitive.mi->getMaterial();
|
||||
ma->prepareProgram(first->primitive.materialVariant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
@@ -399,7 +411,6 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
if constexpr (isColorPass) {
|
||||
cmdColor.primitive.primitiveHandle = primitive.getHwHandle();
|
||||
RenderPass::setupColorCommand(cmdColor, variant, mi, inverseFrontFaces);
|
||||
ma->prepareProgram(cmdColor.primitive.materialVariant);
|
||||
|
||||
cmdColor.primitive.morphWeightBuffer = morphing.handle;
|
||||
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
|
||||
@@ -484,7 +495,6 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
}
|
||||
|
||||
if constexpr (isDepthPass) {
|
||||
ma->prepareProgram(cmdDepth.primitive.materialVariant);
|
||||
const RasterState rs = ma->getRasterState();
|
||||
const TransparencyMode mode = mi->getTransparencyMode();
|
||||
const BlendingMode blendingMode = ma->getBlendingMode();
|
||||
@@ -532,7 +542,7 @@ void RenderPass::updateSummedPrimitiveCounts(
|
||||
|
||||
void RenderPass::Executor::execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept {
|
||||
backend::RenderPassParams const& params) const noexcept {
|
||||
FEngine& engine = mEngine;
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ public:
|
||||
backend::Handle<backend::HwBufferObject> uboHandle) noexcept;
|
||||
|
||||
// specifies camera information (e.g. used for sorting commands)
|
||||
void setCamera(const CameraInfo& camera) noexcept { mCamera = camera; }
|
||||
void setCamera(const CameraInfo& camera) noexcept;
|
||||
|
||||
// flags controlling how commands are generated
|
||||
void setRenderFlags(RenderFlags flags) noexcept { mFlags = flags; }
|
||||
@@ -333,7 +333,7 @@ public:
|
||||
~Executor() noexcept;
|
||||
void execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept;
|
||||
backend::RenderPassParams const& params) const noexcept;
|
||||
};
|
||||
|
||||
// returns a new executor for this pass
|
||||
@@ -406,7 +406,8 @@ private:
|
||||
backend::Handle<backend::HwBufferObject> mUboHandle;
|
||||
|
||||
// info about the camera
|
||||
CameraInfo mCamera;
|
||||
math::float3 mCameraPosition{};
|
||||
math::float3 mCameraForwardVector{};
|
||||
|
||||
// info about the scene features (e.g.: has shadows, lighting, etc...)
|
||||
RenderFlags mFlags{};
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 "RendererUtils.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include "fg/FrameGraph.h"
|
||||
#include "fg/FrameGraphId.h"
|
||||
#include "fg/FrameGraphResources.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor) noexcept {
|
||||
|
||||
struct ColorPassData {
|
||||
FrameGraphId<FrameGraphTexture> shadows;
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphId<FrameGraphTexture> ssr; // either screen-space reflections or refractions
|
||||
FrameGraphId<FrameGraphTexture> structure;
|
||||
float4 clearColor{};
|
||||
TargetBufferFlags clearFlags{};
|
||||
};
|
||||
|
||||
Blackboard& blackboard = fg.getBlackboard();
|
||||
|
||||
auto& colorPass = fg.addPass<ColorPassData>(name,
|
||||
[&](FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
|
||||
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
|
||||
TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
data.color = blackboard.get<FrameGraphTexture>("color");
|
||||
data.depth = blackboard.get<FrameGraphTexture>("depth");
|
||||
|
||||
// Screen-space reflection or refractions
|
||||
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
|
||||
if (data.ssr) {
|
||||
data.ssr = builder.sample(data.ssr);
|
||||
}
|
||||
|
||||
if (config.hasContactShadows) {
|
||||
data.structure = blackboard.get<FrameGraphTexture>("structure");
|
||||
assert_invariant(data.structure);
|
||||
data.structure = builder.sample(data.structure);
|
||||
}
|
||||
|
||||
if (data.shadows) {
|
||||
data.shadows = builder.sample(data.shadows);
|
||||
}
|
||||
|
||||
if (data.ssao) {
|
||||
data.ssao = builder.sample(data.ssao);
|
||||
}
|
||||
|
||||
if (!data.color) {
|
||||
// FIXME: this works only when the viewport is full
|
||||
// if (view.isSkyboxVisible()) {
|
||||
// // if the skybox is visible, then we don't need to clear at all
|
||||
// clearColorFlags &= ~TargetBufferFlags::COLOR;
|
||||
// }
|
||||
data.color = builder.createTexture("Color Buffer", colorBufferDesc);
|
||||
}
|
||||
|
||||
const bool canResolveDepth = engine.getDriverApi().isAutoDepthResolveSupported();
|
||||
|
||||
if (!data.depth) {
|
||||
// clear newly allocated depth buffers, regardless of given clear flags
|
||||
clearDepthFlags = TargetBufferFlags::DEPTH;
|
||||
data.depth = builder.createTexture("Depth Buffer", {
|
||||
.width = colorBufferDesc.width,
|
||||
.height = colorBufferDesc.height,
|
||||
// If the color attachment requested MS, we assume this means the MS buffer
|
||||
// must be kept, and for that reason we allocate the depth buffer with MS
|
||||
// as well. On the other hand, if the color attachment was allocated without
|
||||
// MS, no need to allocate the depth buffer with MS, if the RT is MS,
|
||||
// the tile depth buffer will be MS, but it'll be resolved to single
|
||||
// sample automatically -- which is what we want.
|
||||
.samples = canResolveDepth ? colorBufferDesc.samples : uint8_t(config.msaa),
|
||||
.format = TextureFormat::DEPTH32F,
|
||||
});
|
||||
}
|
||||
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
data.output = builder.createTexture("Tonemapped Buffer", {
|
||||
.width = colorBufferDesc.width,
|
||||
.height = colorBufferDesc.height,
|
||||
.format = colorGradingConfig.ldrFormat
|
||||
});
|
||||
data.color = builder.read(data.color, FrameGraphTexture::Usage::SUBPASS_INPUT);
|
||||
data.output = builder.write(data.output, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
} else if (colorGradingConfig.customResolve) {
|
||||
data.color = builder.read(data.color, FrameGraphTexture::Usage::SUBPASS_INPUT);
|
||||
}
|
||||
|
||||
// We set a "read" constraint on these attachments here because we need to preserve them
|
||||
// when the color pass happens in several passes (e.g. with SSR)
|
||||
data.color = builder.read(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
/*
|
||||
* There is a bit of magic happening here regarding the viewport used.
|
||||
* We do not specify the viewport in declareRenderPass() below, so it will be
|
||||
* deduced automatically to be { 0, 0, w, h }, with w,h the min width/height of
|
||||
* all the attachments. This has the side effect of moving the viewport to the
|
||||
* origin and ignore the left/bottom of 'svp'. The attachment sizes are set from
|
||||
* svp's width/height, however.
|
||||
* But that's not all! When we're rendering directly into the swap-chain (by way
|
||||
* of calling forwardResource() later), the effective viewport comes from the
|
||||
* imported resource (i.e. the swap-chain) and is set to 'vp' which has its
|
||||
* left/bottom honored -- the view is therefore rendered directly where it should
|
||||
* be (the imported resource viewport is set to 'vp', see how 'fgViewRenderTarget'
|
||||
* is initialized in this file).
|
||||
*/
|
||||
builder.declareRenderPass("Color Pass Target", {
|
||||
.attachments = { .color = { data.color, data.output }, .depth = data.depth },
|
||||
.samples = config.msaa,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags });
|
||||
|
||||
data.clearColor = config.clearColor;
|
||||
data.clearFlags = clearColorFlags | clearDepthFlags;
|
||||
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
[=, &view, &engine](FrameGraphResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
// set samplers and uniforms
|
||||
view.prepareSSAO(data.ssao ?
|
||||
resources.getTexture(data.ssao) : engine.getOneTextureArray());
|
||||
|
||||
view.prepareShadowMap();
|
||||
|
||||
// set shadow sampler
|
||||
view.prepareShadow(data.shadows ?
|
||||
resources.getTexture(data.shadows) : engine.getOneTextureArray());
|
||||
|
||||
// set structure sampler
|
||||
view.prepareStructure(data.structure ?
|
||||
resources.getTexture(data.structure) : engine.getOneTexture());
|
||||
|
||||
// set screen-space reflections and screen-space refractions
|
||||
TextureHandle ssr = data.ssr ?
|
||||
resources.getTexture(data.ssr) : engine.getOneTextureArray();
|
||||
|
||||
view.prepareSSR(ssr, config.ssrLodOffset,
|
||||
view.getScreenSpaceReflectionsOptions());
|
||||
|
||||
// Note: here we can't use data.color's descriptor for the viewport because
|
||||
// the actual viewport might be offset when the target is the swapchain.
|
||||
// However, the width/height should be the same.
|
||||
assert_invariant(
|
||||
out.params.viewport.width == resources.getDescriptor(data.color).width);
|
||||
assert_invariant(
|
||||
out.params.viewport.height == resources.getDescriptor(data.color).height);
|
||||
|
||||
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
|
||||
config.xoffset, config.yoffset);
|
||||
view.commitUniforms(driver);
|
||||
|
||||
out.params.clearColor = data.clearColor;
|
||||
out.params.flags.clear = data.clearFlags;
|
||||
if (view.getBlendMode() == BlendMode::TRANSLUCENT) {
|
||||
if (any(out.params.flags.discardStart & TargetBufferFlags::COLOR0)) {
|
||||
// if the buffer is discarded (e.g. it's new) and we're blending,
|
||||
// then clear it to transparent
|
||||
out.params.flags.clear |= TargetBufferFlags::COLOR;
|
||||
out.params.clearColor = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (colorGradingConfig.asSubpass || colorGradingConfig.customResolve) {
|
||||
out.params.subpassMask = 1;
|
||||
}
|
||||
passExecutor.execute(resources.getPassName(), out.target, out.params);
|
||||
|
||||
// color pass is typically heavy, and we don't have much CPU work left after
|
||||
// this point, so flushing now allows us to start the GPU earlier and reduce
|
||||
// latency, without creating bubbles.
|
||||
driver.flush();
|
||||
}
|
||||
);
|
||||
|
||||
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
|
||||
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
|
||||
|
||||
blackboard["color"] = output;
|
||||
return output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> RendererUtils::refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass) noexcept {
|
||||
|
||||
auto& blackboard = fg.getBlackboard();
|
||||
auto input = blackboard.get<FrameGraphTexture>("color");
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
|
||||
// find the first refractive object
|
||||
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
|
||||
[](auto const& command) {
|
||||
return (command.key & RenderPass::PASS_MASK) < uint64_t(RenderPass::Pass::REFRACT);
|
||||
});
|
||||
|
||||
const bool hasScreenSpaceRefraction =
|
||||
(refraction->key & RenderPass::PASS_MASK) == uint64_t(RenderPass::Pass::REFRACT);
|
||||
|
||||
// if there wasn't any refractive object, just skip everything below.
|
||||
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
|
||||
PostProcessManager& ppm = engine.getPostProcessManager();
|
||||
|
||||
// clear the color/depth buffers, which will orphan (and cull) the color pass
|
||||
input.clear();
|
||||
blackboard.remove("color");
|
||||
blackboard.remove("depth");
|
||||
|
||||
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
|
||||
// When rendering the opaques, we need to conserve the sample buffer,
|
||||
// so create config that specifies the sample count.
|
||||
.width = config.width,
|
||||
.height = config.height,
|
||||
.samples = config.msaa,
|
||||
.format = config.hdrFormat
|
||||
}, config, { .asSubpass = false },
|
||||
pass.getExecutor(pass.begin(), refraction));
|
||||
|
||||
// generate the mipmap chain
|
||||
PostProcessManager::generateMipmapSSR(ppm, fg,
|
||||
input, ssrConfig.refraction, true, ssrConfig);
|
||||
|
||||
// Now we're doing the refraction pass proper.
|
||||
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
|
||||
// automatically because these are set in the Blackboard (they were set by the opaque
|
||||
// pass). For this reason, `desc` below is only used in colorPass() for the width and
|
||||
// height.
|
||||
config.clearFlags = TargetBufferFlags::NONE;
|
||||
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view,
|
||||
{ .width = config.width, .height = config.height },
|
||||
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
|
||||
|
||||
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
|
||||
// We need to do a resolve here because later passes (such as color grading or DoF) will need
|
||||
// to sample from 'output'. However, because we have MSAA, we know we're not sampleable.
|
||||
// And this is because in the SSR case, we had to use a renderbuffer to conserve the
|
||||
// multi-sample buffer.
|
||||
output = ppm.resolveBaseLevel(fg, "Resolved Color Buffer", output);
|
||||
}
|
||||
} else {
|
||||
output = input;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void RendererUtils::readPixels(backend::DriverApi& driver, Handle<HwRenderTarget> renderTargetHandle,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) {
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(
|
||||
buffer.type != PixelDataType::COMPRESSED,
|
||||
"buffer.format cannot be COMPRESSED")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(
|
||||
buffer.alignment > 0 && buffer.alignment <= 8 &&
|
||||
!(buffer.alignment & (buffer.alignment - 1u)),
|
||||
"buffer.alignment must be 1, 2, 4 or 8")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's not really possible to know here which formats will be supported because
|
||||
// it can vary depending on the RenderTarget, in GL the following are ALWAYS supported though:
|
||||
// format: RGBA, RGBA_INTEGER
|
||||
// type: UBYTE, UINT, INT, FLOAT
|
||||
|
||||
const size_t sizeNeeded = PixelBufferDescriptor::computeDataSize(
|
||||
buffer.format, buffer.type,
|
||||
buffer.stride ? buffer.stride : width,
|
||||
buffer.top + height,
|
||||
buffer.alignment);
|
||||
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.size >= sizeNeeded,
|
||||
"Pixel buffer too small: has %u bytes, needs %u bytes", buffer.size, sizeNeeded)) {
|
||||
return;
|
||||
}
|
||||
|
||||
driver.readPixels(renderTargetHandle, xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_DETAILS_RENDERERUTILS_H
|
||||
#define TNT_FILAMENT_DETAILS_RENDERERUTILS_H
|
||||
|
||||
#include "PostProcessManager.h"
|
||||
#include "RenderPass.h"
|
||||
|
||||
#include "fg/FrameGraphId.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FRenderTarget;
|
||||
class FrameGraph;
|
||||
class FrameGraph;
|
||||
class FView;
|
||||
|
||||
class RendererUtils {
|
||||
public:
|
||||
|
||||
struct ColorPassConfig {
|
||||
// Rendering viewport width (e.g. scaled down viewport from dynamic resolution)
|
||||
uint32_t width;
|
||||
// Rendering viewport height (e.g. scaled down viewport from dynamic resolution)
|
||||
uint32_t height;
|
||||
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
|
||||
uint32_t xoffset;
|
||||
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
|
||||
uint32_t yoffset;
|
||||
// dynamic resolution scale
|
||||
math::float2 scale;
|
||||
// HDR format
|
||||
backend::TextureFormat hdrFormat;
|
||||
// MSAA sample count
|
||||
uint8_t msaa;
|
||||
// Clear flags
|
||||
backend::TargetBufferFlags clearFlags;
|
||||
// Clear color
|
||||
math::float4 clearColor = {};
|
||||
// Lod offset for the SSR passes
|
||||
float ssrLodOffset;
|
||||
// Contact shadow enabled?
|
||||
bool hasContactShadows;
|
||||
};
|
||||
|
||||
static FrameGraphId<FrameGraphTexture> colorPass(
|
||||
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor) noexcept;
|
||||
|
||||
static FrameGraphId<FrameGraphTexture> refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass) noexcept;
|
||||
|
||||
static void readPixels(backend::DriverApi& driver,
|
||||
backend::Handle<backend::HwRenderTarget> renderTargetHandle,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_DETAILS_RENDERERUTILS_H
|
||||
@@ -60,6 +60,9 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::terminate(FEngine& engine) {
|
||||
}
|
||||
|
||||
ShadowMap::~ShadowMap() {
|
||||
FEngine& engine = mEngine;
|
||||
Entity entities[] = { mCamera->getEntity(), mDebugCamera->getEntity() };
|
||||
@@ -70,9 +73,7 @@ ShadowMap::~ShadowMap() {
|
||||
}
|
||||
|
||||
void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo,
|
||||
RenderPass* const pass) noexcept {
|
||||
pass->setCamera(cameraInfo);
|
||||
FScene::VisibleMaskType visibilityMask, RenderPass* const pass) noexcept {
|
||||
pass->setVisibilityMask(visibilityMask);
|
||||
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
|
||||
@@ -320,7 +321,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
|
||||
if (params.options.stable) {
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
|
||||
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
|
||||
1.0f / mShadowMapInfo.shadowDimension);
|
||||
}
|
||||
|
||||
@@ -1048,13 +1049,12 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
|
||||
void ShadowMap::initSceneInfo(FScene const& scene, mat4f const& viewMatrix,
|
||||
ShadowMap::SceneInfo& sceneInfo) {
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
|
||||
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
|
||||
// computeShadowCameraDirectional() which takes this into account.
|
||||
const mat4f V = camera.view;
|
||||
|
||||
// Compute scene bounds in world space, as well as the light-space and view-space near/far planes
|
||||
sceneInfo.wsShadowCastersVolume = {};
|
||||
@@ -1071,7 +1071,7 @@ void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& c
|
||||
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
|
||||
sceneInfo.wsShadowReceiversVolume.max =
|
||||
max(sceneInfo.wsShadowReceiversVolume.max, receiver.max);
|
||||
float2 nf = ShadowMap::computeNearFar(V, receiver);
|
||||
float2 nf = ShadowMap::computeNearFar(viewMatrix, receiver);
|
||||
sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x);
|
||||
sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
~ShadowMap();
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
struct ShadowMapInfo {
|
||||
// the dimension of the encompassing texture atlas
|
||||
uint16_t atlasDimension = 0;
|
||||
@@ -105,8 +107,7 @@ public:
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo,
|
||||
RenderPass* pass) noexcept;
|
||||
FScene::VisibleMaskType visibilityMask, RenderPass* const pass) noexcept;
|
||||
|
||||
// Do we have visible shadows. Valid after calling update().
|
||||
bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; }
|
||||
@@ -129,7 +130,7 @@ public:
|
||||
|
||||
// Call once per frame to populate the SceneInfo struct, then pass to update().
|
||||
// This computes values constant across all shadow maps.
|
||||
static void initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
|
||||
static void initSceneInfo(FScene const& scene, math::mat4f const& viewMatrix,
|
||||
ShadowMap::SceneInfo& sceneInfo);
|
||||
|
||||
// Update SceneInfo struct for a given light
|
||||
|
||||
@@ -40,6 +40,10 @@ ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguideline
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
new (&entry) ShadowMap(engine);
|
||||
}
|
||||
|
||||
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
|
||||
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
|
||||
&engine.debug.shadowmap.visualize_cascades);
|
||||
@@ -47,18 +51,26 @@ ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguideline
|
||||
&engine.debug.shadowmap.tightly_bound_scene);
|
||||
}
|
||||
|
||||
void ShadowMapManager::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyBufferObject(mShadowUbh);
|
||||
UTILS_NOUNROLL
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
|
||||
}
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
// destroy the ShadowMap array in-place
|
||||
UTILS_NOUNROLL
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
std::launder(reinterpret_cast<ShadowMap*>(&entry))->~ShadowMap();
|
||||
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
|
||||
}
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
FEngine& engine, FView& view,
|
||||
TypedUniformBuffer<ShadowUib>& shadowUb, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
|
||||
CameraInfo const& cameraInfo,
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
|
||||
ShadowTechnique shadowTechnique = {};
|
||||
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
@@ -66,15 +78,19 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers());
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
ShadowMap::initSceneInfo(
|
||||
*view.getScene(), view.getCameraInfo(),
|
||||
sceneInfo);
|
||||
ShadowMap::initSceneInfo(*view.getScene(), cameraInfo.view, sceneInfo);
|
||||
|
||||
shadowTechnique |= updateCascadeShadowMaps(
|
||||
engine, view, renderableData, lightData, sceneInfo);
|
||||
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
|
||||
|
||||
shadowTechnique |= updateSpotShadowMaps(
|
||||
engine, view, renderableData, lightData, sceneInfo, shadowUb);
|
||||
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
|
||||
|
||||
if (mShadowUb.isDirty()) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.updateBufferObject(mShadowUbh,
|
||||
mShadowUb.toBufferDescriptor(driver), 0);
|
||||
}
|
||||
|
||||
return shadowTechnique;
|
||||
}
|
||||
@@ -169,8 +185,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
.format = view.hasVSM() ? vsmTextureFormat : mTextureFormat
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { });
|
||||
|
||||
[&view](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
// set uniforms needed to render this ShadowMap
|
||||
view.prepareShadowMap();
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -180,12 +198,11 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
|
||||
struct ShadowPassData {
|
||||
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
uint32_t blurRt{};
|
||||
uint32_t shadowRt{};
|
||||
};
|
||||
|
||||
auto shadows = prepareShadowPass.getData().shadows;
|
||||
|
||||
auto& ppm = engine.getPostProcessManager();
|
||||
|
||||
for (auto const& entry : passList) {
|
||||
@@ -198,7 +215,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
|
||||
FrameGraphRenderPass::Descriptor renderTargetDesc{};
|
||||
|
||||
auto attachment = builder.createSubresource(prepareShadowPass->shadows,
|
||||
data.output = builder.createSubresource(prepareShadowPass->shadows,
|
||||
"Shadowmap Layer", { .layer = layer });
|
||||
|
||||
if (view.hasVSM()) {
|
||||
@@ -223,10 +240,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
depth = builder.write(depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
attachment = builder.write(attachment,
|
||||
data.output = builder.write(data.output,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
|
||||
renderTargetDesc.attachments.color[0] = attachment;
|
||||
renderTargetDesc.attachments.color[0] = data.output;
|
||||
renderTargetDesc.attachments.depth = depth;
|
||||
renderTargetDesc.clearFlags =
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
|
||||
@@ -250,9 +267,9 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
}
|
||||
} else {
|
||||
// the shadowmap layer
|
||||
attachment = builder.write(attachment,
|
||||
data.output = builder.write(data.output,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
renderTargetDesc.attachments.depth = attachment;
|
||||
renderTargetDesc.attachments.depth = data.output;
|
||||
renderTargetDesc.clearFlags = TargetBufferFlags::DEPTH;
|
||||
}
|
||||
|
||||
@@ -270,7 +287,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
RenderPass entryPass(pass);
|
||||
shadowMap.render(*scene, entry.range, entry.visibilityMask, cameraInfo, &entryPass);
|
||||
shadowMap.render(*scene, entry.range, entry.visibilityMask, &entryPass);
|
||||
|
||||
const auto& executor = entryPass.getExecutor();
|
||||
const bool blur = view.hasVSM() && options->vsm.blurWidth > 0.0f;
|
||||
@@ -289,12 +306,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
// than clearing.
|
||||
const uint32_t dim = options->mapSize;
|
||||
filament::Viewport viewport{ 1, 1, dim - 2, dim - 2 };
|
||||
view.prepareViewport(viewport);
|
||||
|
||||
// set uniforms needed to render this ShadowMap
|
||||
// Currently these uniforms are owned by View and are global, but eventually
|
||||
// this will set a separate per shadowmap UBO
|
||||
view.prepareShadowMap();
|
||||
view.prepareViewport(viewport, 0, 0);
|
||||
|
||||
view.commitUniforms(driver);
|
||||
|
||||
@@ -317,7 +329,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
const float ratio = float(kernelWidth + 1) / sigma;
|
||||
ppm.gaussianBlurPass(fg,
|
||||
shadowPass->tempBlurSrc,
|
||||
shadows,
|
||||
shadowPass->output,
|
||||
false, kernelWidth, ratio);
|
||||
}
|
||||
|
||||
@@ -327,21 +339,20 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
|
||||
if (textureRequirements.levels > 1) {
|
||||
for (size_t level = 0; level < textureRequirements.levels - 1; level++) {
|
||||
const bool finalize = level == textureRequirements.levels - 2;
|
||||
shadows = ppm.vsmMipmapPass(fg, shadows, layer, level,
|
||||
ppm.vsmMipmapPass(fg, prepareShadowPass->shadows, layer, level,
|
||||
vsmClearColor, finalize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shadows;
|
||||
return prepareShadowPass->shadows;
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo& sceneInfo) noexcept {
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept {
|
||||
FScene* scene = view.getScene();
|
||||
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
FLightManager::Instance directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
|
||||
@@ -365,7 +376,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMapEntry& entry = mCascadeShadowMaps[0];
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
|
||||
shadowMap.updateDirectional(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
shadowMap.updateDirectional(lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
@@ -384,8 +395,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -viewingCameraInfo.zn;
|
||||
float vsFar = -viewingCameraInfo.zf;
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
@@ -402,7 +413,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
const CascadeSplits::Params p {
|
||||
.proj = viewingCameraInfo.cullingProjection,
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
@@ -440,7 +451,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
shadowMap.updateDirectional(lightData, 0,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
cameraInfo, shadowMapInfo,
|
||||
*scene, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
@@ -479,11 +490,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept {
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept {
|
||||
|
||||
auto& lcm = engine.getLightManager();
|
||||
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
|
||||
|
||||
// shadow-map shadows for point/spotlights
|
||||
ShadowTechnique shadowTechnique{};
|
||||
@@ -529,7 +539,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
shadowMap.updateSpot(lightData, lightIndex,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
cameraInfo, shadowMapInfo,
|
||||
*view.getScene(), sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
@@ -541,7 +551,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = shadowUb.edit();
|
||||
auto& s = mShadowUb.edit();
|
||||
const float n = shadowMap.getCamera().getNear();
|
||||
const float f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
explicit ShadowMapManager(FEngine& engine);
|
||||
~ShadowMapManager();
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
// Reset shadow map layout.
|
||||
void reset() noexcept;
|
||||
|
||||
@@ -82,8 +84,8 @@ public:
|
||||
|
||||
// Updates all of the shadow maps and performs culling.
|
||||
// Returns true if any of the shadow maps have visible shadows.
|
||||
ShadowTechnique update(FEngine& engine, FView& view,
|
||||
TypedUniformBuffer<ShadowUib>& shadowUb,
|
||||
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
|
||||
CameraInfo const& cameraInfo,
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
// Renders all the shadow maps.
|
||||
@@ -101,7 +103,8 @@ public:
|
||||
|
||||
ShadowMap* getSpotShadowMap(size_t spot) noexcept {
|
||||
assert_invariant(spot < CONFIG_MAX_SHADOW_CASTING_SPOTS);
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + spot]));
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(
|
||||
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + spot]));
|
||||
}
|
||||
|
||||
ShadowMap const* getSpotShadowMap(size_t spot) const noexcept {
|
||||
@@ -113,14 +116,18 @@ public:
|
||||
return mShadowMappingUniforms;
|
||||
}
|
||||
|
||||
private:
|
||||
ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo& sceneInfo) noexcept;
|
||||
auto& getShadowUniforms() const { return mShadowUb; }
|
||||
|
||||
ShadowTechnique updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept;
|
||||
auto& getShadowUniformsHandle() const { return mShadowUbh; }
|
||||
|
||||
private:
|
||||
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
@@ -205,6 +212,9 @@ private:
|
||||
// TODO: iOS does not support the DEPTH16 texture format.
|
||||
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
|
||||
|
||||
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
|
||||
backend::Handle<backend::HwBufferObject> mShadowUbh;
|
||||
|
||||
ShadowMappingUniforms mShadowMappingUniforms;
|
||||
|
||||
utils::FixedCapacityVector<ShadowMapEntry> mCascadeShadowMaps{
|
||||
|
||||
@@ -36,11 +36,6 @@ void Stream::setDimensions(uint32_t width, uint32_t height) noexcept {
|
||||
upcast(this)->setDimensions(width, height);
|
||||
}
|
||||
|
||||
void Stream::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) noexcept {
|
||||
upcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
int64_t Stream::getTimestamp() const noexcept {
|
||||
return upcast(this)->getTimestamp();
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ math::mat4 FCamera::getProjectionMatrix() const noexcept {
|
||||
// Note that this math ends up setting the projection matrix' p33 to 0, which is where we're
|
||||
// getting back a lot of precision in the depth buffer.
|
||||
const mat4 m{ mat4::row_major_init{
|
||||
mScaling.x, 0.0, 0.0, mShiftCS.x,
|
||||
0.0, mScaling.y, 0.0, mShiftCS.y,
|
||||
mScalingCS.x, 0.0, 0.0, mShiftCS.x,
|
||||
0.0, mScalingCS.y, 0.0, mShiftCS.y,
|
||||
0.0, 0.0, -0.5, 0.5, // GL to inverted DX convention
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
}};
|
||||
@@ -167,8 +167,8 @@ math::mat4 FCamera::getProjectionMatrix() const noexcept {
|
||||
math::mat4 FCamera::getCullingProjectionMatrix() const noexcept {
|
||||
// The culling projection matrix stays in the GL convention
|
||||
const mat4 m{ mat4::row_major_init{
|
||||
mScaling.x, 0.0, 0.0, mShiftCS.x,
|
||||
0.0, mScaling.y, 0.0, mShiftCS.y,
|
||||
mScalingCS.x, 0.0, 0.0, mShiftCS.x,
|
||||
0.0, mScalingCS.y, 0.0, mShiftCS.y,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
}};
|
||||
@@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
|
||||
zn = camera.getNear();
|
||||
zf = camera.getCullingFar();
|
||||
ev100 = Exposure::ev100(camera);
|
||||
f = camera.getFocalLength();
|
||||
f = (float)camera.getFocalLength();
|
||||
A = f / camera.getAperture();
|
||||
d = std::max(zn, camera.getFocusDistance());
|
||||
}
|
||||
@@ -286,14 +286,13 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer
|
||||
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
|
||||
model = mat4f{ modelMatrix };
|
||||
view = mat4f{ inverse(modelMatrix) };
|
||||
worldOrigin = worldOriginCamera;
|
||||
zn = camera.getNear();
|
||||
zf = camera.getCullingFar();
|
||||
ev100 = Exposure::ev100(camera);
|
||||
f = camera.getFocalLength();
|
||||
f = (float)camera.getFocalLength();
|
||||
A = f / camera.getAperture();
|
||||
d = std::max(zn, camera.getFocusDistance());
|
||||
worldOffset = camera.getPosition();
|
||||
worldOrigin = mat4f{ worldOriginCamera };
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -63,9 +63,9 @@ public:
|
||||
void setCustomProjection(math::mat4 const& projection,
|
||||
math::mat4 const& projectionForCulling, double near, double far) noexcept;
|
||||
|
||||
void setScaling(math::double2 scaling) noexcept { mScaling = scaling; }
|
||||
void setScaling(math::double2 scaling) noexcept { mScalingCS = scaling; }
|
||||
|
||||
math::double4 getScaling() const noexcept { return math::double4{ mScaling, 1.0, 1.0 }; }
|
||||
math::double4 getScaling() const noexcept { return math::double4{ mScalingCS, 1.0, 1.0 }; }
|
||||
|
||||
void setShift(math::double2 shift) noexcept { mShiftCS = shift * 2.0; }
|
||||
|
||||
@@ -189,8 +189,8 @@ private:
|
||||
|
||||
math::mat4 mProjection; // projection matrix (infinite far)
|
||||
math::mat4 mProjectionForCulling; // projection matrix (with far plane)
|
||||
math::double2 mScaling = { 1.0f }; // additional scaling applied to projection
|
||||
math::double2 mShiftCS = { 0.0f }; // additional translation applied to projection
|
||||
math::double2 mScalingCS = { 1.0 }; // additional scaling applied to projection
|
||||
math::double2 mShiftCS = { 0.0 }; // additional translation applied to projection
|
||||
|
||||
float mNear{};
|
||||
float mFar{};
|
||||
@@ -209,19 +209,18 @@ struct CameraInfo {
|
||||
math::mat4f projection; // projection matrix for drawing (infinite zfar)
|
||||
math::mat4f cullingProjection; // projection matrix for culling
|
||||
math::mat4f model; // camera model matrix
|
||||
math::mat4f view; // camera view matrix
|
||||
math::mat4f view; // camera view matrix (inverse(model))
|
||||
math::mat4 worldOrigin; // world origin transform (already applied to model and view)
|
||||
float zn{}; // distance (positive) to the near plane
|
||||
float zf{}; // distance (positive) to the far plane
|
||||
float ev100{}; // exposure
|
||||
float f{}; // focal length [m]
|
||||
float A{}; // f-number or f / aperture diameter [m]
|
||||
float d{}; // focus distance [m]
|
||||
math::float3 worldOffset{}; // world offset, API-level camera position
|
||||
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
|
||||
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
|
||||
|
||||
// for debugging:
|
||||
math::mat4f worldOrigin; // this is already applied to model and view
|
||||
math::float3 getWorldOffset() const noexcept { return -worldOrigin[3].xyz; }
|
||||
math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; }
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(Camera)
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include "MaterialParser.h"
|
||||
#include "ResourceAllocator.h"
|
||||
|
||||
#include "DFG.h"
|
||||
#include "RenderPrimitive.h"
|
||||
|
||||
#include "details/BufferObject.h"
|
||||
@@ -82,6 +80,7 @@ FEngine* FEngine::create(Backend backend, Platform* platform, void* sharedGLCont
|
||||
}
|
||||
if (platform == nullptr) {
|
||||
slog.e << "Selected backend not supported in this build." << io::endl;
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
instance->mDriver = platform->createDriver(sharedGLContext);
|
||||
@@ -95,6 +94,7 @@ FEngine* FEngine::create(Backend backend, Platform* platform, void* sharedGLCont
|
||||
if (UTILS_UNLIKELY(!instance->mDriver)) {
|
||||
// something went horribly wrong during driver initialization
|
||||
instance->mDriverThread.join();
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,7 @@ FEngine* FEngine::getEngine(void* token) {
|
||||
if (UTILS_UNLIKELY(!instance->mDriver)) {
|
||||
// something went horribly wrong during driver initialization
|
||||
instance->mDriverThread.join();
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -333,19 +334,11 @@ void FEngine::init() {
|
||||
|
||||
mPostProcessManager.init();
|
||||
mLightManager.init(*this);
|
||||
mDFG = std::make_unique<DFG>(*this);
|
||||
mDFG.init(*this);
|
||||
}
|
||||
|
||||
FEngine::~FEngine() noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
ASSERT_DESTRUCTOR(mTerminated, "Engine destroyed but not terminated!");
|
||||
|
||||
// we use mResourceAllocator as a proxy for knowing if init() was called
|
||||
if (mResourceAllocator) {
|
||||
std::destroy_at(std::launder(reinterpret_cast<DriverApi*>(&mDriverApiStorage)));
|
||||
}
|
||||
|
||||
delete mResourceAllocator;
|
||||
delete mDriver;
|
||||
if (mOwnPlatform) {
|
||||
@@ -356,6 +349,9 @@ FEngine::~FEngine() noexcept {
|
||||
void FEngine::shutdown() {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
// by construction this should never be nullptr
|
||||
assert_invariant(mResourceAllocator);
|
||||
|
||||
ASSERT_PRECONDITION(ThreadUtils::isThisThread(mMainThreadId),
|
||||
"Engine::shutdown() called from the wrong thread!");
|
||||
|
||||
@@ -375,7 +371,7 @@ void FEngine::shutdown() {
|
||||
|
||||
mPostProcessManager.terminate(driver); // free-up post-process manager resources
|
||||
mResourceAllocator->terminate();
|
||||
mDFG->terminate(); // free-up the DFG
|
||||
mDFG.terminate(*this); // free-up the DFG
|
||||
mRenderableManager.terminate(); // free-up all renderables
|
||||
mLightManager.terminate(); // free-up all lights
|
||||
mCameraManager.terminate(); // free-up all cameras
|
||||
@@ -452,14 +448,15 @@ void FEngine::shutdown() {
|
||||
// These callbacks CANNOT call driver APIs.
|
||||
getDriver().purge();
|
||||
|
||||
// and destroy the CommandStream
|
||||
std::destroy_at(std::launder(reinterpret_cast<DriverApi*>(&mDriverApiStorage)));
|
||||
|
||||
/*
|
||||
* Terminate the JobSystem...
|
||||
*/
|
||||
|
||||
// detach this thread from the jobsystem
|
||||
// detach this thread from the JobSystem
|
||||
mJobSystem.emancipate();
|
||||
|
||||
mTerminated = true;
|
||||
}
|
||||
|
||||
void FEngine::prepare() {
|
||||
@@ -698,7 +695,6 @@ FRenderer* FEngine::createRenderer() noexcept {
|
||||
FRenderer* p = mHeapAllocator.make<FRenderer>(*this);
|
||||
if (p) {
|
||||
mRenderers.insert(p);
|
||||
p->init();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "upcast.h"
|
||||
|
||||
#include "Allocators.h"
|
||||
#include "DFG.h"
|
||||
#include "PostProcessManager.h"
|
||||
#include "ResourceList.h"
|
||||
|
||||
@@ -97,7 +98,6 @@ class FScene;
|
||||
class FSwapChain;
|
||||
class FView;
|
||||
|
||||
class DFG;
|
||||
class ResourceAllocator;
|
||||
|
||||
/*
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
return *std::launder(reinterpret_cast<DriverApi*>(&mDriverApiStorage));
|
||||
}
|
||||
|
||||
DFG* getDFG() const noexcept { return mDFG.get(); }
|
||||
DFG const& getDFG() const noexcept { return mDFG; }
|
||||
|
||||
// the per-frame Area is used by all Renderer, so they must run in sequence and
|
||||
// have freed all allocated memory when done. If this needs to change in the future,
|
||||
@@ -383,7 +383,6 @@ private:
|
||||
Platform* mPlatform = nullptr;
|
||||
bool mOwnPlatform = false;
|
||||
void* mSharedGLContext = nullptr;
|
||||
bool mTerminated = false;
|
||||
backend::Handle<backend::HwRenderPrimitive> mFullScreenTriangleRph;
|
||||
FVertexBuffer* mFullScreenTriangleVb = nullptr;
|
||||
FIndexBuffer* mFullScreenTriangleIb = nullptr;
|
||||
@@ -424,7 +423,7 @@ private:
|
||||
// FMaterialInstance are handled directly by FMaterial
|
||||
std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>> mMaterialInstances;
|
||||
|
||||
std::unique_ptr<DFG> mDFG;
|
||||
DFG mDFG;
|
||||
|
||||
std::thread mDriverThread;
|
||||
backend::CommandBufferQueue mCommandBufferQueue;
|
||||
|
||||
@@ -139,17 +139,32 @@ FRenderTarget::FRenderTarget(FEngine& engine, const RenderTarget::Builder& build
|
||||
}
|
||||
};
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
if (mAttachments[i].texture) {
|
||||
mAttachmentMask |= getTargetBufferFlagsAt(i);
|
||||
Attachment const& attachment = mAttachments[i];
|
||||
if (attachment.texture) {
|
||||
TargetBufferFlags targetBufferBit = getTargetBufferFlagsAt(i);
|
||||
mAttachmentMask |= targetBufferBit;
|
||||
setAttachment(mrt[i], (AttachmentPoint)i);
|
||||
if (any(attachment.texture->getUsage() &
|
||||
(TextureUsage::SAMPLEABLE | Texture::Usage::SUBPASS_INPUT))) {
|
||||
mSampleableAttachmentsMask |= targetBufferBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mAttachments[(size_t)AttachmentPoint::DEPTH].texture) {
|
||||
|
||||
Attachment const& depthAttachment = mAttachments[(size_t)AttachmentPoint::DEPTH];
|
||||
if (depthAttachment.texture) {
|
||||
mAttachmentMask |= TargetBufferFlags::DEPTH;
|
||||
setAttachment(dinfo, AttachmentPoint::DEPTH);
|
||||
if (any(depthAttachment.texture->getUsage() &
|
||||
(TextureUsage::SAMPLEABLE | Texture::Usage::SUBPASS_INPUT))) {
|
||||
mSampleableAttachmentsMask |= TargetBufferFlags::DEPTH;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add stencil here when we support it
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createRenderTarget(mAttachmentMask,
|
||||
builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples, mrt, dinfo, {});
|
||||
|
||||
@@ -55,6 +55,10 @@ public:
|
||||
return mAttachmentMask;
|
||||
}
|
||||
|
||||
backend::TargetBufferFlags getSampleableAttachmentsMask() const noexcept {
|
||||
return mSampleableAttachmentsMask;
|
||||
}
|
||||
|
||||
uint8_t getSupportedColorAttachmentsCount() const noexcept {
|
||||
return mSupportedColorAttachmentsCount;
|
||||
}
|
||||
@@ -67,6 +71,7 @@ private:
|
||||
Attachment mAttachments[ATTACHMENT_COUNT];
|
||||
HwHandle mHandle{};
|
||||
backend::TargetBufferFlags mAttachmentMask = {};
|
||||
backend::TargetBufferFlags mSampleableAttachmentsMask = {};
|
||||
const uint8_t mSupportedColorAttachmentsCount;
|
||||
};
|
||||
|
||||
|
||||
+438
-628
File diff suppressed because it is too large
Load Diff
@@ -33,17 +33,13 @@
|
||||
#include <fg/FrameGraphTexture.h>
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
#include <filament/View.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PresentCallable.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
|
||||
@@ -53,12 +49,9 @@ namespace backend {
|
||||
class Driver;
|
||||
} // namespace backend
|
||||
|
||||
class View;
|
||||
|
||||
class FEngine;
|
||||
class FRenderTarget;
|
||||
class FView;
|
||||
class ShadowMap;
|
||||
|
||||
/*
|
||||
* A concrete implementation of the Renderer Interface.
|
||||
@@ -68,39 +61,42 @@ class FRenderer : public Renderer {
|
||||
|
||||
public:
|
||||
explicit FRenderer(FEngine& engine);
|
||||
|
||||
~FRenderer() noexcept;
|
||||
|
||||
void init() noexcept;
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
FEngine& getEngine() const noexcept { return mEngine; }
|
||||
|
||||
math::float4 getShaderUserTime() const { return mShaderUserTime; }
|
||||
|
||||
// do all the work here!
|
||||
void renderJob(ArenaScope& arena, FView& view);
|
||||
void resetUserTime();
|
||||
|
||||
// renders a single standalone view. The view must have a a custom rendertarget.
|
||||
void renderStandaloneView(FView const* view);
|
||||
|
||||
// start a frame
|
||||
bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano);
|
||||
|
||||
// end a frame
|
||||
void endFrame();
|
||||
|
||||
// render a view. must be called between beginFrame/enfFrame.
|
||||
void render(FView const* view);
|
||||
|
||||
// read pixel from the current swapchain. must be called between beginFrame/enfFrame.
|
||||
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
void copyFrame(FSwapChain* dstSwapChain, Viewport const& dstViewport,
|
||||
Viewport const& srcViewport, CopyFrameFlag flags);
|
||||
|
||||
void endFrame();
|
||||
|
||||
void renderStandaloneView(FView const* view);
|
||||
|
||||
// read pixel from a rendertarget. must be called between beginFrame/enfFrame.
|
||||
void readPixels(FRenderTarget* renderTarget,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
void resetUserTime();
|
||||
// blits the current swapchain to another one
|
||||
void copyFrame(FSwapChain* dstSwapChain, Viewport const& dstViewport,
|
||||
Viewport const& srcViewport, CopyFrameFlag flags);
|
||||
|
||||
// Clean-up everything, this is typically called when the client calls Engine::destroyRenderer()
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void setDisplayInfo(DisplayInfo const& info) noexcept {
|
||||
mDisplayInfo = info;
|
||||
@@ -131,83 +127,45 @@ public:
|
||||
private:
|
||||
friend class Renderer;
|
||||
using Command = RenderPass::Command;
|
||||
using clock = std::chrono::steady_clock;
|
||||
using Epoch = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
|
||||
void initializeClearFlags();
|
||||
|
||||
backend::TextureFormat getHdrFormat(const FView& view, bool translucent) const noexcept;
|
||||
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
|
||||
|
||||
Epoch getUserEpoch() const { return mUserEpoch; }
|
||||
duration getUserTime() const noexcept { return clock::now() - getUserEpoch(); }
|
||||
|
||||
void getRenderTarget(FView const& view,
|
||||
backend::TargetBufferFlags& outAttachementMask,
|
||||
backend::Handle<backend::HwRenderTarget>& outTarget) const noexcept;
|
||||
|
||||
void readPixels(backend::Handle<backend::HwRenderTarget> renderTargetHandle,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
void renderInternal(FView const* view);
|
||||
|
||||
struct ColorPassConfig {
|
||||
// User Viewport
|
||||
Viewport vp;
|
||||
// Scaled (down) viewport from dynamic resolution
|
||||
Viewport svp;
|
||||
// dynamic resolution scale
|
||||
math::float2 scale;
|
||||
// HDR format
|
||||
backend::TextureFormat hdrFormat;
|
||||
// MSAA sample count
|
||||
uint8_t msaa;
|
||||
// Clear flags
|
||||
backend::TargetBufferFlags clearFlags;
|
||||
// Clear color
|
||||
math::float4 clearColor = {};
|
||||
// Lod offset for the SSR passes
|
||||
float ssrLodOffset;
|
||||
// Contact shadow enabled?
|
||||
bool hasContactShadows;
|
||||
};
|
||||
|
||||
FrameGraphId<FrameGraphTexture> colorPass(FrameGraph& fg, const char* name,
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor, FView const& view) const noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> refractionPass(FrameGraph& fg,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass, FView const& view) const noexcept;
|
||||
|
||||
void recordHighWatermark(size_t watermark) noexcept {
|
||||
mCommandsHighWatermark = std::max(mCommandsHighWatermark, watermark);
|
||||
}
|
||||
|
||||
size_t getCommandsHighWatermark() const noexcept {
|
||||
return mCommandsHighWatermark * sizeof(RenderPass::Command);
|
||||
return mCommandsHighWatermark;
|
||||
}
|
||||
|
||||
backend::TextureFormat getHdrFormat(const View& view, bool translucent) const noexcept;
|
||||
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
|
||||
|
||||
void initializeClearFlags();
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using Epoch = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
|
||||
Epoch getUserEpoch() const { return mUserEpoch; }
|
||||
duration getUserTime() const noexcept {
|
||||
return clock::now() - getUserEpoch();
|
||||
}
|
||||
void renderInternal(FView const* view);
|
||||
void renderJob(ArenaScope& arena, FView& view);
|
||||
|
||||
// keep a reference to our engine
|
||||
FEngine& mEngine;
|
||||
FrameSkipper mFrameSkipper;
|
||||
backend::Handle<backend::HwRenderTarget> mRenderTarget;
|
||||
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
|
||||
FSwapChain* mSwapChain = nullptr;
|
||||
size_t mCommandsHighWatermark = 0;
|
||||
uint32_t mFrameId = 0;
|
||||
uint32_t mViewRenderedCount = 0;
|
||||
FrameInfoManager mFrameInfoManager;
|
||||
backend::TextureFormat mHdrTranslucent{};
|
||||
backend::TextureFormat mHdrQualityMedium{};
|
||||
backend::TextureFormat mHdrQualityHigh{};
|
||||
backend::TextureFormat mHdrTranslucent;
|
||||
backend::TextureFormat mHdrQualityMedium;
|
||||
backend::TextureFormat mHdrQualityHigh;
|
||||
bool mIsRGB8Supported : 1;
|
||||
Epoch mUserEpoch;
|
||||
math::float4 mShaderUserTime{};
|
||||
|
||||
@@ -33,7 +33,6 @@ using namespace backend;
|
||||
|
||||
struct Stream::BuilderDetails {
|
||||
void* mStream = nullptr;
|
||||
intptr_t mExternalTextureId = 0;
|
||||
uint32_t mWidth = 0;
|
||||
uint32_t mHeight = 0;
|
||||
};
|
||||
@@ -52,11 +51,6 @@ Stream::Builder& Stream::Builder::stream(void* stream) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Stream::Builder& Stream::Builder::stream(intptr_t externalTextureId) noexcept {
|
||||
mImpl->mExternalTextureId = externalTextureId;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Stream::Builder& Stream::Builder::width(uint32_t width) noexcept {
|
||||
mImpl->mWidth = width;
|
||||
return *this;
|
||||
@@ -68,11 +62,6 @@ Stream::Builder& Stream::Builder::height(uint32_t height) noexcept {
|
||||
}
|
||||
|
||||
Stream* Stream::Builder::build(Engine& engine) {
|
||||
if (!ASSERT_PRECONDITION_NON_FATAL(!mImpl->mStream || !mImpl->mExternalTextureId,
|
||||
"One and only one of the stream or external texture can be specified")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return upcast(engine).createStream(*this);
|
||||
}
|
||||
|
||||
@@ -80,21 +69,14 @@ Stream* Stream::Builder::build(Engine& engine) {
|
||||
|
||||
FStream::FStream(FEngine& engine, const Builder& builder) noexcept
|
||||
: mEngine(engine),
|
||||
mStreamType(
|
||||
builder->mExternalTextureId ? StreamType::TEXTURE_ID :
|
||||
(builder->mStream ? StreamType::NATIVE : StreamType::ACQUIRED)
|
||||
),
|
||||
mStreamType(builder->mStream ? StreamType::NATIVE : StreamType::ACQUIRED),
|
||||
mNativeStream(builder->mStream),
|
||||
mExternalTextureId(builder->mExternalTextureId),
|
||||
mWidth(builder->mWidth),
|
||||
mHeight(builder->mHeight) {
|
||||
|
||||
if (mNativeStream) {
|
||||
// Note: this is a synchronous call. On Android, this calls back into Java.
|
||||
mStreamHandle = engine.getDriverApi().createStreamNative(mNativeStream);
|
||||
} else if (mExternalTextureId) {
|
||||
mStreamHandle = engine.getDriverApi().createStreamFromTextureId(
|
||||
mExternalTextureId, mWidth, mHeight);
|
||||
} else {
|
||||
mStreamHandle = engine.getDriverApi().createStreamAcquired();
|
||||
}
|
||||
@@ -124,28 +106,6 @@ void FStream::setDimensions(uint32_t width, uint32_t height) noexcept {
|
||||
mEngine.getDriverApi().setStreamDimensions(mStreamHandle, mWidth, mHeight);
|
||||
}
|
||||
|
||||
void FStream::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) noexcept {
|
||||
if (getStreamType() == StreamType::TEXTURE_ID) {
|
||||
// this works only on external texture id streams
|
||||
|
||||
const size_t sizeNeeded = PixelBufferDescriptor::computeDataSize(
|
||||
buffer.format, buffer.type,
|
||||
buffer.stride ? buffer.stride : width,
|
||||
buffer.top + height,
|
||||
buffer.alignment);
|
||||
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.size >= sizeNeeded,
|
||||
"buffer.size too small %u bytes, needed %u bytes", buffer.size, sizeNeeded)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEngine::DriverApi& driver = mEngine.getDriverApi();
|
||||
driver.readStreamPixels(mStreamHandle,
|
||||
xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FStream::getTimestamp() const noexcept {
|
||||
FEngine::DriverApi& driver = mEngine.getDriverApi();
|
||||
return driver.getStreamTimestamp(mStreamHandle);
|
||||
|
||||
@@ -41,9 +41,6 @@ public:
|
||||
|
||||
void setDimensions(uint32_t width, uint32_t height) noexcept;
|
||||
|
||||
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) noexcept;
|
||||
|
||||
StreamType getStreamType() const noexcept { return mStreamType; }
|
||||
|
||||
uint32_t getWidth() const noexcept { return mWidth; }
|
||||
@@ -57,7 +54,6 @@ private:
|
||||
const StreamType mStreamType;
|
||||
backend::Handle<backend::HwStream> mStreamHandle;
|
||||
void* mNativeStream = nullptr;
|
||||
intptr_t mExternalTextureId;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
};
|
||||
|
||||
@@ -92,9 +92,6 @@ FView::FView(FEngine& engine)
|
||||
mLightUbh = driver.createBufferObject(CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
|
||||
|
||||
mShadowUbh = driver.createBufferObject(mShadowUb.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
|
||||
|
||||
mIsDynamicResolutionSupported = driver.isFrameTimeSupported();
|
||||
|
||||
mDefaultColorGrading = mColorGrading = engine.getDefaultColorGrading();
|
||||
@@ -114,9 +111,9 @@ void FView::terminate(FEngine& engine) {
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyBufferObject(mLightUbh);
|
||||
driver.destroyBufferObject(mShadowUbh);
|
||||
driver.destroyBufferObject(mRenderableUbh);
|
||||
drainFrameHistory(engine);
|
||||
mShadowMapManager.terminate(engine);
|
||||
mPerViewUniforms.terminate(driver);
|
||||
mFroxelizer.terminate(driver);
|
||||
}
|
||||
@@ -160,10 +157,6 @@ float2 FView::updateScale(FEngine& engine,
|
||||
Renderer::FrameRateOptions const& frameRateOptions,
|
||||
Renderer::DisplayInfo const& displayInfo) noexcept {
|
||||
|
||||
// scale factor returned to the caller is modified so the scaled viewport is rounded to
|
||||
// 8 pixels. The internal scale factor, mScale, doesn't have this rounding.
|
||||
float2 roundedScale = mScale;
|
||||
|
||||
DynamicResolutionOptions const& options = mDynamicResolution;
|
||||
if (options.enabled) {
|
||||
if (!UTILS_UNLIKELY(info.valid)) {
|
||||
@@ -241,14 +234,8 @@ float2 FView::updateScale(FEngine& engine,
|
||||
// (i.e. we clamped). This help not to have to wait too long for the Integral term
|
||||
// to kick in after a clamping event.
|
||||
mPidController.setIntegralInhibitionEnabled(mScale != s);
|
||||
|
||||
// now tweak the scaling factor to get multiples of 8 (to help quad-shading)
|
||||
// i.e. 8x8=64 fragments, to try to help with warp sizes.
|
||||
roundedScale.x = mScale.x == 1.0f ? 1.0f : (std::floor(mScale.x * float{ w } / 8) * 8) / float{ w };
|
||||
roundedScale.y = mScale.y == 1.0f ? 1.0f : (std::floor(mScale.y * float{ h } / 8) * 8) / float{ h };
|
||||
} else {
|
||||
mScale = 1.0f;
|
||||
roundedScale = 1.0f;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -270,7 +257,7 @@ float2 FView::updateScale(FEngine& engine,
|
||||
};
|
||||
#endif
|
||||
|
||||
return roundedScale;
|
||||
return mScale;
|
||||
}
|
||||
|
||||
void FView::setVisibleLayers(uint8_t select, uint8_t values) noexcept {
|
||||
@@ -283,7 +270,8 @@ bool FView::isSkyboxVisible() const noexcept {
|
||||
}
|
||||
|
||||
void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
CameraInfo const& cameraInfo) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
mHasShadowing = false;
|
||||
@@ -338,18 +326,17 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
|
||||
}
|
||||
}
|
||||
|
||||
auto shadowTechnique = mShadowMapManager.update(engine, *this,
|
||||
mShadowUb, renderableData, lightData);
|
||||
auto shadowTechnique = mShadowMapManager.update(engine, *this, cameraInfo,
|
||||
renderableData, lightData);
|
||||
|
||||
mHasShadowing = any(shadowTechnique);
|
||||
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
|
||||
}
|
||||
|
||||
void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena,
|
||||
filament::Viewport const& viewport) noexcept {
|
||||
filament::Viewport const& viewport, CameraInfo const& cameraInfo) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
const CameraInfo& camera = mViewingCameraInfo;
|
||||
FScene* const scene = mScene;
|
||||
auto const& lightData = scene->getLightData();
|
||||
|
||||
@@ -359,9 +346,10 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
|
||||
|
||||
mHasDynamicLighting = scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT;
|
||||
if (mHasDynamicLighting) {
|
||||
scene->prepareDynamicLights(camera, arena, mLightUbh);
|
||||
scene->prepareDynamicLights(cameraInfo, arena, mLightUbh);
|
||||
Froxelizer& froxelizer = mFroxelizer;
|
||||
if (froxelizer.prepare(driver, arena, viewport, camera.projection, camera.zn, camera.zf)) {
|
||||
if (froxelizer.prepare(driver, arena, viewport,
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
|
||||
// update our uniform buffer if needed
|
||||
mPerViewUniforms.prepareDynamicLights(mFroxelizer);
|
||||
}
|
||||
@@ -374,9 +362,9 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
|
||||
* Exposure
|
||||
*/
|
||||
|
||||
const float exposure = Exposure::exposure(camera.ev100);
|
||||
const float exposure = Exposure::exposure(cameraInfo.ev100);
|
||||
|
||||
mPerViewUniforms.prepareExposure(camera.ev100);
|
||||
mPerViewUniforms.prepareExposure(cameraInfo.ev100);
|
||||
|
||||
/*
|
||||
* Indirect light (IBL)
|
||||
@@ -406,16 +394,8 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
|
||||
mHasDirectionalLight = directionalLight.isValid();
|
||||
}
|
||||
|
||||
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
filament::Viewport const& viewport, float4 const& userTime, bool needsAlphaChannel) noexcept {
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
|
||||
/*
|
||||
* Prepare the scene -- this is where we gather all the objects added to the scene,
|
||||
* and in particular their world-space AABB.
|
||||
*/
|
||||
|
||||
FScene* const scene = getScene();
|
||||
CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
|
||||
FScene const* const scene = getScene();
|
||||
|
||||
/*
|
||||
* We apply a "world origin" to "everything" in order to implement the IBL rotation.
|
||||
@@ -444,20 +424,45 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
worldOriginScene[3].xyz -= camera->getPosition();
|
||||
}
|
||||
|
||||
// Note: for debugging (i.e. visualize what the camera / objects are doing, using
|
||||
// the viewing camera), we can set worldOriginScene to identity when mViewingCamera
|
||||
// is set
|
||||
mViewingCameraInfo = CameraInfo(*camera, worldOriginScene);
|
||||
return { *camera, worldOriginScene };
|
||||
}
|
||||
|
||||
mCullingFrustum = Frustum(mat4f{
|
||||
mCullingCamera->getCullingProjectionMatrix() *
|
||||
inverse(worldOriginScene * mCullingCamera->getModelMatrix()) });
|
||||
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
|
||||
float4 const& userTime, bool needsAlphaChannel) noexcept {
|
||||
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
|
||||
/*
|
||||
* Prepare the scene -- this is where we gather all the objects added to the scene,
|
||||
* and in particular their world-space AABB.
|
||||
*/
|
||||
|
||||
auto getFrustum = [this, &cameraInfo]() -> Frustum {
|
||||
if (UTILS_LIKELY(mViewingCamera == nullptr)) {
|
||||
// In the common case when we don't have a viewing camera, cameraInfo.view is
|
||||
// already the culling view matrix
|
||||
return Frustum{ mat4f{ highPrecisionMultiply(cameraInfo.projection, cameraInfo.view) }};
|
||||
} else {
|
||||
// Otherwise, we need to recalculate it from the culling camera.
|
||||
// Note: it is correct to always do the math from mCullingCamera, but it hides the
|
||||
// intent of the code, which is that we should only depend on CameraInfo here.
|
||||
// This is an extremely uncommon case.
|
||||
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
|
||||
const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix());
|
||||
return Frustum{ mat4f{ projection * view }};
|
||||
}
|
||||
};
|
||||
|
||||
const Frustum cullingFrustum = getFrustum();
|
||||
|
||||
FScene* const scene = getScene();
|
||||
|
||||
/*
|
||||
* Gather all information needed to render this scene. Apply the world origin to all
|
||||
* objects in the scene.
|
||||
*/
|
||||
scene->prepare(worldOriginScene, hasVSM());
|
||||
scene->prepare(cameraInfo.worldOrigin, hasVSM());
|
||||
|
||||
/*
|
||||
* Light culling: runs in parallel with Renderable culling (below)
|
||||
@@ -466,9 +471,9 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
JobSystem::Job* prepareVisibleLightsJob = nullptr;
|
||||
if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) {
|
||||
prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
|
||||
[this, &engine, &arena, scene](JobSystem&, JobSystem::Job*) {
|
||||
[&cullingFrustum, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) {
|
||||
FView::prepareVisibleLights(engine.getLightManager(), arena,
|
||||
mViewingCameraInfo, mCullingFrustum, scene->getLightData());
|
||||
cameraInfo.view, cullingFrustum, scene->getLightData());
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -485,7 +490,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
* (this will set the VISIBLE_RENDERABLE bit)
|
||||
*/
|
||||
|
||||
prepareVisibleRenderables(js, mCullingFrustum, renderableData);
|
||||
prepareVisibleRenderables(js, cullingFrustum, renderableData);
|
||||
|
||||
|
||||
/*
|
||||
@@ -497,7 +502,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
if (prepareVisibleLightsJob) {
|
||||
js.waitAndRelease(prepareVisibleLightsJob);
|
||||
}
|
||||
prepareShadowing(engine, driver, renderableData, scene->getLightData());
|
||||
prepareShadowing(engine, driver, renderableData, scene->getLightData(), cameraInfo);
|
||||
|
||||
/*
|
||||
* Partition the SoA so that renderables are partitioned w.r.t their visibility into the
|
||||
@@ -567,14 +572,14 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
|
||||
* Relies on FScene::prepare() and prepareVisibleLights()
|
||||
*/
|
||||
|
||||
prepareLighting(engine, driver, arena, viewport);
|
||||
prepareLighting(engine, driver, arena, viewport, cameraInfo);
|
||||
|
||||
/*
|
||||
* Update driver state
|
||||
*/
|
||||
|
||||
mPerViewUniforms.prepareTime(userTime);
|
||||
mPerViewUniforms.prepareFog(mViewingCameraInfo, mFogOptions);
|
||||
mPerViewUniforms.prepareFog(cameraInfo.getPosition(), mFogOptions);
|
||||
mPerViewUniforms.prepareTemporalNoise(mTemporalAntiAliasingOptions);
|
||||
mPerViewUniforms.prepareBlending(needsAlphaChannel);
|
||||
|
||||
@@ -644,14 +649,17 @@ void FView::prepareUpscaler(float2 scale) const noexcept {
|
||||
mPerViewUniforms.prepareUpscaler(scale, mDynamicResolution);
|
||||
}
|
||||
|
||||
void FView::prepareCamera(const CameraInfo& camera) const noexcept {
|
||||
void FView::prepareCamera(const CameraInfo& cameraInfo) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
mPerViewUniforms.prepareCamera(camera);
|
||||
mPerViewUniforms.prepareCamera(cameraInfo);
|
||||
}
|
||||
|
||||
void FView::prepareViewport(const filament::Viewport& viewport) const noexcept {
|
||||
void FView::prepareViewport(const filament::Viewport& viewport,
|
||||
uint32_t xoffset, uint32_t yoffset) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
mPerViewUniforms.prepareViewport(viewport);
|
||||
// TODO: we should pass viewport.{left|bottom} to the backend so it can offset the
|
||||
// scissor properly.
|
||||
mPerViewUniforms.prepareViewport(viewport, xoffset, yoffset);
|
||||
}
|
||||
|
||||
void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
|
||||
@@ -676,42 +684,39 @@ void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
|
||||
}
|
||||
|
||||
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
|
||||
const auto& uniforms = mShadowMapManager.getShadowMappingUniforms();
|
||||
switch (mShadowType) {
|
||||
case filament::ShadowType::PCF:
|
||||
mPerViewUniforms.prepareShadowPCF(texture);
|
||||
mPerViewUniforms.prepareShadowPCF(texture, uniforms);
|
||||
break;
|
||||
case filament::ShadowType::VSM:
|
||||
mPerViewUniforms.prepareShadowVSM(texture, mVsmShadowOptions);
|
||||
mPerViewUniforms.prepareShadowVSM(texture, uniforms, mVsmShadowOptions);
|
||||
break;
|
||||
case filament::ShadowType::DPCF:
|
||||
mPerViewUniforms.prepareShadowDPCF(texture, mSoftShadowOptions);
|
||||
mPerViewUniforms.prepareShadowDPCF(texture, uniforms, mSoftShadowOptions);
|
||||
break;
|
||||
case filament::ShadowType::PCSS:
|
||||
mPerViewUniforms.prepareShadowPCSS(texture, mSoftShadowOptions);
|
||||
mPerViewUniforms.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FView::prepareShadowMap() const noexcept {
|
||||
mPerViewUniforms.prepareShadowMapping(
|
||||
mShadowMapManager.getShadowMappingUniforms(), mVsmShadowOptions);
|
||||
mPerViewUniforms.prepareShadowMapping();
|
||||
}
|
||||
|
||||
void FView::cleanupRenderPasses() const noexcept {
|
||||
mPerViewUniforms.unbindSamplers();
|
||||
}
|
||||
|
||||
void FView::froxelize(FEngine& engine) const noexcept {
|
||||
void FView::froxelize(FEngine& engine, mat4f const& viewMatrix) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
assert_invariant(mHasDynamicLighting);
|
||||
mFroxelizer.froxelizeLights(engine, mViewingCameraInfo, mScene->getLightData());
|
||||
mFroxelizer.froxelizeLights(engine, viewMatrix, mScene->getLightData());
|
||||
}
|
||||
|
||||
void FView::commitUniforms(DriverApi& driver) const noexcept {
|
||||
mPerViewUniforms.commit(driver);
|
||||
if (mShadowUb.isDirty()) {
|
||||
driver.updateBufferObject(mShadowUbh, mShadowUb.toBufferDescriptor(driver), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void FView::commitFroxels(DriverApi& driverApi) const noexcept {
|
||||
@@ -759,7 +764,8 @@ void FView::cullRenderables(JobSystem& js,
|
||||
}
|
||||
|
||||
void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena,
|
||||
const CameraInfo& camera, Frustum const& frustum, FScene::LightSoa& lightData) noexcept {
|
||||
mat4f const& viewMatrix, Frustum const& frustum,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
assert_invariant(lightData.size() > FScene::DIRECTIONAL_LIGHTS_COUNT);
|
||||
|
||||
@@ -837,7 +843,7 @@ void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena
|
||||
// pre-compute the lights' distance to the camera, for sorting below
|
||||
// - we don't skip the directional light, because we don't care, it's ignored during sorting
|
||||
float4 const* const UTILS_RESTRICT spheres = lightData.data<FScene::POSITION_RADIUS>();
|
||||
computeLightCameraDistances(distances, camera, spheres, size);
|
||||
computeLightCameraDistances(distances, viewMatrix, spheres, size);
|
||||
|
||||
// skip directional light
|
||||
Zip2Iterator<FScene::LightSoa::iterator, float*> b = { lightData.begin(), distances };
|
||||
@@ -855,15 +861,15 @@ void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena
|
||||
UTILS_ALWAYS_INLINE
|
||||
inline void FView::computeLightCameraDistances(
|
||||
float* UTILS_RESTRICT const distances,
|
||||
CameraInfo const& UTILS_RESTRICT camera,
|
||||
float4 const* UTILS_RESTRICT const spheres, size_t count) noexcept {
|
||||
mat4f const& UTILS_RESTRICT viewMatrix,
|
||||
float4 const* UTILS_RESTRICT spheres, size_t count) noexcept {
|
||||
|
||||
// without this, the vectorization is less efficient
|
||||
// we're guaranteed to have a multiple of 4 lights (at least)
|
||||
count = uint32_t(count + 3u) & ~3u;
|
||||
for (size_t i = 0 ; i < count; i++) {
|
||||
const float4 sphere = spheres[i];
|
||||
const float4 center = camera.view * sphere.xyz; // camera points towards the -z axis
|
||||
const float4 center = viewMatrix * sphere.xyz; // camera points towards the -z axis
|
||||
distances[i] = length(center);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-21
@@ -51,6 +51,7 @@
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
namespace utils {
|
||||
class JobSystem;
|
||||
@@ -118,8 +119,11 @@ public:
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
CameraInfo computeCameraInfo(FEngine& engine) const noexcept;
|
||||
|
||||
void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena,
|
||||
Viewport const& viewport, math::float4 const& userTime, bool needsAlphaChannel) noexcept;
|
||||
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
|
||||
math::float4 const& userTime, bool needsAlphaChannel) noexcept;
|
||||
|
||||
void setScene(FScene* scene) { mScene = scene; }
|
||||
FScene const* getScene() const noexcept { return mScene; }
|
||||
@@ -128,8 +132,6 @@ public:
|
||||
void setCullingCamera(FCamera* camera) noexcept { mCullingCamera = camera; }
|
||||
void setViewingCamera(FCamera* camera) noexcept { mViewingCamera = camera; }
|
||||
|
||||
CameraInfo const& getCameraInfo() const noexcept { return mViewingCameraInfo; }
|
||||
|
||||
void setViewport(Viewport const& viewport) noexcept;
|
||||
Viewport const& getViewport() const noexcept {
|
||||
return mViewport;
|
||||
@@ -159,16 +161,17 @@ public:
|
||||
|
||||
// returns the view's name. The pointer is owned by View.
|
||||
const char* getName() const noexcept {
|
||||
return mName.c_str();
|
||||
return mName.c_str_safe();
|
||||
}
|
||||
|
||||
void prepareUpscaler(math::float2 scale) const noexcept;
|
||||
void prepareCamera(const CameraInfo& camera) const noexcept;
|
||||
void prepareViewport(const Viewport& viewport) const noexcept;
|
||||
void prepareCamera(const CameraInfo& cameraInfo) const noexcept;
|
||||
void prepareViewport(const Viewport& viewport, uint32_t xoffset, uint32_t yoffset) const noexcept;
|
||||
void prepareShadowing(FEngine& engine, backend::DriverApi& driver,
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
|
||||
void prepareLighting(FEngine& engine, FEngine::DriverApi& driver,
|
||||
ArenaScope& arena, Viewport const& viewport) noexcept;
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
CameraInfo const& cameraInfo) noexcept;
|
||||
void prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena,
|
||||
filament::Viewport const& viewport, CameraInfo const &cameraInfo) noexcept;
|
||||
|
||||
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
|
||||
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset,
|
||||
@@ -181,7 +184,7 @@ public:
|
||||
void prepareShadowMap() const noexcept;
|
||||
|
||||
void cleanupRenderPasses() const noexcept;
|
||||
void froxelize(FEngine& engine) const noexcept;
|
||||
void froxelize(FEngine& engine, math::mat4f const& viewMatrix) const noexcept;
|
||||
void commitUniforms(backend::DriverApi& driver) const noexcept;
|
||||
void commitFroxels(backend::DriverApi& driverApi) const noexcept;
|
||||
|
||||
@@ -407,8 +410,6 @@ public:
|
||||
static void cullRenderables(utils::JobSystem& js, FScene::RenderableSoa& renderableData,
|
||||
Frustum const& frustum, size_t bit) noexcept;
|
||||
|
||||
auto& getShadowUniforms() const { return mShadowUb; }
|
||||
|
||||
PerViewUniforms const& getPerViewUniforms() const noexcept { return mPerViewUniforms; }
|
||||
PerViewUniforms& getPerViewUniforms() noexcept { return mPerViewUniforms; }
|
||||
|
||||
@@ -419,7 +420,7 @@ public:
|
||||
|
||||
// Clean-up the oldest frame and save the current frame information.
|
||||
// This is typically called after all operations for this View's rendering are complete.
|
||||
// (e.g.: after the FrameFraph execution).
|
||||
// (e.g.: after the FrameGraph execution).
|
||||
void commitFrameHistory(FEngine& engine) noexcept;
|
||||
|
||||
// create the picking query
|
||||
@@ -461,11 +462,11 @@ private:
|
||||
Frustum const& frustum, FScene::RenderableSoa& renderableData) const noexcept;
|
||||
|
||||
static void prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena,
|
||||
const CameraInfo& camera, Frustum const& frustum,
|
||||
math::mat4f const& viewMatrix, Frustum const& frustum,
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
static inline void computeLightCameraDistances(float* distances,
|
||||
const CameraInfo& camera, const math::float4* spheres, size_t count) noexcept;
|
||||
math::mat4f const& viewMatrix, const math::float4* spheres, size_t count) noexcept;
|
||||
|
||||
static void computeVisibilityMasks(
|
||||
uint8_t visibleLayers, uint8_t const* layers,
|
||||
@@ -476,7 +477,7 @@ private:
|
||||
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept {
|
||||
mPerViewUniforms.bind(driver);
|
||||
driver.bindUniformBuffer(BindingPoints::LIGHTS, mLightUbh);
|
||||
driver.bindUniformBuffer(BindingPoints::SHADOW, mShadowUbh);
|
||||
driver.bindUniformBuffer(BindingPoints::SHADOW, mShadowMapManager.getShadowUniformsHandle());
|
||||
driver.bindUniformBuffer(BindingPoints::FROXEL_RECORDS, mFroxelizer.getRecordBuffer());
|
||||
}
|
||||
|
||||
@@ -493,16 +494,14 @@ private:
|
||||
|
||||
// these are accessed in the render loop, keep together
|
||||
backend::Handle<backend::HwBufferObject> mLightUbh;
|
||||
backend::Handle<backend::HwBufferObject> mShadowUbh;
|
||||
backend::Handle<backend::HwBufferObject> mRenderableUbh;
|
||||
|
||||
FScene* mScene = nullptr;
|
||||
// The camera set by the user, used for culling and viewing
|
||||
FCamera* mCullingCamera = nullptr;
|
||||
// The optional (debug) camera, used only for viewing
|
||||
FCamera* mViewingCamera = nullptr;
|
||||
|
||||
CameraInfo mViewingCameraInfo;
|
||||
Frustum mCullingFrustum{};
|
||||
|
||||
mutable Froxelizer mFroxelizer;
|
||||
|
||||
Viewport mViewport;
|
||||
@@ -540,7 +539,6 @@ private:
|
||||
RenderQuality mRenderQuality;
|
||||
|
||||
mutable PerViewUniforms mPerViewUniforms;
|
||||
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
|
||||
|
||||
mutable FrameHistory mFrameHistory{};
|
||||
|
||||
|
||||
@@ -215,8 +215,6 @@ void FrameGraph::execute(backend::DriverApi& driver) noexcept {
|
||||
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
// this is a good place to kick the GPU, since we've just done a bunch of work
|
||||
driver.flush();
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,14 +39,19 @@ using namespace math;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
void FSR_ScalingSetup(FSRUniforms* outUniforms, FSRScalingConfig config) noexcept {
|
||||
FsrEasuCon( outUniforms->EasuCon0.v, outUniforms->EasuCon1.v,
|
||||
FsrEasuConOffset( outUniforms->EasuCon0.v, outUniforms->EasuCon1.v,
|
||||
outUniforms->EasuCon2.v, outUniforms->EasuCon3.v,
|
||||
// Viewport size (top left aligned) in the input image which is to be scaled.
|
||||
config.viewportWidth, config.viewportHeight,
|
||||
config.input.width, config.input.height,
|
||||
// The size of the input image.
|
||||
config.inputWidth, config.inputHeight,
|
||||
// The output resolution.
|
||||
config.outputWidth, config.outputHeight);
|
||||
config.outputWidth, config.outputHeight,
|
||||
// Input image offset
|
||||
config.input.left, config.input.bottom);
|
||||
// FIXME: FsrEasu api claims it needs the top,left offset, but when using that we get
|
||||
// some bad artifacts. To investigate. For reference:
|
||||
// top = config.inputHeight - (config.input.bottom + config.input.height)
|
||||
}
|
||||
|
||||
void FSR_SharpeningSetup(FSRUniforms* outUniforms, FSRSharpeningConfig config) noexcept {
|
||||
|
||||
+7
-6
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_FSR_H
|
||||
#define TNT_FILAMENT_FSR_H
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -24,12 +26,11 @@
|
||||
namespace filament {
|
||||
|
||||
struct FSRScalingConfig {
|
||||
uint32_t viewportWidth;
|
||||
uint32_t viewportHeight;
|
||||
uint32_t inputWidth;
|
||||
uint32_t inputHeight;
|
||||
uint32_t outputWidth;
|
||||
uint32_t outputHeight;
|
||||
filament::Viewport input; // region of source to be scaled
|
||||
uint32_t inputWidth; // width of source
|
||||
uint32_t inputHeight; // height of source
|
||||
uint32_t outputWidth; // width of destination
|
||||
uint32_t outputHeight; // height of destination
|
||||
};
|
||||
|
||||
struct FSRSharpeningConfig {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user