Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 791f51dcb4 Update glslang to e3bca2a 2022-05-04 12:29:21 -07:00
Benjamin Doherty 4621f3ec5d Filament specific changes 2022-05-04 12:06:26 -07:00
Benjamin Doherty ab9c09d0d9 Update spirv-tools to 46492aa 2022-05-04 12:00:04 -07:00
Benjamin Doherty dbf51c1c7d Update SPIRV-Cross to 0c8a79b 2022-05-04 11:07:15 -07:00
Mathias Agopian ebd5f150c1 fix a potential threading/memory corruption
We can't call prepareProgram() from any thread, since it is calling
into the backend. When we had more than a certain number of commands,
we would use the jobsystem to generate them and so prepareProgram()
could end up being called from a jobsystem thread.

This is fixed by looping through all the commands once they're generated
and calling prepareProgram() then.
2022-05-03 15:16:11 -07:00
Philip Rideout 156dcb54e8 gltf_viewer: fix accidental removal of camera selector. 2022-05-03 14:33:43 -07:00
Mathias Agopian 88ef76989c Cleanup Renderer.cpp
The main changes are:

- ColorPass() and RefractionPass() are now static and moved into a 
  new RendererUtils file. These methods don't need FRenderer and are 
  more like a big "script".

- Cleaned-up includes

- Reformatted/reordered methods
2022-05-03 14:27:48 -07:00
Philip Rideout fc284af684 Rename SimpleViewer to ViewerGui.
The SimpleViewer C++ class was not viewer component like `ModelViewer`
and `<filament-viewer>`. It was actually just the UI builder used
in the `gltf_viewer` desktop app and the remote Android interface.
2022-05-03 08:10:53 -07:00
Philip Rideout 3a8685cd15 Upgrade emsdk to 3.1.9 2022-05-03 08:10:26 -07:00
Philip Rideout f035655f5b Add support for KHR_texture_basisu.
This adds a new implementation of the TextureProvider interface called
Ktx2Provider.

Tested using the KTX2 variant of the StainedGlassLamp model in the
Khronos samples repo.

Tested on WebGL 2.0 (Chrome v100), Android (Pixel 6 Pro), and Desktop
(Metal, OpenGL, and Vulkan via MoltenVK).
2022-05-02 13:15:56 -07:00
Ben Doherty f498e08d1e Update hello-pbr to work in all orientations (#5488) 2022-05-02 14:41:32 -04:00
Philip Rideout db3186a65f Remove etc2comp and astcenc from the repo. 2022-05-02 11:29:19 -07:00
Philip Rideout 248815a3e0 Fix regression with <filament-viewer>. 2022-05-02 11:09:26 -07:00
Benjamin Doherty ebb62b5d55 Release Filament 1.21.3 2022-05-02 13:17:33 -04:00
Philip Rideout b9a5ad205a Fix unit test failures.
Basis does not support DXT3 so in the past this unit test passed only
due to the enum translation bug that was recently fixed.
2022-05-02 09:48:36 -07:00
Philip Rideout 100986d8d9 Fix WebGL debug build (assert with 3-channel images) 2022-04-29 16:30:14 -07:00
Philip Rideout b7c9a36d57 Ktx2Reader: Fix error in enum translation table.
This caused DXT5 content to be interpreted as DXT3 by the GPU,
which seemed to manifest as visual artifacts in the alpha channel.
2022-04-29 16:29:53 -07:00
Philip Rideout d76551ab28 Add async interface to Ktx2Reader. 2022-04-29 14:20:57 -07:00
Mathias Agopian 92bc0752d1 repair VSM gaussian blur
When there was more than one shadowmap we would hit an assert. This 
bug was introduced recently and was due to incorrect use of the 
framegraph.
2022-04-29 12:46:37 -07:00
Mathias Agopian 657c251463 Ensure we always have 4 "safe" mip levels
A "safe" mip-level is one that has exactly half the dimensions of
the current level. In other words, we guarantee that we can halve the
dimensions 4 times without loss (i.e. odd dimension).

This is achieved by effectively padding the viewport (i.e. making the
viewport's dimensions multiple of 16). The post-processing passes
eventually take care of cropping the extra padding so the final
image is unchanged.

This is a more generic solution to dynamic resolution padding, helps
with memory allocations and helps all the algorithms that depend on
mipmaping (e.g. DoF, SSAO, Bloom).


One important improvement brought by this change is that all "final"
post-processing effects are now able to handle a sub-region of the
source, including: color-gradding, fxaa and upscaling.
2022-04-29 12:05:08 -07:00
Philip Rideout b6b8fee483 WebGL: fix isTextureFormatSupported for ETC2 formats.
It was assumed that ETC2 is supported on all ES3 devices, but WebGL on
desktop machines does not support ETC2.

Note that WebGL has its own special extension strings for compressed
textures.
2022-04-29 12:04:59 -07:00
Mathias Agopian be5b60f895 cleaner handling of shadow related uniforms
Some uniforms are needed during shadow map generation, other
during shadow sampling (color pass). We didn't have a clear separation
between them and ended up setting the UBOs that were needed only for
sampling multiple times during generation.
2022-04-29 12:04:47 -07:00
Mathias Agopian 681d31758f move the ShadowUib out of View into ShadowMapManager 2022-04-29 12:04:47 -07:00
Ben Doherty 80dc2f5c9d Move toCompressedFilamentEnum to Ktx1Reader header (#5493) 2022-04-29 14:49:54 -04:00
Mathias Agopian ce7dd7a1fb reorganize again our main UBO
- try to sort data by usage.
- clarify what post-process materials have access to (not enforced yet)
2022-04-28 10:13:20 -07:00
Mathias Agopian 85617c96a2 shrink post-process materials a bit
post-process materials don't need common_shadowing nor common_shading.

also don't rely on MIN_ROUGHNESS in .mat files, because it's not public.
2022-04-28 10:13:20 -07:00
Mathias Agopian 58760adb6a fix typo introduced in previous PR. fixes froxels. 2022-04-27 17:28:16 -07:00
Mathias Agopian 4ef0b72773 rearrange the per-view UBO (breaks materials)
- rename common_math and common_shadowing from .fs to .glsl
  because they were both included in both vertex and fragment
  shaders

- add getFragCoord() which always returns coordinates in the
  glsl convention. This is useful when used with RNGs to get
  consistant results accross backends.
2022-04-27 15:27:47 -07:00
Mathias Agopian 45631c3b81 update binding points from 8 to 12. 2022-04-27 15:27:03 -07:00
Mathias Agopian 6f6dde2579 Optimize custom rendertargets use in Renderer
we just store the "sampleable" attachments as a bitmask in 
RenderTarget instead of computing it each time.
2022-04-27 15:26:45 -07:00
Mathias Agopian e0acbe42cf update material version 2022-04-27 10:14:38 -07:00
Mathias Agopian 4f137ccf89 don't rely on frameUniform.resolution for froxelization
this is because this uniform is not well defined -- it's unclear if
it is set to the viewport or the buffer size.

also, it's not needed, and removes a couple multiplies from the shader.
2022-04-27 09:38:33 -07:00
Mathias Agopian 04010e7dd9 when matdbg is used don't terminate the program on errors 2022-04-27 08:20:00 -07:00
Mathias Agopian f4f9f331c0 CameraInfo cleanups
- add getUserViewMatrix() on CameraInfo, the "user" view matrix is the 
view matrix before we apply the world origin transform, it is needed in
 a few places, so we make it a  method so that in the future we could 
precompute it if we wanted to.

- remove worldOffset which is just the last column of worldOrigin
2022-04-26 11:32:41 -07:00
Mathias Agopian dada291f6b rename mScaling to mScalingCS 2022-04-26 11:32:41 -07:00
Mathias Agopian 18f2f8b6d4 remove unused parameters and state 2022-04-26 11:32:41 -07:00
Mathias Agopian d4117ac968 use interleavedGradientNoise() instead of custom implementaion
interleavedGradientNoise() is available in common_math, so use that
instead of our custom gradient noise RNG.
2022-04-26 11:32:41 -07:00
Mathias Agopian eccbc8c242 simplify FXAA setup
We now do all the setup math on the CPU side, but we also remove some
unnecessary computations based on assumptions that are no longer true.
2022-04-26 11:32:41 -07:00
Mathias Agopian 3cdf24f515 remove getUV() from post-process materials
it made it actually more confusing to have two version of the uv
(normalized and not). Also the unnormalized version was computed from
frameUniforms which is not great in the case of post-processing
(it just happened to work).
2022-04-26 11:30:35 -07:00
Mathias Agopian d91faf9df8 avoid unnecessary parameter copies 2022-04-26 11:28:36 -07:00
Brian Stringham d950efa6ea Changed ALL_SHADER_STATE_FLAGS initializer to support c++17, which is required for C++/CLI support. (#5465) 2022-04-26 08:59:44 -07:00
Benjamin Doherty 861d69ea7c Release Filament 1.21.2 2022-04-25 16:57:00 -04:00
LaiJF b2cc904fab Fix typo 2022-04-25 09:06:12 -07:00
Mathias Agopian 3f87b520bb ShadowMap::render() doesn't need CameraInfo anymore 2022-04-22 12:35:00 -07:00
LaiJF 135a362176 Fix typo (#5455) 2022-04-22 10:46:04 -04:00
Philip Rideout 391ebc3702 gltfio: fix progress indicator when error occurs.
The progress bar in `gltf_viewer` should not appear to hang if any
of an asset's textures fail to load.
2022-04-21 17:41:32 -07:00
Philip Rideout b4d1443720 Repair iOS build. I think. 2022-04-21 15:36:37 -07:00
Philip Rideout c06737aca2 TextureProvider fixup in ResourceLoader JNI. 2022-04-21 13:44:44 -07:00
Philip Rideout f847232296 gltfio: introduce TextureProvider API.
This allows clients to provide their own asynchronous texture decoders
for various mime-typed images. This is a plug-in component for gltfio,
in some ways similar to MaterialProvider.

There are two motivations for this: to decouple gltfio from STB and
to make it easier to integrate support for BasisU textures.

This also has the side effect of simplifying ResourceLoader, since the
texture decoding jobs have been moved out.

As part of this work, I made the "stb" CMake target into a traditional
static library. Previously we had several files called `Image.cpp`
whose sole purpose was to enable STB_IMAGE_IMPLEMENTATION.
2022-04-21 13:25:40 -07:00
LaiJF 89f06b8923 Fix typo (#5454) 2022-04-21 09:52:04 -06:00
Philip Rideout 90a2b1f3c9 Remove WebGL workaround for bloom.
Bloom now works fine (tested with Chrome 100 on a x86 MacBook) without
this workaround for overly aggressive feedback loop detection.
2022-04-20 12:07:49 -07:00
Ben Doherty 059f51338d Audit backends and test cases for correct Y orientation. (#5440) 2022-04-20 11:01:31 -06:00
LaiJF d3cea5121e Fix typo 2022-04-20 09:58:46 -07:00
LaiJF c37301d2dc Fix typo. 2022-04-19 20:27:52 -07:00
Mathias Agopian aa16778fbd don't leak Engine if the driver fail to initialize
If there is an error during the driver initialization, Engine::create() 
returns nullptr, but the FEngine was allocated and would then never
be destroyed.

mTerminated was useless because by construction it would always have
the expected value.
2022-04-19 11:49:07 -07:00
Mathias Agopian ee499ad2ca don't use a unique_ptr to hold the DFG
we can simply make it a member of FEngine.
2022-04-19 11:49:07 -07:00
Mathias Agopian 958dd5bb27 a lot of our workaround don't apply when using ANGLE 2022-04-18 20:19:09 -07:00
Mathias Agopian f988b75999 fix the 2 passes version of FSR
This version is used only on qualcomm devices. It's now forbidden to
create a program inside a renderpass. Unfortunately there was still
one codepath doing so.
2022-04-18 20:18:45 -07:00
Philip Rideout e3ccb4bb3c gltfio Animator: add resetBoneMatrices().
See #5436.
2022-04-18 15:07:35 -07:00
Philip Rideout 3f0dc6f7e6 Minor Ktx2Reader refactoring. 2022-04-18 15:02:08 -07:00
Mathias Agopian c212dd5bc7 remove the CameraInfo state from FView
this state only exists during Renderer::render(), having it outside
of view is more flexible as it give us an opportunity to adjust
camera parameter globally before drawing.

Also greatly reduced the size of RenderPass (which is a Value object).
2022-04-18 12:04:18 -07:00
LaiJF 1eceab0040 Fix typo. 2022-04-18 10:46:31 -07:00
Philip Rideout ab9d1e5956 Fix unit test. 2022-04-15 12:22:12 -07:00
Philip Rideout c906290416 Add to release notes. 2022-04-15 10:54:27 -07:00
Philip Rideout d52a24b6e9 Incorporate feedback from code review. 2022-04-15 10:48:48 -07:00
Philip Rideout 9aeec3a759 Add Ktx2Reader and BasisEncoder and use them in samples.
mipgen can now emit basis-encoded KTX2 files. Both the desktop and
web "suzanne" samples use this as a test for compressed textures.

This PR does not add KTX2 support to glTF, but it's on the way.

`BasisEncoder` has a builder style API that calls the basis encoder to
create KTX2 files. This hides some low-level BasisU features that we are
not using, like file I/O and mipmap generation.

`Ktx2Reader` is an easy-to-use API for creating Filament textures from
KTX2 files. Its API primarily consists of these two methods:

    bool requestFormat(Texture::InternalFormat format);
    Filament::Texture* load(const uint8_t* data, size_t size);

The first method is used to build an ordered list of formats that are
supported by your hardware. The second method consumes the contents of a
basis-encoded KTX2 file and attempts to produce a Filament texture with
a preferred format.

IMPORTANT: Our tools still let you use KTX1 for non-compressed images
because it is useful for HDR, but you can no longer use KTX1 for
block-compressed data.

Partial fix for #4771.
2022-04-15 10:48:48 -07:00
Mathias Agopian 550fcfe2e3 fix typos 2022-04-15 09:18:06 -07:00
Mathias Agopian b2cdf9f2b4 don't issue a flush systematically after framegraph's execute
This is only a good place for a flush *if* we're going to draw another
view (so the GPU can start working). Otherwise, it can be costly
on some drivers and unnecessary since swapbuffers will do the same.
2022-04-15 09:12:39 -07:00
Mathias Agopian 19a3fe3dce allow radius parameter for upsample bloom stage
The original article we used for our bloom (siggraph'14) called for
a radius parameter for the upsample stage. We're always using 1.0 right
now, but it is useful to keep this possibility for experimentations.
2022-04-14 14:04:32 -07:00
Mathias Agopian 754abbf78e bloom pass cleanups 2022-04-14 14:04:32 -07:00
Mathias Agopian 9b855e9a03 fix bloom downsample
a bug was introduced in 8/2020 where the downsampling filter was wrong,
due to a mistake using textureLodOffset, which obviously uses offsets
in source texel -- we were assuming destination.
2022-04-14 14:04:32 -07:00
Mathias Agopian 2534fdf92d propagate the matdbg flag to graddle 2022-04-14 14:04:15 -07:00
Mathias Agopian dab02e915e Remove deprecated Stream APIs 2022-04-14 12:51:03 -07:00
Mathias Agopian 68da1a25eb disable ANGLE optimization that hurts filament 2022-04-14 08:52:56 -07:00
Romain Guy c67b363576 Fix compilation warnings 2022-04-13 15:15:16 -07:00
Philip Rideout 5c0e7d8211 sample-gltf-viewer: fix regression with zip files.
Path resolution was broken with #5285, which only worked when the glTF
happened to be at root level. This new fix has been tested against:

- My Sponza zip file that hass all files in a "sponza" folder.
- My Bistro zip file, which has resources in a child folder relative to
  the gltf file
- A self-created flat zip using IridescentDishWithOlives, with
  everything at root level.

Side note: Bistro still fails on a Pixel 6 Pro Mali but for an unrelated
reason: (null deref in backend::TimerQueryNative::queryResultAvailable)
2022-04-13 12:18:47 -07:00
1516 changed files with 64995 additions and 85627 deletions
+1 -1
View File
@@ -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
View File
@@ -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)
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)},
@@ -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.
@@ -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
}
+18 -3
View File
@@ -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);
@@ -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 -1
View File
@@ -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.
@@ -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]
@@ -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
}
}
@@ -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

+10 -6
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2
View File
@@ -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
-3
View File
@@ -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)
+10 -11
View File
@@ -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
-13
View File
@@ -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,
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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>
-5
View File
@@ -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,
+2
View File
@@ -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
+79 -65
View File
@@ -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);
+4 -237
View File
@@ -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
+1 -22
View File
@@ -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);
+2 -2
View File
@@ -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; }
};
+1 -20
View File
@@ -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 -16
View File
@@ -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);
+4 -10
View File
@@ -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();
+15 -57
View File
@@ -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);
+4
View File
@@ -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
}
)");
+36 -9
View File
@@ -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());
+1 -1
View File
@@ -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;
+4
View File
@@ -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.
*
+1 -1
View File
@@ -124,7 +124,7 @@ public:
*
* Ignored if an environment is set.
*
* @param color
* @param color the constant color
*
* @return This Builder, for chaining calls.
*/
+4 -83
View File
@@ -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.
*
+6 -6
View File
@@ -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,
+5 -5
View File
@@ -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
View File
@@ -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
+6 -7
View File
@@ -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
+4 -7
View File
@@ -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;
+33 -20
View File
@@ -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 {
+28 -9
View File
@@ -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
View File
@@ -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;
+7 -7
View File
@@ -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
+17 -7
View File
@@ -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();
+4 -3
View File
@@ -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{};
+327
View File
@@ -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
+89
View File
@@ -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
+7 -7
View File
@@ -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);
}
+4 -3
View File
@@ -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
+52 -42
View File
@@ -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();
+20 -10
View File
@@ -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{
-5
View File
@@ -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();
}
+7 -8
View File
@@ -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
+8 -9
View File
@@ -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)
+12 -16
View File
@@ -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;
}
+3 -4
View File
@@ -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;
+18 -3
View File
@@ -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, {});
+5
View File
@@ -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;
};
File diff suppressed because it is too large Load Diff
+35 -77
View File
@@ -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{};
+1 -41
View File
@@ -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);
-4
View File
@@ -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;
};
+74 -68
View File
@@ -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
View File
@@ -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{};
-2
View File
@@ -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();
}
+8 -3
View File
@@ -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
View File
@@ -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