Compare commits

..
Author SHA1 Message Date
Eliza Velasquez ea9fdbeb5c wip: shader compilation benchmark 2024-01-16 17:03:41 -08:00
Ben Doherty 5fd7a4e153 Don't render in background (#7486) 2024-01-12 10:17:00 -08:00
Sungun Park 20ff230b92 Fix a json parsing bug (#7490)
When parsing a lexeme, we use one less byte than it's intended to be for
comparing the current string.

This results in a success in cases like:
- true and truX
- false and falsX
- null and nulX
where X means an arbitrary character.

Fix this by the full intended length.
2024-01-11 12:47:11 -08:00
Ben Doherty 44ff79ad34 Metal: disable fast math (#7485) 2024-01-10 15:31:23 -08:00
Mathias AgopianandPowei Feng 102d2db008 Bokeh aspect ratio (#7482)
* Bokeh aspect ratio

new DoF option to set the bokeh aspect ratio, this can be used to
simulate anamorphic lenses

* Update android/filament-android/src/main/java/com/google/android/filament/View.java

Co-authored-by: Powei Feng <powei@google.com>

* Update web/filament-js/filament.d.ts

Co-authored-by: Powei Feng <powei@google.com>

---------

Co-authored-by: Powei Feng <powei@google.com>
2024-01-10 15:25:28 -08:00
Powei Feng 5c9039e650 Release Filament 1.49.3 2024-01-10 13:55:33 -08:00
Eliza Velasquez 1203c24f06 Generate dummy stereo variants for FL0 mats
See #7415 for a more detailed description of why this change is necessary.

The remaining variants which are filtered from FL0 materials are all related to
lighting, so further hacks like this won't be necessary.

Future work involves properly supporting differing sets of variants based on
shader language.
2024-01-10 13:50:53 -08:00
Mathias Agopian 9704d27aeb TAA upscaling
This feature is still work in progress. TAA can now optionally
upscale by 4x (this disables dynamic resolution scaling).
2024-01-09 14:07:18 -08:00
Mathias Agopian c6f2c3fc1c bloom: disable fireflies reduction when using TAA
TAA already does a fireflies reduction pass, so it's not needed when
applying bloom.
2024-01-09 14:04:01 -08:00
Mathias Agopian 2faf868341 fix missing includes (new CLion warnings) 2024-01-09 14:04:01 -08:00
Powei Feng 81f6260843 Fix typo oin MaterialCompiler (#7477) 2024-01-08 15:03:07 -08:00
Ben Doherty 1a9063d53a Metal: Always report material name in use-after-free detector (#7473) 2024-01-04 11:55:28 -08:00
Ben Doherty 6062b3c8c6 Fix ostream linking error when compiling Linux DSO (#7470) 2024-01-03 12:06:44 -08:00
Ben Doherty d9186c44ba matdbg: Load codicon font (#7469) 2024-01-03 10:41:18 -08:00
Benjamin Doherty 6d06e00934 Release Filament 1.49.2 2024-01-02 14:52:58 -08:00
Ben Doherty b4021a1fbc Add picking queries to gltf-viewer iOS sample (#7464) 2024-01-02 14:48:59 -08:00
Benjamin Doherty 2c9559f9b8 Add string.h header 2024-01-02 13:24:08 -08:00
Ben Doherty e2da13f817 Fix a couple race conditions in Metal backend (#7457) 2023-12-26 15:33:37 -07:00
Mathias Agopian addae314b4 on vulkan and metal resolve depth manuall with a shader
both vk and metal don't support depth resolves, and are currently
implemented in the backend. vk is buggy and they don't resolve the
same way that gl does.
2023-12-21 21:32:22 -08:00
Mathias Agopian 5e3c51822d add basic support for multisampled samplers
we don't document this feature yet because it's still WIP and 
intended to be used internally only at this point
2023-12-21 21:32:22 -08:00
Mathias Agopian 9d817a0cc9 TAA improvements (#7441)
* TAA improvements

- fix variance + AABB history clipping (we were computing the union
  of both AABB instead of the intersection).
- added a setting for the variance parameter
- added a setting for the jitter pattern
- improved some default values
- fixed a few comments
- smaller code tweaks to facilitate future improvements
2023-12-21 16:51:28 -08:00
princeku07 95ecc3d1e4 Make applyMaterialVariant public 2023-12-19 10:09:42 -08:00
Mathias Agopian cabaa4323e add a way to set a log consumer
this is a private API to capture filament's logs.
2023-12-18 16:28:31 -08:00
Mathias Agopian d6b2ec49b0 cleanup 2023-12-18 16:28:31 -08:00
Mathias Agopian 93004aa5bc add -fno-limit-debug-info to android debug builds
This is what the NDK does, it's needed to keep debug infos for the 
STL symbols (because the STL is provided by the platform and doesn't
have debug infos).
2023-12-18 16:28:06 -08:00
Mathias Agopian 27fa8ab46d don't use eglGetProcAddress on emscripten
In practice none of the extensions we would use eglGetProcAddress for
are supported. And we had a case where an extension was reported
supported but eglGetProcAddress didn't return the corresponding
entry point.

update web demos remote ui.

FIXES=[315033914]
2023-12-18 16:27:33 -08:00
Benjamin Doherty 614990f413 Release Filament 1.49.1 2023-12-18 13:35:28 -08:00
Benjamin Doherty da058028fd Fix VulkanPlatformAndroidLinuxWindows for G3 2023-12-18 09:48:20 -08:00
selcott 55c75453df Assert non-null texture in setTextureData 2023-12-15 09:22:01 -08:00
Mathias Agopian f5eff10532 fix TAA on mobile devices.
The history neighborhood variance needed to be computed in highp.

fixes #6504
2023-12-14 13:44:15 -08:00
Ben Doherty 56a0364534 Metal: call frame scheduled callback on driver thread (#7414) 2023-12-14 11:28:29 -08:00
Mathias Agopian c8aae3cdaa fix android build 2023-12-13 16:32:22 -08:00
Mathias Agopian 4f94c575c9 fix UiHelper
We shouldn't remove the listener callbacks when a surface is destroyed
by the system (e.g. screen off/on) because then we won't know when
it comes back.

But we still need to do this when the user calls UiHelper.detach() or
when we are attaching to a new surface.

Fixes #7424
2023-12-13 15:21:04 -08:00
Mathias Agopian 1a29f020c9 fix getUserWorldFromWorldMatrix() during the shadow passes
getUserWorldFromWorldMatrix() was always set to identity during the
shadow pass. It needs to be set the the same value as the main
camera.

FIXES=[315504607]
2023-12-13 10:58:20 -08:00
Mathias Agopian d5847a3c6a fix per-light shadow "caster" flag update
The per-light shadow caster flag wasn't updated when a light was
toggled from casting to non-casting. This resulted in an out of date or
incorrect shadowmap to be used.

FIXES=[315859790]
2023-12-12 19:31:03 -08:00
Mathias Agopian 3dbae482d3 make a few PostProcessManager methods "package" public
we make PostProcessMaterial, getPostProcessMaterial() as well as
render(), commitAndRender() public (as in public to filament internals).

These methods have no reason to be private to PostProcessManager.
2023-12-12 11:18:52 -08:00
Mathias Agopian 27572e3be6 fix framegraph clear flags for imported targets
Imported targets always use the imported flags, not the flags that
where specified when the target was created. The clear flags
should be cleared after they've been used once in case that rendertarget
is reused by multiple passes.

For e.g. if the clear flag is set, the target will be cleared the first
time it is used, but if it's used again, we don't want to clear again, 
in that case we'll use the "local" flags used when the target was
created (as opposed to the imported flags).
2023-12-12 11:18:36 -08:00
Mathias Agopian dedf276e26 Allow post-process materials to update the vertex position
we add a `position` field to PostProcessVertexInputs which the user
code can modify if needed.
2023-12-12 11:18:18 -08:00
Romain Guy 111ad96134 Update dependencies to latest (#7423) 2023-12-11 21:11:54 -08:00
Mathias Agopian 2a67152dda Dynamic "Material constants" support
Material constants (a.k.a: specialization constants) can only be set
during Material creation through Material::Builder.
This change somewhat relaxes that limitation by allowing constants to
be set at runtime on Material directly.

Currently this new API is still private and only supported on FMaterial.

This feature works by invalidating the HwProgram cache of the concerned
Material, causing a shader recompile per variant; so this API is costly
and should be used only for debugging or during app/game configuration.

The TAA material is modified to use constants instead of #defines for
various settings and those are exposed in TaaOptions as well is in 
ViewerGui. So with this change all aspects of the TAA material can
be changed at runtime.
2023-12-11 16:20:47 -08:00
Maximilien 2f62978e4c Changed the level 0 materials custom commands so that materials for all APIs are built.
This allows to turn off the OpenGL backend (using -DFILAMENT_SUPPORTS_OPENGL=OFF).
Previously, disabling the OpenGL backend was leading to a compilation error related to missing level 0 materials.
2023-12-11 10:40:34 -08:00
Benjamin Doherty fdb90f2f49 Bump MATERIAL_VERSION to 49 2023-12-11 00:16:13 -08:00
103 changed files with 1899 additions and 979 deletions
+9 -1
View File
@@ -41,6 +41,8 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
set(FILAMENT_NDK_VERSION "" CACHE STRING
@@ -426,7 +428,13 @@ endif()
if (FILAMENT_ENABLE_ASAN_UBSAN)
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=address -fsanitize=undefined")
endif()
if (FILAMENT_ENABLE_TSAN)
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=thread")
endif()
if (ANDROID)
# keep STL debug infos (mimics what the NDK does)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info")
endif()
if (NOT MSVC AND NOT WEBGL)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector")
endif()
+2
View File
@@ -7,3 +7,5 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Metal: fix some shader artifacts by disabling fast math optimizations.
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.49.1'
implementation 'com.google.android.filament:filament-android:1.49.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:
```shell
pod 'Filament', '~> 1.49.1'
pod 'Filament', '~> 1.49.3'
```
### Snapshots
+10
View File
@@ -7,6 +7,16 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.50.0
- engine: TAA now supports 4x upscaling [BETA] [⚠️ **New Material Version**]
## v1.49.3
- matc: Generate stereo variants for FL0 materials [⚠️ **Recompile materials**]
## v1.49.2
## v1.49.1
+8 -8
View File
@@ -81,14 +81,14 @@ buildscript {
ext.versions = [
'jdk': 17,
'minSdk': 19,
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.9.0',
'kotlin_coroutines': '1.7.2',
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
'kotlin_coroutines': '1.7.3',
'buildTools': '34.0.0',
'ndk': '25.1.8937393',
'androidx_core': '1.10.1',
'androidx_annotations': '1.6.0'
'ndk': '26.1.10909125',
'androidx_core': '1.12.0',
'androidx_annotations': '1.7.0'
]
ext.deps = [
@@ -104,7 +104,7 @@ buildscript {
]
dependencies {
classpath 'com.android.tools.build:gradle:8.1.0'
classpath 'com.android.tools.build:gradle:8.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
@@ -1637,6 +1637,10 @@ public class View {
* circle of confusion scale factor (amount of blur)
*/
public float cocScale = 1.0f;
/**
* width/height aspect ratio of the circle of confusion (simulate anamorphic lenses)
*/
public float cocAspectRatio = 1.0f;
/**
* maximum aperture diameter in meters (zero to disable rotation)
*/
@@ -1852,7 +1856,7 @@ public class View {
}
/**
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
* Options for Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
*/
public static class MultiSampleAntiAliasingOptions {
@@ -1876,21 +1880,106 @@ public class View {
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth`, `feedback` and `jitterPattern`, however, could be changed at any time.
*
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
*
* @see setTemporalAntiAliasingOptions()
*/
public static class TemporalAntiAliasingOptions {
public enum BoxType {
/**
* use an AABB neighborhood
*/
AABB,
/**
* use the variance of the neighborhood (not recommended)
*/
VARIANCE,
/**
* use both AABB and variance
*/
AABB_VARIANCE,
}
public enum BoxClipping {
/**
* Accurate box clipping
*/
ACCURATE,
/**
* clamping
*/
CLAMP,
/**
* no rejections (use for debugging)
*/
NONE,
}
public enum JitterPattern {
RGSS_X4,
UNIFORM_HELIX_X4,
HALTON_23_X8,
HALTON_23_X16,
}
/**
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
*/
public float filterWidth = 1.0f;
/**
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
*/
public float feedback = 0.04f;
public float feedback = 0.12f;
/**
* texturing lod bias (typically -1 or -2)
*/
public float lodBias = -1.0f;
/**
* enables or disables temporal anti-aliasing
*/
public boolean enabled = false;
/**
* 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
*/
public boolean upscaling = false;
/**
* whether to filter the history buffer
*/
public boolean filterHistory = true;
/**
* whether to apply the reconstruction filter to the input
*/
public boolean filterInput = true;
/**
* whether to use the YcoCg color-space for history rejection
*/
public boolean useYCoCg = false;
/**
* type of color gamut box
*/
@NonNull
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.AABB;
/**
* clipping algorithm
*/
@NonNull
public TemporalAntiAliasingOptions.BoxClipping boxClipping = TemporalAntiAliasingOptions.BoxClipping.ACCURATE;
@NonNull
public TemporalAntiAliasingOptions.JitterPattern jitterPattern = TemporalAntiAliasingOptions.JitterPattern.HALTON_23_X16;
public float varianceGamma = 1.0f;
/**
* adjust the feedback dynamically to reduce flickering
*/
public boolean preventFlickering = false;
/**
* whether to apply history reprojection (debug option)
*/
public boolean historyReprojection = true;
}
/**
@@ -329,7 +329,6 @@ public class UiHelper {
@Override
public void detach() {
mTextureView.setSurfaceTextureListener(null);
setSurface(null);
}
@@ -382,6 +381,7 @@ public class UiHelper {
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
setSurface(null);
destroySwapChain();
return true;
}
@@ -445,6 +445,9 @@ public class UiHelper {
* {@link #attachTo(TextureView)}, or {@link #attachTo(SurfaceHolder)}.
*/
public void detach() {
if (mRenderSurface != null) {
mRenderSurface.detach();
}
destroySwapChain();
mNativeWindow = null;
mRenderSurface = null;
@@ -590,6 +593,10 @@ public class UiHelper {
// nothing to do
return false;
}
if (mRenderSurface != null) {
mRenderSurface.detach();
mRenderSurface = null;
}
destroySwapChain();
}
mNativeWindow = nativeWindow;
@@ -604,9 +611,6 @@ public class UiHelper {
}
private void destroySwapChain() {
if (mRenderSurface != null) {
mRenderSurface.detach();
}
if (mRenderCallback != null) {
mRenderCallback.onDetachedFromSurface();
}
@@ -144,7 +144,7 @@ public class FilamentInstance {
*
* Ignored if variantIndex is out of bounds.
*/
void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
nApplyMaterialVariant(mNativeObject, variantIndex);
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.49.1
VERSION_NAME=1.49.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
File diff suppressed because one or more lines are too long
Binary file not shown.
+14 -1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -587,6 +587,7 @@
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
</ul>
</li>
<li><strong>engine.setStereoscopicOptions()</strong></li>
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
<li><strong>engine.setVignetteOptions()</strong></li>
</ul>
@@ -1462,6 +1463,8 @@ This defines the following functions:</p>
<li>STENCIL_ATTACHMENT</li>
<li>UPLOADABLE</li>
<li>SAMPLEABLE</li>
<li>BLIT_SRC</li>
<li>BLIT_DST</li>
<li>SUBPASS_INPUT</li>
</ul>
</div>
Binary file not shown.
Binary file not shown.
+20 -19
View File
@@ -209,10 +209,13 @@ set(PRIVATE_HDRS
)
set(MATERIAL_SRCS
src/materials/fsr/fsr_easu.mat
src/materials/fsr/fsr_easu_mobile.mat
src/materials/fsr/fsr_easu_mobileF.mat
src/materials/fsr/fsr_rcas.mat
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
src/materials/bloom/bloomUpsample.mat
src/materials/colorGrading/colorGrading.mat
src/materials/colorGrading/colorGradingAsSubpass.mat
src/materials/colorGrading/customResolveAsSubpass.mat
@@ -220,29 +223,27 @@ set(MATERIAL_SRCS
src/materials/defaultMaterial.mat
src/materials/dof/dof.mat
src/materials/dof/dofCoc.mat
src/materials/dof/dofDownsample.mat
src/materials/dof/dofCombine.mat
src/materials/dof/dofDilate.mat
src/materials/dof/dofDownsample.mat
src/materials/dof/dofMedian.mat
src/materials/dof/dofMipmap.mat
src/materials/dof/dofTiles.mat
src/materials/dof/dofTilesSwizzle.mat
src/materials/dof/dofDilate.mat
src/materials/dof/dofMipmap.mat
src/materials/dof/dofMedian.mat
src/materials/flare/flare.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
src/materials/bloom/bloomUpsample.mat
src/materials/fsr/fsr_easu.mat
src/materials/fsr/fsr_easu_mobile.mat
src/materials/fsr/fsr_easu_mobileF.mat
src/materials/fsr/fsr_rcas.mat
src/materials/resolveDepth.mat
src/materials/separableGaussianBlur.mat
src/materials/skybox.mat
src/materials/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat
src/materials/ssao/mipmapDepth.mat
src/materials/skybox.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/separableGaussianBlur.mat
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/vsmMipmap.mat
src/materials/blitLow.mat
)
set(MATERIAL_FL0_SRCS
@@ -320,7 +321,7 @@ foreach (mat_src ${MATERIAL_SRCS})
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
foreach (mat_src ${MATERIAL_FL0_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
@@ -328,7 +329,7 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
add_custom_command(
OUTPUT ${output_path}
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
@@ -300,6 +300,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
+7 -41
View File
@@ -34,24 +34,12 @@ public:
explicit MetalBlitter(MetalContext& context) noexcept;
enum class FormatType { NONE, COLOR, DEPTH, STENCIL, DEPTH_STENCIL };
static FormatType getFormatType(TextureFormat format) noexcept {
bool const depth = isDepthFormat(format);
bool const stencil = isStencilFormat(format);
if (depth && stencil) return FormatType::DEPTH_STENCIL;
if (depth) return FormatType::DEPTH;
if (stencil) return FormatType::STENCIL;
return FormatType::COLOR;
}
struct BlitArgs {
struct Attachment {
using Type = FormatType;
id<MTLTexture> texture = nil;
MTLRegion region = {};
uint8_t level = 0;
uint32_t slice = 0; // must be 0 on source attachment
Type type = Type::NONE;
};
// Valid source formats: 2D, 2DArray, 2DMultisample, 3D
@@ -60,16 +48,6 @@ public:
Attachment destination;
SamplerMagFilter filter;
bool blitColor() const {
return source.type == Attachment::Type::COLOR &&
destination.type == Attachment::Type::COLOR;
}
bool blitDepth() const {
return source.type == Attachment::Type::DEPTH &&
destination.type == Attachment::Type::DEPTH;
}
bool destinationIsFullAttachment() const {
return destination.texture.width == destination.region.size.width &&
destination.texture.height == destination.region.size.height;
@@ -90,31 +68,20 @@ private:
const BlitArgs& args, uint32_t depthPlane);
struct BlitFunctionKey {
bool blitColor;
bool blitDepth;
bool msaaColorSource;
bool msaaDepthSource;
bool sources3D;
char padding[3];
bool msaaColorSource{};
bool sources3D{};
char padding[2]{};
bool isValid() const noexcept {
// MSAA 3D textures do not exist.
bool const hasMsaa = msaaColorSource || msaaDepthSource;
bool const hasMsaa = msaaColorSource;
return !(hasMsaa && sources3D);
}
bool operator==(const BlitFunctionKey& rhs) const noexcept {
return blitColor == rhs.blitColor &&
blitDepth == rhs.blitDepth &&
msaaColorSource == rhs.msaaColorSource &&
msaaDepthSource == rhs.msaaDepthSource &&
return msaaColorSource == rhs.msaaColorSource &&
sources3D == rhs.sources3D;
}
BlitFunctionKey() {
std::memset(this, 0, sizeof(BlitFunctionKey));
}
};
static bool blitFastPath(id<MTLCommandBuffer> cmdBuffer,
@@ -123,9 +90,8 @@ private:
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
const BlitArgs& args, const char* label);
void blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
const char* label);
void blitDepthPlane(id <MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label);
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key) const;
id<MTLFunction> getBlitVertexFunction();
+19 -93
View File
@@ -36,13 +36,7 @@ struct VertexOut
struct FragmentOut
{
#ifdef BLIT_COLOR
float4 color [[color(0)]];
#endif
#ifdef BLIT_DEPTH
float depth [[depth(any)]];
#endif
};
vertex VertexOut
@@ -66,7 +60,6 @@ fragment FragmentOut
blitterFrag(VertexOut in [[stage_in]],
sampler sourceSampler [[sampler(0)]],
#ifdef BLIT_COLOR
#ifdef MSAA_COLOR_SOURCE
texture2d_ms<float, access::read> sourceColor [[texture(0)]],
#elif SOURCES_3D
@@ -74,32 +67,18 @@ blitterFrag(VertexOut in [[stage_in]],
#else
texture2d<float, access::sample> sourceColor [[texture(0)]],
#endif // MSAA_COLOR_SOURCE
#endif // BLIT_COLOR
#ifdef BLIT_DEPTH
#ifdef MSAA_DEPTH_SOURCE
texture2d_ms<float, access::read> sourceDepth [[texture(1)]],
#elif SOURCES_3D
texture3d<float, access::sample> sourceDepth [[texture(1)]],
#else
texture2d<float, access::sample> sourceDepth [[texture(1)]],
#endif // MSAA_DEPTH_SOURCE
#endif // BLIT_DEPTH
constant FragmentArgs* args [[buffer(0)]])
{
FragmentOut out = {};
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
// These coordinates match the Vulkan vkCmdBlitImage spec:
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
float2 uvoffset = uvbase - args->dstOffset;
float2 uvscaled = uvoffset * args->scale;
float2 uv = uvscaled + args->srcOffset;
#endif
#ifdef BLIT_COLOR
#ifdef MSAA_COLOR_SOURCE
out.color = float4(0.0);
for (uint s = 0; s < sourceColor.get_num_samples(); s++) {
@@ -115,25 +94,7 @@ blitterFrag(VertexOut in [[stage_in]],
float2 uvnorm = uv / float2(sourceColor.get_width(args->lod), sourceColor.get_height(args->lod));
out.color += sourceColor.sample(sourceSampler, uvnorm, level(args->lod));
#endif // MSAA_COLOR_SOURCE
#endif // BLIT_COLOR
#ifdef BLIT_DEPTH
#ifdef MSAA_DEPTH_SOURCE
out.depth = 0.0;
for (uint s = 0; s < sourceDepth.get_num_samples(); s++) {
out.depth += sourceDepth.read(static_cast<uint2>(uv), s).r;
}
out.depth /= sourceDepth.get_num_samples();
#elif SOURCES_3D
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
float3 coords = float3(uvnormd, (static_cast<float>(args->depthPlane) + 0.5) /
sourceDepth.get_depth(args->lod));
out.depth = sourceDepth.sample(sourceSampler, coords, level(args->lod)).r;
#else
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
out.depth = sourceDepth.sample(sourceSampler, uvnormd, level(args->lod)).r;
#endif // MSAA_DEPTH_SOURCE
#endif // BLIT_DEPTH
return out;
}
)";
@@ -158,9 +119,12 @@ void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, co
return;
}
// If the destination is MSAA, and we weren't able to use the fast path (meaning formats
// didn't match or sample count didn't match), report an error, as blitting to a MSAA texture
// isn't supported through the "slow path" yet.
// If we end-up here, it means that either:
// - we're resolving a color texture
// - src/dest formats didn't match, or we're scaling -- this can only happen with the legacy
// blit() path and implies that the format is not depth.
// note: in the future we will support a fast-path resolve
UTILS_UNUSED_IN_RELEASE
const bool destinationIsMultisample =
args.destination.texture.textureType == MTLTextureType2DMultisample;
@@ -197,49 +161,31 @@ bool MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer,
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
const BlitArgs& args, const char* label) {
bool const blitColor = args.blitColor();
bool const blitDepth = args.blitDepth();
// scaling in any dimension is not allowed
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
// we're always blitting a single plane
assert_invariant(blitColor != blitDepth);
uint32_t depthPlaneSource = args.source.region.origin.z;
uint32_t depthPlaneDest = args.destination.region.origin.z;
for (NSUInteger d = 0; d < args.source.region.size.depth; d++) {
blitDepthPlane(cmdBuffer, blitColor, blitDepth, args,
blitDepthPlane(cmdBuffer, args,
depthPlaneSource++, depthPlaneDest++, label);
}
}
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
const char* label) {
assert_invariant(blitColor != blitDepth);
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label) {
MTLRenderPassDescriptor* const descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
if (blitColor) {
setupAttachment(descriptor.colorAttachments[0], args, depthPlaneDest);
}
if (blitDepth) {
setupAttachment(descriptor.depthAttachment, args, depthPlaneDest);
}
setupAttachment(descriptor.colorAttachments[0], args, depthPlaneDest);
id<MTLRenderCommandEncoder> const encoder =
[cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
encoder.label = @(label);
BlitFunctionKey key;
key.blitColor = blitColor;
key.blitDepth = blitDepth;
key.msaaColorSource = blitColor && args.source.texture.textureType == MTLTextureType2DMultisample;
key.msaaDepthSource = blitDepth && args.source.texture.textureType == MTLTextureType2DMultisample;
key.sources3D = blitColor && args.source.texture.textureType == MTLTextureType3D;
if (key.sources3D && blitDepth) {
assert_invariant(args.source.texture.textureType == MTLTextureType3D);
}
key.msaaColorSource = args.source.texture.textureType == MTLTextureType2DMultisample;
key.sources3D = args.source.texture.textureType == MTLTextureType3D;
id<MTLFunction> const fragmentFunction = getBlitFragmentFunction(key);
MetalPipelineState const pipelineState {
@@ -247,7 +193,7 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
.fragmentFunction = fragmentFunction,
.vertexDescription = {},
.colorAttachmentPixelFormat = {
blitColor ? args.destination.texture.pixelFormat : MTLPixelFormatInvalid,
args.destination.texture.pixelFormat,
MTLPixelFormatInvalid,
MTLPixelFormatInvalid,
MTLPixelFormatInvalid,
@@ -256,8 +202,7 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
MTLPixelFormatInvalid,
MTLPixelFormatInvalid
},
.depthAttachmentPixelFormat =
blitDepth ? args.destination.texture.pixelFormat : MTLPixelFormatInvalid,
.depthAttachmentPixelFormat = MTLPixelFormatInvalid,
.sampleCount = 1,
.blendState = {}
};
@@ -267,21 +212,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
// For texture arrays, create a view of the texture at the given slice (layer).
if (blitColor) {
id<MTLTexture> srcTextureColor = args.source.texture;
if (srcTextureColor && srcTextureColor.textureType == MTLTextureType2DArray) {
srcTextureColor = createTextureViewWithSingleSlice(srcTextureColor, args.source.slice);
}
[encoder setFragmentTexture:srcTextureColor atIndex:0];
}
if (blitDepth) {
id<MTLTexture> srcTextureDepth = args.source.texture;
if (srcTextureDepth && srcTextureDepth.textureType == MTLTextureType2DArray) {
srcTextureDepth = createTextureViewWithSingleSlice(srcTextureDepth, args.source.slice);
}
[encoder setFragmentTexture:srcTextureDepth atIndex:1];
id<MTLTexture> srcTextureColor = args.source.texture;
if (srcTextureColor && srcTextureColor.textureType == MTLTextureType2DArray) {
srcTextureColor = createTextureViewWithSingleSlice(srcTextureColor, args.source.slice);
}
[encoder setFragmentTexture:srcTextureColor atIndex:0];
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
if (args.filter == SamplerMagFilter::NEAREST) {
@@ -310,7 +245,7 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
DepthStencilState const depthStencilState {
.depthCompare = MTLCompareFunctionAlways,
.depthWriteEnabled = blitDepth
.depthWriteEnabled = false
};
id<MTLDepthStencilState> const depthStencil =
mContext.depthStencilStateCache.getOrCreateState(depthStencilState);
@@ -370,18 +305,9 @@ void MetalBlitter::setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const {
MTLCompileOptions* const options = [MTLCompileOptions new];
NSMutableDictionary* const macros = [NSMutableDictionary dictionary];
if (key.blitColor) {
macros[@"BLIT_COLOR"] = @"1";
}
if (key.blitDepth) {
macros[@"BLIT_DEPTH"] = @"1";
}
if (key.msaaColorSource) {
macros[@"MSAA_COLOR_SOURCE"] = @"1";
}
if (key.msaaDepthSource) {
macros[@"MSAA_DEPTH_SOURCE"] = @"1";
}
if (key.sources3D) {
macros[@"SOURCES_3D"] = @"1";
}
+4 -1
View File
@@ -73,7 +73,10 @@ private:
std::unordered_set<MetalBufferPoolEntry const*> mUsedStages;
// Store the current "time" (really just a frame count) and LRU eviction parameters.
uint64_t mCurrentFrame = 0;
// An atomic is necessary as mCurrentFrame is incremented in gc() (called on
// the driver thread) and read from acquireBuffer() and releaseBuffer(),
// which may be called on non-driver threads.
std::atomic<uint64_t> mCurrentFrame = 0;
static constexpr uint32_t TIME_BEFORE_EVICTION = 10;
};
+4 -1
View File
@@ -142,7 +142,10 @@ struct MetalContext {
// Fences, only supported on macOS 10.14 and iOS 12 and above.
API_AVAILABLE(macos(10.14), ios(12.0))
MTLSharedEventListener* eventListener = nil;
uint64_t signalId = 1;
// signalId is incremented in the MetalFence constructor, which is called on
// both the driver (MetalTimerQueryFence::beginTimeElapsedQuery) and main
// threads (in createFenceS), so an atomic is necessary.
std::atomic<uint64_t> signalId = 1;
MetalTimerQueryInterface* timerQueryImpl;
+12 -1
View File
@@ -28,6 +28,10 @@
#include <utils/Log.h>
#include <utils/debug.h>
#include <functional>
#include <mutex>
#include <vector>
namespace filament {
namespace backend {
@@ -52,13 +56,13 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
friend class MetalSwapChain;
MetalPlatform& mPlatform;
MetalContext* mContext;
ShaderModel getShaderModel() const noexcept final;
@@ -66,6 +70,13 @@ private:
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final;
/*
* Tasks run regularly on the driver thread.
*/
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
*/
+34 -12
View File
@@ -166,6 +166,7 @@ MetalDriver::~MetalDriver() noexcept {
}
void MetalDriver::tick(int) {
executeTickOps();
}
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -583,6 +584,8 @@ void MetalDriver::terminate() {
// This must be done before calling bufferPool->reset() to ensure no buffers are in flight.
finish();
executeTickOps();
mContext->bufferPool->reset();
mContext->commandQueue = nil;
@@ -727,6 +730,10 @@ bool MetalDriver::isParallelShaderCompileSupported() {
return true;
}
bool MetalDriver::isDepthStencilResolveSupported() {
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1257,11 +1264,9 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
args.source.level = miplevel;
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
args.source.texture = srcTexture;
args.source.type = MetalBlitter::BlitArgs::Attachment::Type::COLOR;
args.destination.level = 0;
args.destination.region = MTLRegionMake2D(0, 0, readPixelsTexture.width, readPixelsTexture.height);
args.destination.texture = readPixelsTexture;
args.destination.type = MetalBlitter::BlitArgs::Attachment::Type::COLOR;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "readPixels blit");
@@ -1319,6 +1324,12 @@ void MetalDriver::resolve(
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
"src and dst texture format don't match");
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
"can't resolve depth formats");
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
"can't resolve stencil formats");
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
"texture doesn't have BLIT_DST");
@@ -1378,9 +1389,7 @@ void MetalDriver::blit(
// FIXME: we shouldn't need to know the type here. This is an artifact of using the old API.
args.source.texture = srcTexture->getMtlTextureForRead();
args.source.type = MetalBlitter::getFormatType(srcTexture->format);
args.destination.texture = dstTexture->getMtlTextureForWrite();
args.destination.type = MetalBlitter::getFormatType(dstTexture->format);
args.source.level = srcLevel;
args.source.slice = srcLayer;
args.destination.level = dstLevel;
@@ -1435,13 +1444,11 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
args.source.texture = srcColorAttachment.getTexture();
args.source.level = srcColorAttachment.level;
args.source.slice = srcColorAttachment.layer;
args.source.type = MetalBlitter::FormatType::COLOR;
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.destination.texture = dstColorAttachment.getTexture();
args.destination.level = dstColorAttachment.level;
args.destination.slice = dstColorAttachment.layer;
args.destination.type = MetalBlitter::FormatType::COLOR;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
}
@@ -1581,7 +1588,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
stencilPixelFormat = stencilAttachment.getPixelFormat();
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
}
MetalPipelineState pipelineState {
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
.vertexDescription = primitive->vertexDescription,
@@ -1599,13 +1606,13 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
.stencilAttachmentPixelFormat = stencilPixelFormat,
.sampleCount = mContext->currentRenderTarget->getSamples(),
.blendState = BlendState {
.blendingEnabled = rs.hasBlending(),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha),
.destinationRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionDstRGB),
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha)
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
.blendingEnabled = rs.hasBlending(),
},
.colorWrite = rs.colorWrite
};
@@ -1904,6 +1911,21 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<MetalDriver>;
+26 -9
View File
@@ -245,13 +245,30 @@ void MetalSwapChain::present() {
}
}
struct PresentDrawableData {
void* drawable = nullptr;
MetalDriver* driver = nullptr;
};
void presentDrawable(bool presentFrame, void* user) {
// CFBridgingRelease here is used to balance the CFBridgingRetain inside of acquireDrawable.
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>) CFBridgingRelease(user);
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
// CFBridgingRelease here is used to balance the CFBridgingRetain inside acquireDrawable.
id<CAMetalDrawable> drawable =
(id<CAMetalDrawable>)CFBridgingRelease(presentDrawableData->drawable);
if (presentFrame) {
[drawable present];
}
// The drawable will be released here when the "drawable" variable goes out of scope.
// Schedule the drawable destruction on the driver thread.
void* voidDrawable = (void*) CFBridgingRetain(drawable);
MetalDriver* driver = presentDrawableData->driver;
driver->runAtNextTick([voidDrawable]() {
// The drawable is released here.
CFBridgingRelease(voidDrawable);
});
delete presentDrawableData;
}
void MetalSwapChain::scheduleFrameScheduledCallback() {
@@ -266,13 +283,15 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// capture the _this_ pointer (MetalSwapChain*) instead of the drawable.
id<CAMetalDrawable> d = drawable;
void* userData = frameScheduledUserData;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
// CFBridgingRetain is used here to give the drawable a +1 retain count before
// casting it to a void*.
PresentCallable callable(presentDrawable, (void*) CFBridgingRetain(d));
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
callback(callable, userData);
});
auto* presentDrawableData = new PresentDrawableData;
presentDrawableData->drawable = (void*) CFBridgingRetain(d);
presentDrawableData->driver = driver;
PresentCallable callable(presentDrawable, (void*) presentDrawableData);
callback(callable, userData);
}];
}
@@ -801,12 +820,10 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
args.source.slice = 0;
args.source.region = sourceRegion;
args.source.texture = stagingTexture;
args.source.type = MetalBlitter::FormatType::COLOR;
args.destination.level = level;
args.destination.slice = slice;
args.destination.region = region;
args.destination.texture = destinationTexture;
args.destination.type = MetalBlitter::FormatType::COLOR;
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
}
@@ -103,10 +103,19 @@ void MetalShaderCompiler::terminate() noexcept {
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size() - 1
encoding:NSUTF8StringEncoding];
// By default, Metal uses the most recent language version.
MTLCompileOptions* options = [MTLCompileOptions new];
// Disable Fast Math optimizations.
// This ensures that operations adhere to IEEE standards for floating-point arithmetic,
// which is crucial for half precision floats in scenarios where fast math optimizations
// lead to inaccuracies, such as in handling special values like NaN or Infinity.
options.fastMathEnabled = NO;
NSError* error = nil;
// When options is nil, Metal uses the most recent language version available.
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:nil
options:options
error:&error];
if (library == nil) {
if (error) {
+4
View File
@@ -182,6 +182,10 @@ bool NoopDriver::isParallelShaderCompileSupported() {
return false;
}
bool NoopDriver::isDepthStencilResolveSupported() {
return true;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
+14 -2
View File
@@ -274,6 +274,7 @@ void OpenGLContext::setDefaultState() noexcept {
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if !defined(__EMSCRIPTEN__)
if (ext.EXT_clip_control) {
#if defined(BACKEND_OPENGL_VERSION_GL)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
@@ -281,6 +282,7 @@ void OpenGLContext::setDefaultState() noexcept {
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
#endif
}
#endif
if (ext.EXT_clip_cull_distance) {
glEnable(GL_CLIP_DISTANCE0);
@@ -309,7 +311,9 @@ void OpenGLContext::initProcs(Procs* procs,
# ifdef BACKEND_OPENGL_VERSION_GL
procs->getQueryObjectui64v = glGetQueryObjectui64v; // only core in GL
# elif defined(GL_EXT_disjoint_timer_query)
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
# ifndef __EMSCRIPTEN__
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
# endif
# endif // BACKEND_OPENGL_VERSION_GL
// core in ES 3.0 and GL 4.3
@@ -321,6 +325,7 @@ void OpenGLContext::initProcs(Procs* procs,
#ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef IOS // IOS is guaranteed to have ES3.x
# ifndef __EMSCRIPTEN__
if (UTILS_UNLIKELY(major == 2)) {
// Runtime OpenGL version is ES 2.x
if (UTILS_LIKELY(ext.OES_vertex_array_object)) {
@@ -348,6 +353,7 @@ void OpenGLContext::initProcs(Procs* procs,
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsKHR;
}
# endif // __EMSCRIPTEN__
# endif // IOS
#else
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsARB;
@@ -580,17 +586,23 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
// figure out and initialize the extensions we need
using namespace std::literals;
ext->APPLE_color_buffer_packed_float = exts.has("GL_APPLE_color_buffer_packed_float"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_clip_control = exts.has("GL_EXT_clip_control"sv);
#endif
ext->EXT_clip_cull_distance = exts.has("GL_EXT_clip_cull_distance"sv);
ext->EXT_color_buffer_float = exts.has("GL_EXT_color_buffer_float"sv);
ext->EXT_color_buffer_half_float = exts.has("GL_EXT_color_buffer_half_float"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
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);
#endif
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
#if !defined(__EMSCRIPTEN__)
#ifndef __EMSCRIPTEN__
ext->EXT_texture_compression_etc2 = true;
#endif
ext->EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
+19 -5
View File
@@ -537,7 +537,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
if (UTILS_UNLIKELY(mContext.isES2())) {
// Here we patch the specification constants to enable or not the rec709 output
// Here we patch the specialization constants to enable or not the rec709 output
// color space emulation in this program. Obviously, the backend shouldn't know about
// specific spec-constants, so we need to handle failures gracefully. This cannot be
// done at Material creation time because only the backend has access to
@@ -1064,6 +1064,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
}
CHECK_GL_ERROR(utils::slog.e)
} else
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_multisampled_render_to_texture
// EXT_multisampled_render_to_texture only support GL_COLOR_ATTACHMENT0
if (!attachmentTypeNotSupportedByMSRTT && (t->depth <= 1)
@@ -1076,7 +1077,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
// This extension only exists on OpenGL ES.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
glext::glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,
attachment, target, t->gl.id, binfo.level, rt->gl.samples);
} else {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
@@ -1084,7 +1085,8 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
}
CHECK_GL_ERROR(utils::slog.e)
} else
#endif
#endif // GL_EXT_multisampled_render_to_texture
#endif // __EMSCRIPTEN__
if (!any(t->usage & TextureUsage::SAMPLEABLE) && t->samples > 1) {
assert_invariant(rt->gl.samples > 1);
assert_invariant(glIsRenderbuffer(t->gl.id));
@@ -1183,6 +1185,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
uint32_t height, uint8_t samples) const noexcept {
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
if (samples > 1) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_multisampled_render_to_texture
auto& gl = mContext;
if (gl.ext.EXT_multisampled_render_to_texture ||
@@ -1190,7 +1193,8 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
samples, internalformat, width, height);
} else
#endif
#endif // GL_EXT_multisampled_render_to_texture
#endif // __EMSCRIPTEN__
{
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glRenderbufferStorageMultisample(GL_RENDERBUFFER,
@@ -1906,6 +1910,10 @@ bool OpenGLDriver::isParallelShaderCompileSupported() {
return mShaderCompilerService.isParallelShaderCompileSupported();
}
bool OpenGLDriver::isDepthStencilResolveSupported() {
return true;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -2283,6 +2291,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
PixelBufferDescriptor&& p) {
auto& gl = mContext;
assert_invariant(t != nullptr);
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
assert_invariant(yoffset + height <= std::max(1u, t->height >> level));
assert_invariant(t->samples <= 1);
@@ -2981,16 +2990,18 @@ GLuint OpenGLDriver::getSamplerSlow(SamplerParams params) const noexcept {
#endif
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
auto& gl = mContext;
if (gl.ext.EXT_debug_marker) {
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
}
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
@@ -3003,9 +3014,11 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
#endif
}
void OpenGLDriver::popGroupMarker(int) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
@@ -3018,6 +3031,7 @@ void OpenGLDriver::popGroupMarker(int) {
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
#endif
}
void OpenGLDriver::startCapture(int) {
@@ -547,7 +547,8 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
// add support for ARB_shading_language_packing if needed
auto const packingFunctions = process_ARB_shading_language_packing(context);
// split shader source, so we can insert the specification constants and the packing functions
// split shader source, so we can insert the specialization constants and the packing
// functions
auto const [prolog, body] = splitShaderSource(source);
const std::array<const char*, 4> sources = {
+4 -1
View File
@@ -28,6 +28,7 @@ static void getProcAddress(T& pfn, const char* name) noexcept {
}
namespace glext {
#ifndef __EMSCRIPTEN__
#ifdef GL_OES_EGL_image
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
#endif
@@ -72,10 +73,11 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
// use getProcAddress for ES3.1 and above entry points.
PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
static std::once_flag sGlExtInitialized;
#endif // __EMSCRIPTEN__
void importGLESExtensionsEntryPoints() {
#ifndef __EMSCRIPTEN__
std::call_once(sGlExtInitialized, +[]() {
#ifdef GL_OES_EGL_image
getProcAddress(glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES");
@@ -119,6 +121,7 @@ void importGLESExtensionsEntryPoints() {
getProcAddress(glDispatchCompute, "glDispatchCompute");
#endif
});
#endif // __EMSCRIPTEN__
}
} // namespace glext
+2
View File
@@ -112,6 +112,7 @@ namespace glext {
// it is currently called from PlatformEGL.
void importGLESExtensionsEntryPoints();
#ifndef __EMSCRIPTEN__
#ifdef GL_OES_EGL_image
extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
#endif
@@ -153,6 +154,7 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
#endif // __EMSCRIPTEN__
} // namespace glext
using namespace glext;
+1 -1
View File
@@ -455,7 +455,7 @@ io::ostream& operator<<(io::ostream& out, filament::backend::Viewport const& vie
io::ostream& operator<<(io::ostream& out, TargetBufferFlags flags) {
// TODO: implement decoding of enum
out << uint8_t(flags);
out << uint32_t(flags);
return out;
}
+3 -280
View File
@@ -154,18 +154,14 @@ struct BlitterUniforms {
}// anonymous namespace
VulkanBlitter::VulkanBlitter(VulkanStagePool& stagePool, VulkanPipelineCache& pipelineCache,
VulkanFboCache& fboCache, VulkanSamplerCache& samplerCache) noexcept
: mStagePool(stagePool), mPipelineCache(pipelineCache), mFramebufferCache(fboCache),
mSamplerCache(samplerCache) {}
VulkanBlitter::VulkanBlitter() noexcept = default;
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept {
VmaAllocator allocator, VulkanCommands* commands) noexcept {
mPhysicalDevice = physicalDevice;
mDevice = device;
mAllocator = allocator;
mCommands = commands;
mEmptyTexture = emptyTexture;
}
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
@@ -173,14 +169,7 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
// src and dst should have the same aspect here
VkImageAspectFlags const aspect = src.texture->getImageAspect();
if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT &&
src.texture->samples > 1 && dst.texture->samples == 1) {
// we're resolving the depth here, which is not supported by the blitter in vulkan
// I think, eventually we want to do that inside filament, instead.
// FIXME: remove this once filament does it
resolveSlowDepth(src, dst);
return;
}
assert_invariant(!(aspect & VK_IMAGE_ASPECT_DEPTH_BIT));
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT)
VkPhysicalDevice const gpu = mPhysicalDevice;
@@ -231,272 +220,6 @@ void VulkanBlitter::blit(VkFilter filter,
}
void VulkanBlitter::terminate() noexcept {
if (mDepthResolveProgram) {
delete mDepthResolveProgram;
mDepthResolveProgram = nullptr;
}
if (mTriangleBuffer) {
delete mTriangleBuffer;
mTriangleBuffer = nullptr;
}
if (mParamsBuffer) {
delete mParamsBuffer;
mParamsBuffer = nullptr;
}
}
// If we created these shader modules in the constructor, the device might not be ready yet.
// It is easier to do lazy initialization, which can also improve load time.
void VulkanBlitter::lazyInit() noexcept {
if (mDepthResolveProgram != nullptr) {
return;
}
assert_invariant(mDevice);
auto decode = [device = mDevice](const uint8_t* compressed, int compressedSize) {
const size_t spirvSize = smolv::GetDecodedBufferSize(compressed, compressedSize);
FixedCapacityVector<uint8_t> spirv(spirvSize);
smolv::Decode(compressed, compressedSize, spirv.data(), spirvSize);
VkShaderModuleCreateInfo moduleInfo = {};
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleInfo.codeSize = spirvSize;
moduleInfo.pCode = (uint32_t*) spirv.data();
VkShaderModule result = VK_NULL_HANDLE;
vkCreateShaderModule(device, &moduleInfo, VKALLOC, &result);
assert_invariant(result);
return result;
};
VkShaderModule vertexShader = decode(VKSHADERS_BLITDEPTHVS_DATA, VKSHADERS_BLITDEPTHVS_SIZE);
VkShaderModule fragmentShader = decode(VKSHADERS_BLITDEPTHFS_DATA, VKSHADERS_BLITDEPTHFS_SIZE);
// Allocate one anonymous sampler at slot 0.
VulkanProgram::CustomSamplerInfoList const samplers = {
{ 0, 0, ShaderStageFlags::FRAGMENT },
};
mDepthResolveProgram = new(std::nothrow) VulkanProgram(mDevice, vertexShader, fragmentShader, samplers);
#if FVK_ENABLED(FVK_DEBUG_BLITTER)
utils::slog.d << "Created Shader Module for VulkanBlitter "
<< "shaders = (" << vertexShader << ", " << fragmentShader << ")"
<< utils::io::endl;
#endif
static const float kTriangleVertices[] = {
-1.0f, -1.0f,
+1.0f, -1.0f,
-1.0f, +1.0f,
+1.0f, +1.0f,
};
VulkanCommandBuffer const& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer();
mTriangleBuffer = new(std::nothrow) VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
sizeof(kTriangleVertices));
mTriangleBuffer->loadFromCpu(cmdbuffer, kTriangleVertices, 0, sizeof(kTriangleVertices));
mParamsBuffer = new(std::nothrow) VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
sizeof(BlitterUniforms));
}
// At a high level, the procedure for resolving depth looks like this:
// 1. Load uniforms
// 2. Begin render pass
// 3. Draw a big triangle
// 4. End render pass.
void VulkanBlitter::resolveSlowDepth(VulkanAttachment src, VulkanAttachment dst) {
lazyInit();
const VkOffset3D dstRect[2] = {
{ 0, 0, 0 },
{ (int32_t)dst.texture->width, (int32_t)dst.texture->height, 1 }};
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer const cmdbuffer = commands->buffer();
commands->acquire(src.texture);
commands->acquire(dst.texture);
BlitterUniforms const uniforms = {
.sampleCount = src.texture->samples,
.inverseSampleCount = 1.0f / float(src.texture->samples),
};
mParamsBuffer->loadFromCpu(cmdbuffer, &uniforms, 0, sizeof(uniforms));
VkImageAspectFlags const aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
// We will transition the src into sampler layout and also keep it in sampler layout for
// consistency.
VulkanLayout const samplerLayout = VulkanLayout::DEPTH_SAMPLER;
// BEGIN RENDER PASS
// -----------------
const VulkanFboCache::RenderPassKey rpkey = {
.initialColorLayoutMask = 0,
.initialDepthLayout = VulkanLayout::UNDEFINED,
.renderPassDepthLayout = samplerLayout,
.finalDepthLayout = samplerLayout,
.depthFormat = dst.getFormat(),
.clear = {},
.discardStart = TargetBufferFlags::DEPTH,
.samples = 1,
};
const VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, 0);
const VulkanFboCache::FboKey fbkey {
.renderPass = renderPass,
.width = (uint16_t) (dst.texture->width >> dst.level),
.height = (uint16_t) (dst.texture->height >> dst.level),
.layers = 1,
.samples = rpkey.samples,
.color = {},
.resolve = {},
.depth = dst.getImageView(aspect),
};
const VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
VkRenderPassBeginInfo renderPassInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = renderPass,
.framebuffer = vkfb,
};
renderPassInfo.renderArea.offset.x = dstRect[0].x;
renderPassInfo.renderArea.offset.y = dstRect[0].y;
renderPassInfo.renderArea.extent.width = dstRect[1].x - dstRect[0].x;
renderPassInfo.renderArea.extent.height = dstRect[1].y - dstRect[0].y;
// We need to transition the source into a sampler since it'll be sampled in the shader.
const VkImageSubresourceRange srcRange = {
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
};
src.texture->transitionLayout(cmdbuffer, srcRange, samplerLayout);
vkCmdBeginRenderPass(cmdbuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
VkViewport const viewport = {
.x = (float) dstRect[0].x,
.y = (float) dstRect[0].y,
.width = (float) renderPassInfo.renderArea.extent.width,
.height = (float) renderPassInfo.renderArea.extent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
// DRAW THE TRIANGLE
// -----------------
mPipelineCache.bindProgram(mDepthResolveProgram);
mPipelineCache.bindPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
auto vkraster = mPipelineCache.getCurrentRasterState();
vkraster.depthWriteEnable = true;
vkraster.depthCompareOp = SamplerCompareFunc::A;
vkraster.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
vkraster.alphaToCoverageEnable = false,
vkraster.blendEnable = VK_FALSE,
vkraster.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
vkraster.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
vkraster.colorBlendOp = BlendEquation::ADD,
vkraster.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
vkraster.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
vkraster.alphaBlendOp = BlendEquation::ADD,
vkraster.colorWriteMask = (VkColorComponentFlags) 0,
vkraster.cullMode = VK_CULL_MODE_NONE;
vkraster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
vkraster.depthBiasEnable = VK_FALSE;
vkraster.colorTargetCount = 0;
mPipelineCache.bindRasterState(vkraster);
VkBuffer buffers[1] = {};
VkDeviceSize offsets[1] = {};
buffers[0] = mTriangleBuffer->getGpuBuffer();
VkVertexInputAttributeDescription const attribDesc = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
};
VkVertexInputBindingDescription const bufferDesc = {
.binding = 0,
.stride = sizeof(float) * 2,
};
mPipelineCache.bindVertexArray(&attribDesc, &bufferDesc, 1);
// Select nearest filtering and clamp_to_edge.
VkSampler vksampler = mSamplerCache.getSampler({});
VkDescriptorImageInfo samplers[VulkanPipelineCache::SAMPLER_BINDING_COUNT];
VulkanTexture* textures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
for (auto& sampler : samplers) {
sampler = {
.sampler = vksampler,
.imageView = mEmptyTexture->getPrimaryImageView(),
.imageLayout = ImgUtil::getVkLayout(VulkanLayout::READ_WRITE),
};
}
samplers[0] = {
.sampler = vksampler,
.imageView = src.getImageView(VK_IMAGE_ASPECT_DEPTH_BIT),
.imageLayout = ImgUtil::getVkLayout(samplerLayout),
};
textures[0] = src.texture;
mPipelineCache.bindSamplers(samplers, textures,
VulkanPipelineCache::getUsageFlags(0, ShaderStageFlags::FRAGMENT));
auto previousUbo = mPipelineCache.getUniformBufferBinding(0);
mPipelineCache.bindUniformBuffer(0, mParamsBuffer->getGpuBuffer());
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
assert_invariant(false);
}
const VkRect2D scissor = {
.offset = renderPassInfo.renderArea.offset,
.extent = renderPassInfo.renderArea.extent,
};
mPipelineCache.bindScissor(cmdbuffer, scissor);
if (!mPipelineCache.bindPipeline(commands)) {
assert_invariant(false);
}
vkCmdBindVertexBuffers(cmdbuffer, 0, 1, buffers, offsets);
vkCmdDraw(cmdbuffer, 4, 1, 0, 0);
// END RENDER PASS
// ---------------
vkCmdEndRenderPass(cmdbuffer);
VkMemoryBarrier const barrier {
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
VkPipelineStageFlags const srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
vkCmdPipelineBarrier(cmdbuffer, srcStageMask,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | // <== For Mali
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 1, &barrier, 0, nullptr, 0, nullptr);
mPipelineCache.bindUniformBuffer(0, previousUbo.buffer, previousUbo.offset, previousUbo.size);
}
} // namespace filament::backend
+3 -18
View File
@@ -32,11 +32,10 @@ struct VulkanProgram;
class VulkanBlitter {
public:
VulkanBlitter(VulkanStagePool& stagePool, VulkanPipelineCache& pipelineCache,
VulkanFboCache& fboCache, VulkanSamplerCache& samplerCache) noexcept;
VulkanBlitter() noexcept;
void initialize(VkPhysicalDevice physicalDevice, VkDevice device, VmaAllocator allocator,
VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept;
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands) noexcept;
void blit(VkFilter filter,
VulkanAttachment dst, const VkOffset3D* dstRectPair,
@@ -47,24 +46,10 @@ public:
void terminate() noexcept;
private:
void lazyInit() noexcept;
void resolveSlowDepth(VulkanAttachment src, VulkanAttachment dst);
VulkanBuffer* mTriangleBuffer = nullptr;
VulkanBuffer* mParamsBuffer = nullptr;
VulkanProgram* mDepthResolveProgram = nullptr;
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
VkDevice mDevice;
VmaAllocator mAllocator;
VulkanCommands* mCommands;
VulkanTexture* mEmptyTexture;
VulkanStagePool& mStagePool;
VulkanPipelineCache& mPipelineCache;
VulkanFboCache& mFramebufferCache;
VulkanSamplerCache& mSamplerCache;
};
} // namespace filament::backend
+14 -3
View File
@@ -156,7 +156,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mResourceManager(&mResourceAllocator),
mThreadSafeResourceManager(&mResourceAllocator),
mPipelineCache(&mResourceAllocator),
mBlitter(mStagePool, mPipelineCache, mFramebufferCache, mSamplerCache),
mReadPixels(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
@@ -206,7 +205,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
mCommands.get(), mEmptyTexture.get());
mCommands.get());
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -757,6 +756,7 @@ bool VulkanDriver::isFrameTimeSupported() {
}
bool VulkanDriver::isAutoDepthResolveSupported() {
// TODO: this could be supported with vk 1.2 or VK_KHR_depth_stencil_resolve
return false;
}
@@ -772,6 +772,11 @@ bool VulkanDriver::isParallelShaderCompileSupported() {
return false;
}
bool VulkanDriver::isDepthStencilResolveSupported() {
// TODO: apparently it could be supported in core 1.2 and/or with VK_KHR_depth_stencil_resolve
return false;
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -1497,6 +1502,12 @@ void VulkanDriver::resolve(
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
"src and dst texture format don't match");
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
"can't resolve depth formats");
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
"can't resolve stencil formats");
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
"texture doesn't have BLIT_DST");
@@ -1545,7 +1556,7 @@ void VulkanDriver::blit(
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
// no scallng guaranteed
// no scaling guaranteed
mBlitter.blit(VK_FILTER_NEAREST,
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer }, dstOffsets,
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer }, srcOffsets);
+43 -3
View File
@@ -293,6 +293,7 @@ struct DepthOfFieldOptions {
MEDIAN
};
float cocScale = 1.0f; //!< circle of confusion scale factor (amount of blur)
float cocAspectRatio = 1.0f; //!< width/height aspect ratio of the circle of confusion (simulate anamorphic lenses)
float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation)
bool enabled = false; //!< enable or disable depth of field effect
Filter filter = Filter::MEDIAN; //!< filter to use for filling gaps in the kernel
@@ -401,7 +402,7 @@ struct AmbientOcclusionOptions {
};
/**
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
* Options for Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
*/
struct MultiSampleAntiAliasingOptions {
@@ -424,12 +425,51 @@ struct MultiSampleAntiAliasingOptions {
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth`, `feedback` and `jitterPattern`, however, can be changed at any time.
*
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
*
* @see setTemporalAntiAliasingOptions()
*/
struct TemporalAntiAliasingOptions {
float filterWidth = 1.0f; //!< reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
float feedback = 0.04f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
float filterWidth = 1.0f; //!< reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
float feedback = 0.12f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
float lodBias = -1.0f; //!< texturing lod bias (typically -1 or -2)
bool enabled = false; //!< enables or disables temporal anti-aliasing
bool upscaling = false; //!< 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
enum class BoxType : uint8_t {
AABB, //!< use an AABB neighborhood
VARIANCE, //!< use the variance of the neighborhood (not recommended)
AABB_VARIANCE //!< use both AABB and variance
};
enum class BoxClipping : uint8_t {
ACCURATE, //!< Accurate box clipping
CLAMP, //!< clamping
NONE //!< no rejections (use for debugging)
};
enum class JitterPattern : uint8_t {
RGSS_X4, //! 4-samples, rotated grid sampling
UNIFORM_HELIX_X4, //! 4-samples, uniform grid in helix sequence
HALTON_23_X8, //! 8-samples of halton 2,3
HALTON_23_X16 //! 16-samples of halton 2,3
};
bool filterHistory = true; //!< whether to filter the history buffer
bool filterInput = true; //!< whether to apply the reconstruction filter to the input
bool useYCoCg = false; //!< whether to use the YcoCg color-space for history rejection
BoxType boxType = BoxType::AABB; //!< type of color gamut box
BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm
JitterPattern jitterPattern = JitterPattern::HALTON_23_X16; //! Jitter Pattern
float varianceGamma = 1.0f; //! High values increases ghosting artefact, lower values increases jittering, range [0.75, 1.25]
bool preventFlickering = false; //!< adjust the feedback dynamically to reduce flickering
bool historyReprojection = true; //!< whether to apply history reprojection (debug option)
};
/**
+6
View File
@@ -252,6 +252,12 @@ public:
* automatically schedule itself for presentation. Instead, the application must call the
* PresentCallable passed to the FrameScheduledCallback.
*
* If your application delays the call to the PresentCallable by, for example, calling it on a
* separate thread, you must ensure all PresentCallables have been called before shutting down
* the Filament Engine. You can do this by issuing an Engine::flushAndWait before calling
* Engine::shutdown. This is necessary to ensure the Filament Engine has had a chance to clean
* up all memory related to frame presentation.
*
* @param callback A callback, or nullptr to unset.
* @param user An optional pointer to user data passed to the callback function.
*
+1 -2
View File
@@ -71,8 +71,7 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction,
s.clipControl = engine.getDriverApi().getClipSpaceParams();
}
void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction,
float bias) noexcept {
void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction, float bias) noexcept {
auto& s = edit(transaction);
s.lodBias = bias;
}
+3 -2
View File
@@ -50,7 +50,7 @@ PerViewUniforms::PerViewUniforms(FEngine& engine) noexcept
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
if (engine.getDFG().isValid()) {
TextureSampler sampler(TextureSampler::MagFilter::LINEAR);
TextureSampler const sampler(TextureSampler::MagFilter::LINEAR);
mSamplers.setSampler(PerViewSib::IBL_DFG_LUT,
{ engine.getDFG().getTexture(), sampler.getSamplerParams() });
}
@@ -96,9 +96,10 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n
s.clipControl = engine.getDriverApi().getClipSpaceParams();
}
void PerViewUniforms::prepareLodBias(float bias) noexcept {
void PerViewUniforms::prepareLodBias(float bias, float2 derivativesScale) noexcept {
auto& s = mUniforms.edit();
s.lodBias = bias;
s.derivativesScale = derivativesScale;
}
void PerViewUniforms::prepareExposure(float ev100) noexcept {
+1 -1
View File
@@ -69,7 +69,7 @@ public:
void terminate(backend::DriverApi& driver);
void prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept;
void prepareLodBias(float bias) noexcept;
void prepareLodBias(float bias, math::float2 derivativesScale) noexcept;
/*
* @param viewport viewport (should be same as RenderPassParams::viewport)
+212 -51
View File
@@ -27,9 +27,12 @@
#include "details/Engine.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphResources.h"
#include "fg/FrameGraphTexture.h"
#include "fsr.h"
#include "FrameHistory.h"
#include "PerViewUniforms.h"
#include "RenderPass.h"
@@ -41,13 +44,44 @@
#include "generated/resources/materials.h"
#include <filament/Material.h>
#include <filament/MaterialEnums.h>
#include <filament/Options.h>
#include <filament/Viewport.h>
#include <private/filament/EngineEnums.h>
#include <backend/DriverEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/PixelBufferDescriptor.h>
#include <private/backend/BackendUtils.h>
#include <math/half.h>
#include <math/mat2.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/scalar.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/algorithm.h>
#include <utils/BitmaskEnum.h>
#include <utils/debug.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <algorithm>
#include <cmath>
#include <limits>
#include <string_view>
#include <variant>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
@@ -167,7 +201,21 @@ FMaterialInstance* PostProcessManager::PostProcessMaterial::getMaterialInstance(
// ------------------------------------------------------------------------------------------------
const math::float2 PostProcessManager::sHaltonSamples[16] = {
const PostProcessManager::JitterSequence<4> PostProcessManager::sRGSS4 = {{
{ 0.625f, 0.125f },
{ 0.125f, 0.375f },
{ 0.875f, 0.625f },
{ 0.375f, 0.875f }
}};
const PostProcessManager::JitterSequence<4> PostProcessManager::sUniformHelix4 = {{
{ 0.25f, 0.25f },
{ 0.75f, 0.75f },
{ 0.25f, 0.75f },
{ 0.75f, 0.25f },
}};
const PostProcessManager::JitterSequence<16> PostProcessManager::sHaltonSamples = {{
{ filament::halton( 0, 2), filament::halton( 0, 3) },
{ filament::halton( 1, 2), filament::halton( 1, 3) },
{ filament::halton( 2, 2), filament::halton( 2, 3) },
@@ -184,7 +232,7 @@ const math::float2 PostProcessManager::sHaltonSamples[16] = {
{ filament::halton(13, 2), filament::halton(13, 3) },
{ filament::halton(14, 2), filament::halton(14, 3) },
{ filament::halton(15, 2), filament::halton(15, 3) }
};
}};
PostProcessManager::PostProcessManager(FEngine& engine) noexcept
: mEngine(engine),
@@ -200,7 +248,8 @@ void PostProcessManager::registerPostProcessMaterial(std::string_view name, Mate
}
UTILS_NOINLINE
PostProcessManager::PostProcessMaterial& PostProcessManager::getPostProcessMaterial(std::string_view name) noexcept {
PostProcessManager::PostProcessMaterial& PostProcessManager::getPostProcessMaterial(
std::string_view name) noexcept {
assert_invariant(mMaterialRegistry.find(name) != mMaterialRegistry.end());
return mMaterialRegistry[name];
}
@@ -258,6 +307,7 @@ static const PostProcessManager::MaterialInfo sMaterialList[] = {
{ "fsr_easu_mobileF", MATERIAL(FSR_EASU_MOBILEF) },
{ "fsr_rcas", MATERIAL(FSR_RCAS) },
{ "debugShadowCascades", MATERIAL(DEBUGSHADOWCASCADES) },
{ "resolveDepth", MATERIAL(RESOLVEDEPTH) },
};
void PostProcessManager::init() noexcept {
@@ -754,7 +804,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
auto ssao = resources.getRenderPassInfo();
auto const& desc = resources.getDescriptor(data.depth);
// estimate of the size in pixel of a 1m tall/wide object viewed from 1m away (i.e. at z=-1)
// Estimate of the size in pixel units of a 1m tall/wide object viewed from 1m away (i.e. at z=-1)
const float projectionScale = std::min(
0.5f * cameraInfo.projection[0].x * desc.width,
0.5f * cameraInfo.projection[1].y * desc.height);
@@ -911,7 +961,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
auto const& desc = resources.getDescriptor(data.blurred);
// unnormalized gaussian half-kernel of a given standard deviation
// returns number of samples stored in array (max 16)
// returns number of samples stored in the array (max 16)
constexpr size_t kernelArraySize = 16; // limited by bilateralBlur.mat
auto gaussianKernel =
[kernelArraySize](float* outKernel, size_t gaussianWidth, float stdDev) -> uint32_t {
@@ -955,7 +1005,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateGaussianMipmap(Frame
auto const subResourceDesc = fg.getSubResourceDescriptor(input);
// create one subresource per level to be generated from the input. These will be our
// Create one subresource per level to be generated from the input. These will be our
// destinations.
struct MipmapPassData {
FixedCapacityVector<FrameGraphId<FrameGraphTexture>> out;
@@ -1116,7 +1166,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
size_t const m = computeGaussianCoefficients(kernel,
std::min(sizeof(kernel) / sizeof(*kernel), kernelStorageSize));
std::string_view sourceParameterName = is2dArray ? "sourceArray"sv : "source"sv;
std::string_view const sourceParameterName = is2dArray ? "sourceArray"sv : "source"sv;
// horizontal pass
mi->setParameter(sourceParameterName, hwIn, {
.filterMag = SamplerMagFilter::LINEAR,
@@ -1162,7 +1212,7 @@ PostProcessManager::ScreenSpaceRefConfig PostProcessManager::prepareMipmapSSR(Fr
// The kernel-size was determined empirically so that we don't get too many artifacts
// due to the down-sampling with a box filter (which happens implicitly).
// requires only 6 stored coefficients and 11 tap/pass
// Requires only 6 stored coefficients and 11 tap/pass
// e.g.: size of 13 (4 stored coefficients)
// +-------+-------+-------*===*-------+-------+-------+
// ... | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ...
@@ -1326,10 +1376,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateMipmapSSR(
FrameGraphId<FrameGraphTexture> output,
bool needInputDuplication, ScreenSpaceRefConfig const& config) noexcept {
// descriptor of our actual input image (e.g. reflection buffer or refraction framebuffer)
// Descriptor of our actual input image (e.g. reflection buffer or refraction framebuffer)
auto const& desc = fg.getDescriptor(input);
// descriptor of the destination. output is a subresource (i.e. a layer of a 2D array)
// Descriptor of the destination. `output` is a subresource (i.e. a layer of a 2D array)
auto const& outDesc = fg.getDescriptor(output);
/*
@@ -1378,7 +1428,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> depth,
const CameraInfo& cameraInfo,
bool translucent,
float bokehAspectRatio,
float2 bokehScale,
const DepthOfFieldOptions& dofOptions) noexcept {
assert_invariant(depth);
@@ -1389,7 +1439,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
const TextureFormat format = translucent ? TextureFormat::RGBA16F
: TextureFormat::R11F_G11F_B10F;
// rotate the bokeh based on the aperture diameter (i.e. angle of the blades)
// Rotate the bokeh based on the aperture diameter (i.e. angle of the blades)
float bokehAngle = f::PI / 6.0f;
if (dofOptions.maxApertureDiameter > 0.0f) {
bokehAngle += f::PI_2 * saturate(cameraInfo.A / dofOptions.maxApertureDiameter);
@@ -1768,8 +1818,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("tiles", tilesCocMinMax,
{ .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("cocToTexelScale", float2{
bokehAspectRatio / (inputDesc.width * dofResolution),
1.0 / (inputDesc.height * dofResolution)
bokehScale.x / (inputDesc.width * dofResolution),
bokehScale.y / (inputDesc.height * dofResolution)
});
mi->setParameter("cocToPixelScale", (1.0f / float(dofResolution)));
mi->setParameter("ringCounts", float4{
@@ -1875,14 +1925,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
return ppDoFCombine->output;
}
PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
BloomOptions& inoutBloomOptions,
backend::TextureFormat outFormat,
math::float2 scale) noexcept {
return bloomPass(fg, input, outFormat, inoutBloomOptions, scale);
}
FrameGraphId<FrameGraphTexture> PostProcessManager::downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
@@ -1914,9 +1956,11 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::downscalePass(FrameGraph& fg
return downsamplePass->output;
}
PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg,
PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, TextureFormat outFormat,
BloomOptions& inoutBloomOptions, float2 scale) noexcept {
BloomOptions& inoutBloomOptions,
TemporalAntiAliasingOptions const& taaOptions,
float2 scale) noexcept {
// Figure out a good size for the bloom buffer. We must use a fixed bloom buffer size so
// that the size/strength of the bloom doesn't vary much with the resolution, otherwise
@@ -1958,6 +2002,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
bool threshold = inoutBloomOptions.threshold;
// we don't need to do the fireflies reduction if we have TAA (it already does it)
bool fireflies = threshold && !taaOptions.enabled;
while (2 * bloomWidth < float(desc.width) || 2 * bloomHeight < float(desc.height)) {
if (inoutBloomOptions.quality == QualityLevel::LOW ||
inoutBloomOptions.quality == QualityLevel::MEDIUM) {
@@ -1966,8 +2013,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
.height = (desc.height = std::max(1u, desc.height / 2)),
.format = outFormat
},
threshold, inoutBloomOptions.highlight, threshold);
threshold, inoutBloomOptions.highlight, fireflies);
threshold = false; // we do the thresholding only once during down sampling
fireflies = false; // we do the fireflies reduction only once during down sampling
} else if (inoutBloomOptions.quality == QualityLevel::HIGH ||
inoutBloomOptions.quality == QualityLevel::ULTRA) {
// In high quality mode, we increase the size of the bloom buffer such that the
@@ -1988,7 +2036,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
input = downscalePass(fg, input,
{ .width = width, .height = height, .format = outFormat },
threshold, inoutBloomOptions.highlight, threshold);
threshold, inoutBloomOptions.highlight, fireflies);
struct BloomPassData {
FrameGraphId<FrameGraphTexture> out;
@@ -2497,7 +2545,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
return ppFXAA->output;
}
void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& svp,
void PostProcessManager::prepareTaa(FrameGraph& fg,
filament::Viewport const& svp,
TemporalAntiAliasingOptions const& taaOptions,
FrameHistory& frameHistory,
FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa,
CameraInfo* inoutCameraInfo,
@@ -2509,9 +2559,33 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
current.projection = inoutCameraInfo->projection * inoutCameraInfo->getUserViewMatrix();
current.frameId = previous.frameId + 1;
auto jitterPosition = [pattern = taaOptions.jitterPattern](size_t frameIndex){
using JitterPattern = TemporalAntiAliasingOptions::JitterPattern;
switch (pattern) {
case JitterPattern::RGSS_X4:
return sRGSS4(frameIndex);
case JitterPattern::UNIFORM_HELIX_X4:
return sUniformHelix4(frameIndex);
case JitterPattern::HALTON_23_X8:
return sHaltonSamples(frameIndex % 8);
case JitterPattern::HALTON_23_X16:
return sHaltonSamples(frameIndex);
}
};
// sample position within a pixel [-0.5, 0.5]
float2 const jitter = halton(previous.frameId) - 0.5f;
current.jitter = jitter;
// for metal/vulkan we need to reverse the y-offset
current.jitter = jitterPosition(previous.frameId);
float2 jitter = current.jitter;
switch (mEngine.getBackend()) {
case Backend::VULKAN:
case Backend::METAL:
jitter.y = -jitter.y;
UTILS_FALLTHROUGH;
case Backend::OPENGL:
default:
break;
}
float2 const jitterInClipSpace = jitter * (2.0f / float2{ svp.width, svp.height });
@@ -2528,6 +2602,37 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
});
}
void PostProcessManager::configureTemporalAntiAliasingMaterial(
TemporalAntiAliasingOptions const& taaOptions) noexcept {
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
bool dirty = false;
auto setConstantParameter =
[&dirty](FMaterial* const ma, std::string_view name, auto value) noexcept {
auto id = ma->getSpecializationConstantId(name);
if (id.has_value()) {
if (ma->setConstant(id.value(), value)) {
dirty = true;
}
}
};
setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection);
setConstantParameter(ma, "filterHistory", taaOptions.filterHistory);
setConstantParameter(ma, "filterInput", taaOptions.filterInput);
setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg);
setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering);
setConstantParameter(ma, "boxType", (int32_t)taaOptions.boxType);
setConstantParameter(ma, "boxClipping", (int32_t)taaOptions.boxClipping);
setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma);
if (dirty) {
ma->invalidate();
// TODO: call Material::compile(), we can't si that now because it works only
// with surface materials
}
}
FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> depth,
@@ -2560,6 +2665,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
auto& taaPass = fg.addPass<TAAData>("TAA",
[&](FrameGraph::Builder& builder, auto& data) {
auto desc = fg.getDescriptor(input);
if (taaOptions.upscaling) {
desc.width *= 2;
desc.height *= 2;
}
data.color = builder.sample(input);
data.depth = builder.sample(depth);
data.history = builder.sample(colorHistory);
@@ -2594,18 +2703,25 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
{ -1.0f, 1.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f },
};
float sum = 0.0;
float weights[9];
constexpr float2 subSampleOffsets[4] = {
{ -0.25f, 0.25f }, { 0.25f, 0.25f }, { 0.25f, -0.25f }, { -0.25f, -0.25f }
};
float4 sum = 0.0;
float4 weights[9];
// this doesn't get vectorized (probably because of exp()), so don't bother
// unrolling it.
#pragma nounroll
for (size_t i = 0; i < 9; i++) {
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 Brian Karis
weights[i] = std::exp2(-3.3f * (d.x * d.x + d.y * d.y));
float2 const o = sampleOffsets[i];
for (size_t j = 0; j < 4; j++) {
float2 const s = taaOptions.upscaling ? subSampleOffsets[j] : float2{ 0 };
float2 const d = (o - current.jitter - s) / taaOptions.filterWidth;
// This is a gaussian fit of a 3.3-wide Blackman-Harris window
// see: "High Quality Temporal Supersampling" by Brian Karis
weights[i][j] = std::exp(-2.29f * (d.x * d.x + d.y * d.y));
}
sum += weights[i];
}
for (auto& w : weights) {
@@ -2616,8 +2732,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
auto history = resources.getTexture(data.history);
auto const& material = getPostProcessMaterial("taa");
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {}); // nearest
mi->setParameter("depth", depth, {}); // nearest
@@ -2627,6 +2743,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
.filterMin = SamplerMinFilter::LINEAR
});
mi->setParameter("filterWeights", weights, 9);
mi->setParameter("jitter", current.jitter);
mi->setParameter("reprojection",
mat4f{ historyProjection * inverse(current.projection) } *
normalizedToClip);
@@ -2947,6 +3064,14 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
return input;
}
// we currently don't support stencil resolve
assert_invariant(!isStencilFormat(inDesc.format));
// The Metal / Vulkan backends currently don't support depth/stencil resolve.
if (isDepthFormat(inDesc.format) && (!mEngine.getDriverApi().isDepthStencilResolveSupported())) {
return resolveDepth(fg, outputBufferName, input, outDesc);
}
outDesc.width = inDesc.width;
outDesc.height = inDesc.height;
outDesc.format = inDesc.format;
@@ -2968,21 +3093,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
auto const& dst = resources.getTexture(data.output);
auto const& srcSubDesc = resources.getSubResourceDescriptor(data.input);
auto const& dstSubDesc = resources.getSubResourceDescriptor(data.output);
auto const& srcDesc = resources.getDescriptor(data.input);
auto const& dstDesc = resources.getDescriptor(data.output);
UTILS_UNUSED_IN_RELEASE auto const& srcDesc = resources.getDescriptor(data.input);
UTILS_UNUSED_IN_RELEASE auto const& dstDesc = resources.getDescriptor(data.output);
assert_invariant(src);
assert_invariant(dst);
ASSERT_POSTCONDITION(
srcDesc.format == dstDesc.format,
"resolve: src (%d) and dst (%d) formats don't match.",
srcDesc.format, dstDesc.format);
ASSERT_POSTCONDITION(
srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height,
"resolve: src [%u, %u] and dst [%u, %u] sizes don't match.",
srcDesc.width, srcDesc.height, dstDesc.width, dstDesc.height);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
@@ -2991,6 +3107,51 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
return ppResolve->output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::resolveDepth(FrameGraph& fg,
const char* outputBufferName, FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor outDesc) noexcept {
// Don't do anything if we're not a MSAA buffer
auto const& inDesc = fg.getDescriptor(input);
if (inDesc.samples <= 1) {
return input;
}
UTILS_UNUSED_IN_RELEASE auto const& inSubDesc = fg.getSubResourceDescriptor(input);
assert_invariant(isDepthFormat(inDesc.format));
assert_invariant(inSubDesc.layer == 0);
assert_invariant(inSubDesc.level == 0);
outDesc.width = inDesc.width;
outDesc.height = inDesc.height;
outDesc.format = inDesc.format;
outDesc.samples = 0;
struct ResolveData {
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
};
auto& ppResolve = fg.addPass<ResolveData>("resolveDepth",
[&](FrameGraph::Builder& builder, auto& data) {
data.input = builder.sample(input);
data.output = builder.createTexture(outputBufferName, outDesc);
data.output = builder.write(data.output, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
builder.declareRenderPass(builder.getName(data.output), {
.attachments = { .depth = { data.output }},
.clearFlags = TargetBufferFlags::DEPTH });
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto const& input = resources.getTexture(data.input);
auto const& material = getPostProcessMaterial("resolveDepth");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("depth", input, {}); // NEAREST
commitAndRender(resources.getRenderPassInfo(), material, driver);
});
return ppResolve->output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, uint8_t layer, size_t level,
math::float4 clearColor, bool finalize) noexcept {
@@ -3030,7 +3191,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
auto& material = getPostProcessMaterial("vsmMipmap");
// When generating shadow map mip levels, we want to preserve the 1 texel border.
// (note clearing never respects the scissor in filament)
// (note clearing never respects the scissor in Filament)
PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.scissor = { 1u, 1u, dim - 2u, dim - 2u };
+63 -50
View File
@@ -86,6 +86,10 @@ public:
void init() noexcept;
void terminate(backend::DriverApi& driver) noexcept;
void configureTemporalAntiAliasingMaterial(
TemporalAntiAliasingOptions const& taaOptions) noexcept;
// methods below are ordered relative to their position in the pipeline (as much as possible)
// structure (depth) pass
@@ -158,7 +162,7 @@ public:
FrameGraphId<FrameGraphTexture> depth,
const CameraInfo& cameraInfo,
bool translucent,
float bokehAspectRatio,
math::float2 bokehScale,
const DepthOfFieldOptions& dofOptions) noexcept;
// Bloom
@@ -167,7 +171,9 @@ public:
FrameGraphId<FrameGraphTexture> flare;
};
BloomPassOutput bloom(FrameGraph& fg, FrameGraphId<FrameGraphTexture> input,
BloomOptions& inoutBloomOptions, backend::TextureFormat outFormat,
backend::TextureFormat outFormat,
BloomOptions& inoutBloomOptions,
TemporalAntiAliasingOptions const& taaOptions,
math::float2 scale) noexcept;
FrameGraphId<FrameGraphTexture> flarePass(FrameGraph& fg,
@@ -207,7 +213,9 @@ public:
backend::TextureFormat outFormat, bool translucent) noexcept;
// Temporal Anti-aliasing
void prepareTaa(FrameGraph& fg, filament::Viewport const& svp,
void prepareTaa(FrameGraph& fg,
filament::Viewport const& svp,
TemporalAntiAliasingOptions const& taaOptions,
FrameHistory& frameHistory,
FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa,
CameraInfo* inoutCameraInfo,
@@ -246,6 +254,12 @@ public:
const char* outputBufferName, FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor outDesc) noexcept;
// Resolves base level of input and outputs a texture from outDesc.
// outDesc with, height, format and samples will be overridden.
FrameGraphId<FrameGraphTexture> resolveDepth(FrameGraph& fg,
const char* outputBufferName, FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor outDesc) noexcept;
// VSM shadow mipmap pass
FrameGraphId<FrameGraphTexture> vsmMipmapPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, uint8_t layer, size_t level,
@@ -265,48 +279,6 @@ public:
backend::Handle<backend::HwTexture> getOneTextureArray() const;
backend::Handle<backend::HwTexture> getZeroTextureArray() const;
math::float2 halton(size_t index) const noexcept {
return mHaltonSamples[index & 0xFu];
}
private:
FEngine& mEngine;
class PostProcessMaterial;
struct BilateralPassConfig {
uint8_t kernelSize = 11;
bool bentNormals = false;
float standardDeviation = 1.0f;
float bilateralThreshold = 0.0625f;
float scale = 1.0f;
};
FrameGraphId<FrameGraphTexture> bilateralBlurPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, FrameGraphId<FrameGraphTexture> depth,
math::int2 axis, float zf, backend::TextureFormat format,
BilateralPassConfig const& config) noexcept;
BloomPassOutput bloomPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
BloomOptions& inoutBloomOptions, math::float2 scale) noexcept;
FrameGraphId<FrameGraphTexture> downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
bool threshold, float highlight, bool fireflies) noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material, uint8_t variant,
backend::DriverApi& driver) const noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material,
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline,
backend::DriverApi& driver) const noexcept;
class PostProcessMaterial {
public:
PostProcessMaterial() noexcept;
@@ -340,21 +312,62 @@ private:
utils::FixedCapacityVector<ConstantInfo> mConstants{};
};
void registerPostProcessMaterial(std::string_view name, MaterialInfo const& info);
PostProcessMaterial& getPostProcessMaterial(std::string_view name) noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material, uint8_t variant,
backend::DriverApi& driver) const noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material,
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline,
backend::DriverApi& driver) const noexcept;
private:
FEngine& mEngine;
struct BilateralPassConfig {
uint8_t kernelSize = 11;
bool bentNormals = false;
float standardDeviation = 1.0f;
float bilateralThreshold = 0.0625f;
float scale = 1.0f;
};
FrameGraphId<FrameGraphTexture> bilateralBlurPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, FrameGraphId<FrameGraphTexture> depth,
math::int2 axis, float zf, backend::TextureFormat format,
BilateralPassConfig const& config) noexcept;
FrameGraphId<FrameGraphTexture> downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
bool threshold, float highlight, bool fireflies) noexcept;
using MaterialRegistryMap = tsl::robin_map<
std::string_view,
PostProcessMaterial>;
MaterialRegistryMap mMaterialRegistry;
void registerPostProcessMaterial(std::string_view name, MaterialInfo const& info);
PostProcessMaterial& getPostProcessMaterial(std::string_view name) noexcept;
backend::Handle<backend::HwTexture> mStarburstTexture;
std::uniform_real_distribution<float> mUniformDistribution{0.0f, 1.0f};
static const math::float2 sHaltonSamples[16];
math::float2 const* mHaltonSamples = sHaltonSamples;
template<size_t SIZE>
struct JitterSequence {
auto operator()(size_t i) const noexcept { return positions[i % SIZE] - 0.5f; }
const math::float2 positions[SIZE];
};
static const JitterSequence<4> sRGSS4;
static const JitterSequence<4> sUniformHelix4;
static const JitterSequence<16> sHaltonSamples;
bool mWorkaroundSplitEasu : 1;
bool mWorkaroundAllowReadOnlyAncillaryFeedbackLoop : 1;
+3 -3
View File
@@ -247,7 +247,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// cameraInfo only valid after calling update
const CameraInfo cameraInfo{ shadowMap.getCamera() };
const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo };
auto transaction = ShadowMap::open(driver);
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
@@ -844,7 +844,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
FScene::LightSoa const& lightData) noexcept {
// The const_cast here is a little ugly, but conceptually lightData should be const,
// it's just that we're using it to store some temporary data. with SoA we can't have
// it's just that we're using it to store some temporary data. With SoA we can't have
// a `mutable` element, so that's a workaround.
FScene::ShadowInfo* const shadowInfo = const_cast<FScene::ShadowInfo*>(
lightData.data<FScene::SHADOW_INFO>());
@@ -855,7 +855,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
for (ShadowMap const& shadowMap : spotShadowMaps) {
const size_t lightIndex = shadowMap.getLightIndex();
// gather the per-light (not per shadow map) information. For point lights we will
// Gather the per-light (not per shadow map) information. For point lights we will
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
// knows how to find the entry for other faces (they're guaranteed to be sequential).
if (shadowMap.getFace() == 0) {
+14 -16
View File
@@ -265,24 +265,22 @@ uint8_t FCamera::getStereoscopicEyeCount() const noexcept {
// ------------------------------------------------------------------------------------------------
CameraInfo::CameraInfo(FCamera const& camera) noexcept {
for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) {
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
}
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
model = mat4f{ camera.getModelMatrix() };
view = mat4f{ camera.getViewMatrix() };
zn = (float)camera.getNear();
zf = (float)camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = (float)camera.getFocalLength();
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance());
CameraInfo::CameraInfo(FCamera const& camera) noexcept
: CameraInfo(camera, {}, camera.getModelMatrix()) {
}
CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept {
const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() };
for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) {
CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept
: CameraInfo(camera, inWorldTransform, inWorldTransform * camera.getModelMatrix()) {
}
CameraInfo::CameraInfo(FCamera const& camera, CameraInfo const& mainCameraInfo) noexcept
: CameraInfo(camera, mainCameraInfo.worldTransform, camera.getModelMatrix()) {
}
CameraInfo::CameraInfo(FCamera const& camera,
math::mat4 const& inWorldTransform,
math::mat4 const& modelMatrix) noexcept {
for (size_t i = 0; i < camera.getStereoscopicEyeCount(); i++) {
eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) };
eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) };
}
+16 -1
View File
@@ -220,9 +220,19 @@ private:
struct CameraInfo {
CameraInfo() noexcept {}
explicit CameraInfo(FCamera const& camera) noexcept;
// Creates a CameraInfo relative to inWorldTransform (i.e. it's model matrix is
// transformed by inWorldTransform and inWorldTransform is recorded).
// This is typically used for the color pass camera.
CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept;
// Creates a CameraInfo from a camera that is relative to mainCameraInfo.
// This is typically used for the shadow pass cameras.
CameraInfo(FCamera const& camera, CameraInfo const& mainCameraInfo) noexcept;
// Creates a CameraInfo from the FCamera
explicit CameraInfo(FCamera const& camera) noexcept;
union {
// projection matrix for drawing (infinite zfar)
// for monoscopic rendering
@@ -249,6 +259,11 @@ struct CameraInfo {
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
math::mat4 getUserViewMatrix() const noexcept { return view * worldTransform; }
private:
CameraInfo(FCamera const& camera,
math::mat4 const& inWorldTransform,
math::mat4 const& modelMatrix) noexcept;
};
FILAMENT_DOWNCAST(Camera)
+12 -2
View File
@@ -373,8 +373,13 @@ void FEngine::init() {
mDebugRegistry.registerProperty("d.shadowmap.debug_directional_shadowmap",
&debug.shadowmap.debug_directional_shadowmap, [this]() {
mMaterials.forEach([](FMaterial* material) {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
material->setConstant(
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
debug.shadowmap.debug_directional_shadowmap);
material->invalidate(
Variant::DIR | Variant::SRE | Variant::DEP,
Variant::DIR | Variant::SRE);
@@ -384,8 +389,13 @@ void FEngine::init() {
mDebugRegistry.registerProperty("d.lighting.debug_froxel_visualization",
&debug.lighting.debug_froxel_visualization, [this]() {
mMaterials.forEach([](FMaterial* material) {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
material->setConstant(
+ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION,
debug.lighting.debug_froxel_visualization);
material->invalidate(
Variant::DYN | Variant::DEP,
Variant::DYN);
+74 -27
View File
@@ -79,7 +79,10 @@ struct Material::BuilderDetails {
size_t mSize = 0;
MaterialParser* mMaterialParser = nullptr;
bool mDefaultMaterial = false;
std::unordered_map<std::string, std::variant<int32_t, float, bool>> mConstantSpecializations;
std::unordered_map<
utils::CString,
std::variant<int32_t, float, bool>,
CString::Hasher> mConstantSpecializations;
};
FMaterial::DefaultMaterialBuilder::DefaultMaterialBuilder() : Material::Builder() {
@@ -216,9 +219,14 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSubpassInfo.isValid = false;
}
utils::FixedCapacityVector<MaterialConstant> constants;
// Older materials won't have a constants chunk, but that's okay.
parser->getConstants(&constants);
parser->getConstants(&mMaterialConstants);
for (size_t i = 0, c = mMaterialConstants.size(); i < c; i++) {
auto& item = mMaterialConstants[i];
// the key can be a string_view because mMaterialConstant owns the CString
std::string_view const key{ item.name.data(), item.name.size() };
mSpecializationConstantsNameToIndex[key] = i;
}
// Verify that all the constant specializations exist in the material and that their types match.
// The first specialization constants are defined internally by Filament.
@@ -238,7 +246,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
bool const powerVrShaderWorkarounds =
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
mSpecializationConstants.reserve(constants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
mSpecializationConstants.reserve(mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL,
(int)engine.getSupportedFeatureLevel() });
@@ -250,16 +258,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
(int)maxFroxelBufferHeight });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
(bool)engine.debug.shadowmap.debug_directional_shadowmap });
engine.debug.shadowmap.debug_directional_shadowmap });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION,
(bool)engine.debug.lighting.debug_froxel_visualization });
engine.debug.lighting.debug_froxel_visualization });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND,
(bool)staticTextureWorkaround });
staticTextureWorkaround });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS,
(bool)powerVrShaderWorkarounds });
powerVrShaderWorkarounds });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT,
(int)engine.getConfig().stereoscopicEyeCount });
@@ -270,19 +278,18 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
false});
}
for (const auto& [name, value] : builder->mConstantSpecializations) {
auto found = std::find_if(
constants.begin(), constants.end(), [name = name](const auto& constant) {
return strncmp(constant.name.data(), name.data(), name.length()) == 0;
});
ASSERT_PRECONDITION(found != constants.end(),
for (auto const& [name, value] : builder->mConstantSpecializations) {
std::string_view const key{ name.data(), name.size() };
auto pos = mSpecializationConstantsNameToIndex.find(key);
ASSERT_PRECONDITION(pos != mSpecializationConstantsNameToIndex.end(),
"The material %s does not have a constant parameter named %s.",
mName.c_str_safe(), name.c_str());
const char* const types[3] = {"an int", "a float", "a bool"};
const char* const errorMessage =
"The constant parameter %s on material %s is of type %s, but %s was "
"provided.";
switch (found->type) {
auto& constant = mMaterialConstants[pos->second];
switch (constant.type) {
case ConstantType::INT:
ASSERT_PRECONDITION(std::holds_alternative<int32_t>(value), errorMessage,
name.c_str(), mName.c_str_safe(), "int", types[value.index()]);
@@ -296,8 +303,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
name.c_str(), mName.c_str_safe(), "bool", types[value.index()]);
break;
}
uint32_t const index =
std::distance(constants.begin(), found) + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
uint32_t const index = pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
mSpecializationConstants.push_back({ index, value });
}
@@ -462,16 +468,6 @@ FMaterial::~FMaterial() noexcept {
}
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
// update the spec constants that can change
// TODO: should we just always update all of them?
for (auto& item : mSpecializationConstants) {
if (item.id == ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP) {
item.value = mEngine.debug.shadowmap.debug_directional_shadowmap;
} else if (item.id == ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION) {
item.value = mEngine.debug.lighting.debug_froxel_visualization;
}
}
if (mMaterialDomain == MaterialDomain::SURFACE) {
DriverApi& driverApi = mEngine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
@@ -841,4 +837,55 @@ void FMaterial::destroyPrograms(FEngine& engine) {
}
}
std::optional<uint32_t> FMaterial::getSpecializationConstantId(std::string_view name) const noexcept {
auto pos = mSpecializationConstantsNameToIndex.find(name);
if (pos != mSpecializationConstantsNameToIndex.end()) {
return pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
}
return std::nullopt;
}
template<typename T, typename>
bool FMaterial::setConstant(uint32_t id, T value) noexcept {
size_t const maxId = mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
if (UTILS_LIKELY(id < maxId)) {
if (id >= CONFIG_MAX_RESERVED_SPEC_CONSTANTS) {
// Constant from the material itself (as opposed to the reserved ones)
auto& constant = mMaterialConstants[id - CONFIG_MAX_RESERVED_SPEC_CONSTANTS];
using ConstantType = backend::ConstantType;
switch (constant.type) {
case ConstantType::INT:
if (!std::is_same_v<T, int32_t>) return false;
break;
case ConstantType::FLOAT:
if (!std::is_same_v<T, float>) return false;
break;
case ConstantType::BOOL:
if (!std::is_same_v<T, bool>) return false;
break;
}
}
auto pos = std::find_if(
mSpecializationConstants.begin(), mSpecializationConstants.end(),
[id](backend::Program::SpecializationConstant const& specializationConstant) {
return specializationConstant.id == id;
});
if (pos != mSpecializationConstants.end()) {
if (std::get<T>(pos->value) != value) {
pos->value = value;
return true;
}
} else {
mSpecializationConstants.push_back({ id, value });
return true;
}
}
return false;
}
template bool FMaterial::setConstant<int32_t>(uint32_t id, int32_t value) noexcept;
template bool FMaterial::setConstant<float>(uint32_t id, float value) noexcept;
template bool FMaterial::setConstant<bool>(uint32_t id, bool value) noexcept;
} // namespace filament
+13
View File
@@ -165,6 +165,14 @@ public:
void destroyPrograms(FEngine& engine);
// return the id of a specialization constant specified by name for this material
std::optional<uint32_t> getSpecializationConstantId(std::string_view name) const noexcept ;
// Sets a specialization constant by id. call is no-op if the id is invalid.
// Return true is the value was changed.
template<typename T, typename = Builder::is_supported_constant_parameter_t<T>>
bool setConstant(uint32_t id, T value) noexcept;
#if FILAMENT_ENABLE_MATDBG
void applyPendingEdits() noexcept;
@@ -261,6 +269,11 @@ private:
SamplerGroupBindingInfoList mSamplerGroupBindingInfoList;
SamplerBindingToNameMap mSamplerBindingToNameMap;
// Constants defined by this Material
utils::FixedCapacityVector<MaterialConstant> mMaterialConstants;
// A map from the Constant name to the mMaterialConstant index
std::unordered_map<std::string_view, uint32_t> mSpecializationConstantsNameToIndex;
// current specialization constants for the HwProgram
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
#if FILAMENT_ENABLE_MATDBG
+1 -1
View File
@@ -117,7 +117,7 @@ void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* ma
if (!material->getSamplerInterfaceBlock().isEmpty()) {
mSamplers = SamplerGroup(material->getSamplerInterfaceBlock().getSize());
mSbHandle = driver.createSamplerGroup(
mSamplers.getSize(), utils::FixedSizeString<32>("Default material"));
mSamplers.getSize(), utils::FixedSizeString<32>(mMaterial->getName().c_str_safe()));
}
const RasterState& rasterState = material->getRasterState();
+35 -8
View File
@@ -491,6 +491,19 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
hasDithering = false;
hasFXAA = false;
scale = 1.0f;
} else {
// This configures post-process materials by setting constant parameters
if (taaOptions.enabled) {
ppm.configureTemporalAntiAliasingMaterial(taaOptions);
if (taaOptions.upscaling) {
// for now TAA upscaling is incompatible with regular dsr
dsrOptions.enabled = false;
// also, upscaling doesn't work well with quater-resolution SSAO
aoOptions.resolution = 1.0;
// Currently we only support a fixed TAA upscaling ratio
scale = 0.5f;
}
}
}
const bool blendModeTranslucent = view.getBlendMode() == BlendMode::TRANSLUCENT;
@@ -520,7 +533,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
};
// whether we're scaled at all
const bool scaled = any(notEqual(scale, float2(1.0f)));
bool scaled = any(notEqual(scale, float2(1.0f)));
// vp is the user defined viewport within the View
filament::Viewport const& vp = view.getViewport();
@@ -609,7 +622,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
view.prepare(engine, driver, arena, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
view.prepareUpscaler(scale);
view.prepareUpscaler(scale, taaOptions, dsrOptions);
/*
* Allocate command buffer
@@ -813,14 +826,14 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
[=, &view](FrameGraphResources const& resources,
auto const&, DriverApi& driver) mutable {
auto out = resources.getRenderPassInfo();
view.executePickingQueries(driver, out.target, aoOptions.resolution);
view.executePickingQueries(driver, out.target, scale * aoOptions.resolution);
});
}
// Store this frame's camera projection in the frame history.
if (UTILS_UNLIKELY(taaOptions.enabled)) {
// Apply the TAA jitter to everything after the structure pass, starting with the color pass.
ppm.prepareTaa(fg, svp, view.getFrameHistory(), &FrameHistoryEntry::taa,
ppm.prepareTaa(fg, svp, taaOptions, view.getFrameHistory(), &FrameHistoryEntry::taa,
&cameraInfo, view.getPerViewUniforms());
}
@@ -1006,6 +1019,15 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (taaOptions.enabled) {
input = ppm.taa(fg, input, depth, view.getFrameHistory(), &FrameHistoryEntry::taa,
taaOptions, colorGradingConfig);
if (taaOptions.upscaling) {
scale = 1.0f;
scaled = false;
UTILS_UNUSED_IN_RELEASE auto const& inputDesc = fg.getDescriptor(input);
svp.width = inputDesc.width;
svp.height = inputDesc.height;
xvp.width *= 2;
xvp.height *= 2;
}
}
// --------------------------------------------------------------------------------------------
@@ -1021,16 +1043,21 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// The bokeh height is always correct regardless of the dynamic resolution scaling.
// (because the CoC is calculated w.r.t. the height), so we only need to adjust
// the width.
float const bokehAspectRatio = scale.x / scale.y;
float const aspect = (scale.x / scale.y) * dofOptions.cocAspectRatio;
float2 const bokehScale{
aspect < 1.0f ? aspect : 1.0f,
aspect > 1.0f ? 1.0f / aspect : 1.0f
};
input = ppm.dof(fg, input, depth, cameraInfo, needsAlphaChannel,
bokehAspectRatio, dofOptions);
bokehScale, dofOptions);
}
FrameGraphId<FrameGraphTexture> bloom, flare;
if (bloomOptions.enabled) {
// generate the bloom buffer, which is stored in the blackboard as "bloom". This is
// Generate the bloom buffer, which is stored in the blackboard as "bloom". This is
// consumed by the colorGrading pass and will be culled if colorGrading is disabled.
auto [bloom_, flare_] = ppm.bloom(fg, input, bloomOptions, TextureFormat::R11F_G11F_B10F, scale);
auto [bloom_, flare_] = ppm.bloom(fg, input, TextureFormat::R11F_G11F_B10F,
bloomOptions, taaOptions, scale);
bloom = bloom_;
flare = flare_;
}
+22 -7
View File
@@ -313,6 +313,10 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
}
if (UTILS_LIKELY(!lcm.isShadowCaster(li))) {
// Because we early exit here, we need to make sure we mark the light as non-casting.
// See `ShadowMapManager::updateSpotShadowMaps` for const_cast<> justification.
const_cast<FScene::ShadowInfo&>(
lightData.elementAt<FScene::SHADOW_INFO>(l)).castsShadows = false;
continue; // doesn't cast shadows
}
@@ -721,11 +725,22 @@ UTILS_NOINLINE
});
}
void FView::prepareUpscaler(float2 scale) const noexcept {
void FView::prepareUpscaler(float2 scale,
TemporalAntiAliasingOptions const& taaOptions,
DynamicResolutionOptions const& dsrOptions) const noexcept {
SYSTRACE_CALL();
const float bias = (mDynamicResolution.quality >= QualityLevel::HIGH) ?
std::log2(std::min(scale.x, scale.y)) : 0.0f;
mPerViewUniforms.prepareLodBias(bias);
float bias = 0.0f;
float2 derivativesScale{ 1.0f };
if (dsrOptions.enabled && dsrOptions.quality >= QualityLevel::HIGH) {
bias = std::log2(std::min(scale.x, scale.y));
}
if (taaOptions.enabled) {
bias += taaOptions.lodBias;
if (taaOptions.upscaling) {
derivativesScale = 0.5f;
}
}
mPerViewUniforms.prepareLodBias(bias, derivativesScale);
}
void FView::prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept {
@@ -981,15 +996,15 @@ void FView::drainFrameHistory(FEngine& engine) noexcept {
}
void FView::executePickingQueries(backend::DriverApi& driver,
backend::RenderTargetHandle handle, float scale) noexcept {
backend::RenderTargetHandle handle, float2 scale) noexcept {
while (mActivePickingQueriesList) {
FPickingQuery* const pQuery = mActivePickingQueriesList;
mActivePickingQueriesList = pQuery->next;
// adjust for dynamic resolution and structure buffer scale
const uint32_t x = uint32_t(float(pQuery->x) * (scale * mScale.x));
const uint32_t y = uint32_t(float(pQuery->y) * (scale * mScale.y));
const uint32_t x = uint32_t(float(pQuery->x) * scale.x);
const uint32_t y = uint32_t(float(pQuery->y) * scale.y);
if (UTILS_UNLIKELY(driver.getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)) {
driver.readPixels(handle, x, y, 1, 1, {
+4 -2
View File
@@ -133,7 +133,9 @@ public:
return mName.c_str_safe();
}
void prepareUpscaler(math::float2 scale) const noexcept;
void prepareUpscaler(math::float2 scale,
TemporalAntiAliasingOptions const& taaOptions,
DynamicResolutionOptions const& dsrOptions) const noexcept;
void prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept;
void prepareViewport(
@@ -417,7 +419,7 @@ public:
View::PickingQueryResultCallback callback) noexcept;
void executePickingQueries(backend::DriverApi& driver,
backend::RenderTargetHandle handle, float scale) noexcept;
backend::RenderTargetHandle handle, math::float2 scale) noexcept;
void setMaterialGlobal(uint32_t index, math::float4 const& value);
+5
View File
@@ -229,6 +229,11 @@ void RenderPassNode::resolve() noexcept {
rt.descriptor.samples = pImportedRenderTarget->importedDesc.samples;
rt.backend.target = pImportedRenderTarget->target;
// We could end-up here more than once, for instance if the rendertarget is used
// by multiple passes (this would imply a read-back, btw). In this case, we don't want
// to clear it the 2nd time, so we clear the imported pass's clear flags.
pImportedRenderTarget->importedDesc.clearFlags = TargetBufferFlags::NONE;
// but don't discard attachments the imported target tells us to keep
rt.backend.params.flags.discardStart &= ~pImportedRenderTarget->importedDesc.keepOverrideStart;
rt.backend.params.flags.discardEnd &= ~pImportedRenderTarget->importedDesc.keepOverrideEnd;
+170 -132
View File
@@ -24,10 +24,57 @@ material {
precision: high
},
{
type : float[9],
type : float2,
name : jitter,
precision: high
},
{
type : float4[9],
name : filterWeights
}
],
constants : [
{
type : bool,
name : historyReprojection,
default : true
},
{
type : bool,
name : filterHistory,
default : true
},
{
type : bool,
name : filterInput,
default : true
},
{
type : int,
name : boxClipping,
default : 0
},
{
type : int,
name : boxType,
default : 1
},
{
type : bool,
name : useYCoCg,
default : false
},
{
type : bool,
name : preventFlickering,
default : false
},
{
type : float,
name : varianceGamma,
default : 1.0
}
],
variables : [
vertex
],
@@ -44,14 +91,6 @@ vertex {
fragment {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
#define TEXTURE_SPACE_UP -1
#define TEXTURE_SPACE_DN 1
#else
#define TEXTURE_SPACE_UP 1
#define TEXTURE_SPACE_DN -1
#endif
/* Clipping box type */
// min/max neighborhood
@@ -61,9 +100,6 @@ fragment {
// uses both min/max and variance
#define BOX_TYPE_AABB_VARIANCE 2
// High VARIANCE_GAMMA [0.75, 1.25] increases ghosting artefact, lower values increases jittering
#define VARIANCE_GAMMA 1.0
/* Clipping algorithm */
// accurate box clipping
@@ -73,34 +109,12 @@ fragment {
// no clipping (for debugging only)
#define BOX_CLIPPING_NONE 2
/* Configuration mobile/desktop */
#if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH
#define BOX_CLIPPING BOX_CLIPPING_ACCURATE
#define BOX_TYPE BOX_TYPE_VARIANCE
#define USE_YCoCg 0
#define FILTER_INPUT 1
#define FILTER_HISTORY 0
#else
#define BOX_CLIPPING BOX_CLIPPING_ACCURATE
#define BOX_TYPE BOX_TYPE_AABB_VARIANCE
#define USE_YCoCg 1
#define FILTER_INPUT 1
#define FILTER_HISTORY 1
#endif
/* debugging helper */
#define HISTORY_REPROJECTION 1
#define PREVENT_FLICKERING 0 // FIXME: thin lines disapear
float luma(const vec3 color) {
#if USE_YCoCg
return color.x;
#else
return luminance(color);
#endif
if (materialConstants_useYCoCg) {
return color.x;
} else {
return luminance(color);
}
}
vec3 RGB_YCoCg(const vec3 c) {
@@ -194,108 +208,136 @@ vec4 sampleTextureCatmullRom(const sampler2D tex, const highp vec2 uv, const hig
result *= 1.0 / (k0 + k1 + k2 + k3 + k4);
// we could end-up with negative values
result = max(vec4(0), result);
return result;
}
void postProcess(inout PostProcessInputs postProcess) {
highp vec4 uv = variable_vertex.xyxy; // interpolated to pixel center
// read the depth buffer center sample for reprojection
float depth = textureLod(materialParams_depth, uv.xy, 0.0).r;
#if HISTORY_REPROJECTION
uv.zw = uvToRenderTargetUV(uv.zw);
// reproject history to current frame
highp vec4 q = materialParams.reprojection * vec4(uv.zw, depth, 1.0);
uv.zw = (q.xy * (1.0 / q.w)) * 0.5 + 0.5;
uv.zw = uvToRenderTargetUV(uv.zw);
#endif
if (materialConstants_historyReprojection) {
// read the depth buffer center sample for reprojection
highp float depth = textureLod(materialParams_depth, uv.xy, 0.0).r;
// reproject history to current frame
uv.zw = uvToRenderTargetUV(uv.zw);
highp vec4 q = materialParams.reprojection * vec4(uv.zw, depth, 1.0);
uv.zw = (q.xy * (1.0 / q.w)) * 0.5 + 0.5;
uv.zw = uvToRenderTargetUV(uv.zw);
}
// read center color and history samples
vec4 color = textureLod(materialParams_color, uv.xy, 0.0);
#if FILTER_HISTORY
vec4 history = sampleTextureCatmullRom(materialParams_history, uv.zw,
vec2(textureSize(materialParams_history, 0)));
#else
vec4 history = textureLod(materialParams_history, uv.zw, 0.0);
#endif
vec4 history;
if (materialConstants_filterHistory) {
history = sampleTextureCatmullRom(materialParams_history, uv.zw,
vec2(textureSize(materialParams_history, 0)));
} else {
history = textureLod(materialParams_history, uv.zw, 0.0);
}
#if USE_YCoCg
history.rgb = RGB_YCoCg(history.rgb);
#endif
if (materialConstants_useYCoCg) {
history.rgb = RGB_YCoCg(history.rgb);
}
highp vec2 size = vec2(textureSize(materialParams_color, 0));
highp vec2 p = (floor(uv.xy * size) + 0.5) / size;
vec4 color = textureLod(materialParams_color, p, 0.0);
vec3 s[9];
if (materialConstants_filterInput || materialConstants_boxClipping != BOX_CLIPPING_NONE) {
s[0] = textureLodOffset(materialParams_color, p, 0.0, ivec2(-1, -1)).rgb;
s[1] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 0, -1)).rgb;
s[2] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 1, -1)).rgb;
s[3] = textureLodOffset(materialParams_color, p, 0.0, ivec2(-1, 0)).rgb;
s[4] = color.rgb;
s[5] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 1, 0)).rgb;
s[6] = textureLodOffset(materialParams_color, p, 0.0, ivec2(-1, 1)).rgb;
s[7] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 0, 1)).rgb;
s[8] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 1, 1)).rgb;
}
if (materialConstants_useYCoCg) {
color.rgb = RGB_YCoCg(color.rgb);
if (materialConstants_filterInput || materialConstants_boxClipping != BOX_CLIPPING_NONE) {
for (int i = 0; i < 9; i++) {
s[i] = RGB_YCoCg(s[i]);
}
}
}
vec2 subPixelOffset = p - uv.xy; // +/- [0.25, 0.25]
float confidence = 0.0;
vec4 filtered = color;
if (materialConstants_filterInput) {
// unjitter/filter input
// figure out which set of coeficients to use
int jxp = subPixelOffset.y > 0.0 ? 3 : 0;
int jxn = subPixelOffset.y > 0.0 ? 2 : 1;
int j = subPixelOffset.x > 0.0 ? jxp : jxn;
filtered = vec4(0, 0, 0, color.a);
for (int i = 0; i < 9; i++) {
float w = materialParams.filterWeights[i][j];
filtered.rgb += s[i] * w;
confidence = max(confidence, w);
}
} else {
confidence = float(materialParams.jitter.x * subPixelOffset.x > 0.0 &&
materialParams.jitter.y * subPixelOffset.y > 0.0);
}
// build the history clamping box
vec3 s[9];
s[0] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2(-1, TEXTURE_SPACE_DN)).rgb;
s[1] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 0, TEXTURE_SPACE_DN)).rgb;
s[2] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 1, TEXTURE_SPACE_DN)).rgb;
s[3] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2(-1, 0)).rgb;
s[4] = color.rgb;
s[5] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 1, 0)).rgb;
s[6] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2(-1, TEXTURE_SPACE_UP)).rgb;
s[7] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 0, TEXTURE_SPACE_UP)).rgb;
s[8] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 1, TEXTURE_SPACE_UP)).rgb;
if (materialConstants_boxClipping != BOX_CLIPPING_NONE) {
vec3 boxmin;
vec3 boxmax;
if (materialConstants_boxType == BOX_TYPE_AABB ||
materialConstants_boxType == BOX_TYPE_AABB_VARIANCE) {
boxmin = min(s[4], min(min(s[1], s[3]), min(s[5], s[7])));
boxmax = max(s[4], max(max(s[1], s[3]), max(s[5], s[7])));
vec3 box9min = min(boxmin, min(min(s[0], s[2]), min(s[6], s[8])));
vec3 box9max = max(boxmax, max(max(s[0], s[2]), max(s[6], s[8])));
// round the corners of the 3x3 box
boxmin = (boxmin + box9min) * 0.5;
boxmax = (boxmax + box9max) * 0.5;
}
#if USE_YCoCg
for (int i = 0 ; i < 9 ; i++) {
s[i] = RGB_YCoCg(s[i]);
if (materialConstants_boxType == BOX_TYPE_VARIANCE ||
materialConstants_boxType == BOX_TYPE_AABB_VARIANCE) {
// "An Excursion in Temporal Supersampling" by Marco Salvi
highp vec3 m0 = s[4];// conversion to highp
highp vec3 m1 = m0 * m0;
// we use only 5 samples instead of all 9
for (int i = 1; i < 9; i+=2) {
highp vec3 c = s[i];// conversion to highp
m0 += c;
m1 += c * c;
}
highp vec3 a0 = m0 * (1.0 / 5.0);
highp vec3 a1 = m1 * (1.0 / 5.0);
highp vec3 sigma = sqrt(a1 - a0 * a0);
if (materialConstants_boxType == BOX_TYPE_VARIANCE) {
boxmin = a0 - materialConstants_varianceGamma * sigma;
boxmax = a0 + materialConstants_varianceGamma * sigma;
} else {
// intersect both bounding boxes
boxmin = max(boxmin, a0 - materialConstants_varianceGamma * sigma);
boxmax = min(boxmax, a0 + materialConstants_varianceGamma * sigma);
}
}
// history clamping
history = clipToBox(materialConstants_boxClipping, boxmin, boxmax, filtered, history);
}
color.rgb = s[4].rgb;
#endif
#if FILTER_INPUT
// unjitter/filter input
vec4 filtered = vec4(0, 0, 0, color.a);
for (int i = 0 ; i < 9 ; i++) {
filtered.rgb += s[i] * materialParams.filterWeights[i];
}
#else
vec4 filtered = color;
#endif
#if BOX_TYPE == BOX_TYPE_AABB || BOX_TYPE == BOX_TYPE_AABB_VARIANCE
vec3 boxmin = min(s[4], min(min(s[1], s[3]), min(s[5], s[7])));
vec3 boxmax = max(s[4], max(max(s[1], s[3]), max(s[5], s[7])));
vec3 box9min = min(boxmin, min(min(s[0], s[2]), min(s[6], s[8])));
vec3 box9max = max(boxmax, max(max(s[0], s[2]), max(s[6], s[8])));
// round the corners of the 3x3 box
boxmin = (boxmin + box9min) * 0.5;
boxmax = (boxmax + box9max) * 0.5;
#endif
#if BOX_TYPE == BOX_TYPE_VARIANCE || BOX_TYPE == BOX_TYPE_AABB_VARIANCE
// "An Excursion in Temporal Supersampling" by Marco Salvi
vec3 m0 = s[4];
vec3 m1 = s[4] * s[4];
// we use only 5 samples instead of all 9
for (int i = 1 ; i < 9 ; i+=2) {
m0 += s[i];
m1 += s[i] * s[i];
}
vec3 a0 = m0 * (1.0 / 5.0);
vec3 a1 = m1 * (1.0 / 5.0);
vec3 sigma = sqrt(a1 - a0 * a0);
#if BOX_TYPE == BOX_TYPE_VARIANCE
vec3 boxmin = a0 - VARIANCE_GAMMA * sigma;
vec3 boxmax = a0 + VARIANCE_GAMMA * sigma;
#else
boxmin = min(boxmin, a0 - VARIANCE_GAMMA * sigma);
boxmax = max(boxmax, a0 + VARIANCE_GAMMA * sigma);
#endif
#endif
// history clamping
history = clipToBox(BOX_CLIPPING, boxmin, boxmax, filtered, history);
float lumaColor = luma(filtered.rgb);
float lumaHistory = luma(history.rgb);
float alpha = materialParams.alpha;
#if PREVENT_FLICKERING
// [Lottes] prevents flickering by modulating the blend weight by the difference in luma
float diff = 1.0 - abs(lumaColor - lumaHistory) / (0.001 + max(lumaColor, lumaHistory));
alpha *= diff * diff;
#endif
float alpha = materialParams.alpha * confidence;
if (materialConstants_preventFlickering) {
// [Lottes] prevents flickering by modulating the blend weight by the difference in luma
float diff = 1.0 - abs(lumaColor - lumaHistory) / (0.001 + max(lumaColor, lumaHistory));
alpha *= diff * diff;
}
// tonemapping for handling HDR
filtered.rgb *= 1.0 / (1.0 + lumaColor);
@@ -307,20 +349,16 @@ void postProcess(inout PostProcessInputs postProcess) {
// untonemap result
result.rgb *= 1.0 / (1.0 - luma(result.rgb));
#if USE_YCoCg
result.rgb = YCoCg_RGB(result.rgb);
#endif
// store result (which will becomes new history)
// we could end-up with negative values due to the bicubic filter, which never recover on
// their own.
result = max(vec4(0), result);
if (materialConstants_useYCoCg) {
result.rgb = YCoCg_RGB(result.rgb);
}
#if POST_PROCESS_OPAQUE
// kill the work performed above
result.a = 1.0;
#endif
// store result (which will becomes new history)
postProcess.color = result;
}
+33
View File
@@ -0,0 +1,33 @@
material {
name : resolveDepth,
parameters : [
{
type : sampler2d,
name : depth,
precision: high,
multisample : true
}
],
outputs : [
{
name : depth,
target : depth,
type : float
}
],
domain : postprocess,
depthWrite : true,
depthCulling : false,
culling: none
}
fragment {
// resolve depth by keeping the first sample. This is similar to what glBlitFramebuffer() does
// this material is currently only used on Metal and Vulkan.
// Technically it should be feature-level 2 material, but that would restrict when we can use
// it because Vulkan can return feature-level 1 currently (we should fix that).
void postProcess(inout PostProcessInputs postProcess) {
highp ivec2 icoord = ivec2(gl_FragCoord.xy);
postProcess.depth = texelFetch(materialParams_depth, icoord, 0).r;
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.49.1"
spec.version = "1.49.3"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.49.1/filament-v1.49.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.49.3/filament-v1.49.3-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -16,6 +16,8 @@
#import <UIKit/UIKit.h>
#include <utils/Entity.h>
namespace filament {
class Engine;
class Scene;
@@ -76,6 +78,17 @@ typedef NSData* _Nonnull (^ResourceCallback)(NSString* _Nonnull);
- (void)destroyModel;
/**
* Issues a pick query at the given view coordinates.
* The coordinates should be in UIKit's coordinate system, with the origin at
* the top-left.
* The callback is triggered with entity of the picked object.
*/
typedef void (^PickCallback)(utils::Entity);
- (void)issuePickQuery:(CGPoint)point callback:(PickCallback)callback;
- (NSString* _Nullable)getEntityName:(utils::Entity)entity;
/**
* Sets up a root transform on the current model to make it fit into a unit cube.
*/
@@ -127,13 +127,12 @@ const float kSensitivity = 100.0f;
_swapChain = _engine->createSwapChain((__bridge void*)self.layer);
_materialProvider = createUbershaderProvider(_engine,
UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
_materialProvider =
createUbershaderProvider(_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
EntityManager& em = EntityManager::get();
NameComponentManager* ncm = new NameComponentManager(em);
_assetLoader = AssetLoader::create({_engine, _materialProvider, ncm, &em});
_resourceLoader = new ResourceLoader(
{.engine = _engine, .normalizeSkinningWeights = true});
_resourceLoader = new ResourceLoader({.engine = _engine, .normalizeSkinningWeights = true});
_stbDecoder = createStbProvider(_engine);
_ktxDecoder = createKtx2Provider(_engine);
_resourceLoader->addTextureProvider("image/png", _stbDecoder);
@@ -182,6 +181,26 @@ const float kSensitivity = 100.0f;
_animator = nullptr;
}
- (void)issuePickQuery:(CGPoint)point callback:(PickCallback)callback {
CGPoint pointOriginBottomLeft = CGPointMake(point.x, self.bounds.size.height - point.y);
CGPoint pointScaled = CGPointMake(pointOriginBottomLeft.x * self.contentScaleFactor,
pointOriginBottomLeft.y * self.contentScaleFactor);
_view->pick(pointScaled.x, pointScaled.y,
[callback](View::PickingQueryResult const& result) { callback(result.renderable); });
}
- (NSString* _Nullable)getEntityName:(utils::Entity)entity {
if (!_asset) {
return nil;
}
NameComponentManager* ncm = _assetLoader->getNames();
NameComponentManager::Instance instance = ncm->getInstance(entity);
if (instance) {
return [NSString stringWithUTF8String:ncm->getName(ncm->getInstance(entity))];
}
return nil;
}
- (void)transformToUnitCube {
if (!_asset) {
return;
@@ -15,6 +15,7 @@
*/
#import "FILViewController.h"
#include <UIKit/UIKit.h>
#import "FILModelView.h"
@@ -35,6 +36,9 @@ using namespace filament;
using namespace utils;
using namespace ktxreader;
const float kToastAnimationDuration = 0.25f;
const float kToastDelayDuration = 2.0f;
@interface FILViewController ()
- (void)startDisplayLink;
@@ -43,6 +47,9 @@ using namespace ktxreader;
- (void)createRenderables;
- (void)createLights;
- (void)appWillResignActive:(NSNotification*)notification;
- (void)appDidBecomeActive:(NSNotification*)notification;
@end
@implementation FILViewController {
@@ -50,6 +57,7 @@ using namespace ktxreader;
CFTimeInterval _startTime;
viewer::RemoteServer* _server;
viewer::AutomationEngine* _automation;
UILabel* _toastLabel;
Texture* _skyboxTexture;
Skybox* _skybox;
@@ -57,6 +65,7 @@ using namespace ktxreader;
IndirectLight* _indirectLight;
Entity _sun;
UITapGestureRecognizer* _singleTapRecognizer;
UITapGestureRecognizer* _doubleTapRecognizer;
}
@@ -67,6 +76,16 @@ using namespace ktxreader;
self.title = @"https://google.github.io/filament/remote";
// Observe lifecycle notifications to prevent us from rendering in the background.
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(appWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(appDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
// Arguments:
// --model <path>
// path to glb or gltf file to load from documents directory
@@ -94,9 +113,37 @@ using namespace ktxreader;
_server = new viewer::RemoteServer();
_automation = viewer::AutomationEngine::createDefault();
_doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reloadModel)];
_doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(reloadModel)];
_doubleTapRecognizer.numberOfTapsRequired = 2;
[self.modelView addGestureRecognizer:_doubleTapRecognizer];
_singleTapRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(issuePickingQuery)];
_singleTapRecognizer.numberOfTapsRequired = 1;
[self.modelView addGestureRecognizer:_singleTapRecognizer];
// Create a label at the top of the screen to toast messages to the user.
CGRect labelRect = self.view.bounds;
labelRect.size.height = 50;
_toastLabel = [[UILabel alloc] initWithFrame:labelRect];
_toastLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_toastLabel.textAlignment = NSTextAlignmentCenter;
_toastLabel.textColor = [UIColor whiteColor];
_toastLabel.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5f];
_toastLabel.numberOfLines = 0;
_toastLabel.lineBreakMode = NSLineBreakByWordWrapping;
_toastLabel.text = @"";
_toastLabel.alpha = 0.0f;
[self.view addSubview:_toastLabel];
}
- (void)appWillResignActive:(NSNotification*)notification {
[self stopDisplayLink];
}
- (void)appDidBecomeActive:(NSNotification*)notification {
[self startDisplayLink];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -223,7 +270,8 @@ using namespace ktxreader;
.indirectLight = _indirectLight,
.sunlight = _sun,
};
_automation->applySettings(self.modelView.engine, message->buffer, message->bufferByteCount, content);
_automation->applySettings(
self.modelView.engine, message->buffer, message->bufferByteCount, content);
ColorGrading* const colorGrading = _automation->getColorGrading(self.modelView.engine);
self.modelView.view->setColorGrading(colorGrading);
self.modelView.cameraFocalLength = _automation->getViewerOptions().cameraFocalLength;
@@ -268,7 +316,42 @@ using namespace ktxreader;
[self createDefaultRenderables];
}
- (void)issuePickingQuery {
CGPoint tapLocation = [_singleTapRecognizer locationInView:self.modelView];
__weak typeof(self) weakSelf = self;
[self.modelView issuePickQuery:tapLocation
callback:^(utils::Entity entity) {
NSString* name = [self.modelView getEntityName:entity];
if (!name) {
name = @"<unnamed>";
}
NSString* message = [NSString
stringWithFormat:@"Picked entity %d (%@) at (%d,%d)",
entity.getId(), name, int(tapLocation.x), int(tapLocation.y)];
[weakSelf toastMessage:message];
}];
}
- (void)toastMessage:(NSString*)message {
_toastLabel.text = message;
_toastLabel.alpha = 0.0f;
[UIView animateWithDuration:kToastAnimationDuration
animations:^{
_toastLabel.alpha = 1.0f;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:kToastAnimationDuration
delay:kToastDelayDuration
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
_toastLabel.alpha = 0.0f;
}
completion:nil];
}];
}
- (void)dealloc {
[NSNotificationCenter.defaultCenter removeObserver:self];
delete _server;
delete _automation;
self.modelView.engine->destroy(_indirectLight);
@@ -26,7 +26,9 @@
namespace filament {
static constexpr size_t POST_PROCESS_VARIANT_COUNT = 2;
static constexpr size_t POST_PROCESS_VARIANT_BITS = 1;
static constexpr size_t POST_PROCESS_VARIANT_COUNT = (1u << POST_PROCESS_VARIANT_BITS);
static constexpr size_t POST_PROCESS_VARIANT_MASK = POST_PROCESS_VARIANT_COUNT - 1;
enum class PostProcessVariant : uint8_t {
OPAQUE,
TRANSLUCENT
@@ -135,11 +137,15 @@ template<>
struct utils::EnableIntegerOperators<filament::SamplerBindingPoints> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::ReservedSpecializationConstants> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PostProcessVariant> : public std::true_type {};
template<>
inline constexpr size_t utils::Enum::count<filament::UniformBindingPoints>() { return 9; }
template<>
inline constexpr size_t utils::Enum::count<filament::SamplerBindingPoints>() { return 4; }
template<>
inline constexpr size_t utils::Enum::count<filament::PostProcessVariant>() { return filament::POST_PROCESS_VARIANT_COUNT; }
static_assert(utils::Enum::count<filament::UniformBindingPoints>() <= filament::backend::CONFIG_UNIFORM_BINDING_COUNT);
static_assert(utils::Enum::count<filament::SamplerBindingPoints>() <= filament::backend::CONFIG_SAMPLER_BINDING_COUNT);
@@ -99,6 +99,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float lodBias; // load bias to apply to user materials
float refractionLodOffset;
math::float2 derivativesScale;
// camera position in view space (when camera_at_origin is enabled), i.e. it's (0,0,0).
float oneOverFarMinusNear; // 1 / (f-n), always positive
@@ -111,8 +112,6 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
// AO
float aoSamplingQualityAndEdgeDistance; // <0: no AO, 0: bilinear, !0: bilateral edge distance
float aoBentNormals; // 0: no AO bent normal, >0.0 AO bent normals
float aoReserved0;
float aoReserved1;
// --------------------------------------------------------------------------------------------
// Dynamic Lighting [variant: DYN]
@@ -311,12 +311,8 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT,
bool multisample = false) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -622,8 +618,8 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
@@ -640,6 +636,7 @@ public:
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
bool multisample;
enum {
INVALID,
UNIFORM,
+1 -1
View File
@@ -234,7 +234,7 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl,
for (const auto& info: infoList) {
const std::string name = info.uniformName.c_str();
argBufferBuilder
.texture(info.offset * 2, name, info.type, info.format)
.texture(info.offset * 2, name, info.type, info.format, info.multisample)
.sampler(info.offset * 2 + 1, name + "Smplr");
}
+12 -10
View File
@@ -250,15 +250,18 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type,
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, samplerType, format, precision };
return *this;
}
SamplerFormat format, ParameterPrecision precision, bool multisample) noexcept {
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept {
return parameter(name, samplerType, SamplerFormat::FLOAT, precision);
ASSERT_PRECONDITION(
!multisample || (format != SamplerFormat::SHADOW && (
samplerType == SamplerType::SAMPLER_2D
|| samplerType == SamplerType::SAMPLER_2D_ARRAY)),
"multisample samplers only possible with SAMPLER_2D or SAMPLER_2D_ARRAY,"
" as long as type is not SHADOW");
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample };
return *this;
}
template<typename T, typename>
@@ -556,7 +559,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
param.samplerType, param.format, param.precision);
param.samplerType, param.format, param.precision, param.multisample);
} else if (param.isUniform()) {
ibb.add({{{ param.name.data(), param.name.size() },
uint32_t(param.size == 1u ? 0u : param.size), param.uniformType,
@@ -1165,7 +1168,6 @@ error:
mVariantFilter |= uint32_t(UserVariantFilterBit::SHADOW_RECEIVER);
mVariantFilter |= uint32_t(UserVariantFilterBit::VSM);
mVariantFilter |= uint32_t(UserVariantFilterBit::SSR);
mVariantFilter |= uint32_t(UserVariantFilterBit::STE);
}
// Create chunk tree.
+17 -4
View File
@@ -29,11 +29,20 @@ MetalArgumentBuffer::Builder& filamat::MetalArgumentBuffer::Builder::name(
MetalArgumentBuffer::Builder& MetalArgumentBuffer::Builder::texture(size_t index,
const std::string& name, filament::backend::SamplerType type,
filament::backend::SamplerFormat format) noexcept {
filament::backend::SamplerFormat format,
bool multisample) noexcept {
using namespace filament::backend;
// All combinations of SamplerType and SamplerFormat are valid except for SAMPLER_3D / SHADOW.
assert_invariant(type != filament::backend::SamplerType::SAMPLER_3D ||
format != filament::backend::SamplerFormat::SHADOW);
mArguments.emplace_back(TextureArgument { name, index, type, format });
assert_invariant(type != SamplerType::SAMPLER_3D || format != SamplerFormat::SHADOW);
// multisample textures have restrictions too
assert_invariant(!multisample || (
format != SamplerFormat::SHADOW && (
type == SamplerType::SAMPLER_2D || type == SamplerType::SAMPLER_2D_ARRAY)));
mArguments.emplace_back(TextureArgument { name, index, type, format, multisample });
return *this;
}
@@ -79,6 +88,10 @@ std::ostream& MetalArgumentBuffer::Builder::TextureArgument::write(std::ostream&
break;
}
if (multisample) {
os << "_ms";
}
switch (format) {
case filament::backend::SamplerFormat::INT:
os << "<int>";
+3 -1
View File
@@ -48,7 +48,8 @@ public:
*/
Builder& texture(size_t index, const std::string& name,
filament::backend::SamplerType type,
filament::backend::SamplerFormat format) noexcept;
filament::backend::SamplerFormat format,
bool multisample) noexcept;
/**
* Add a sampler argument to the argument buffer structure.
@@ -69,6 +70,7 @@ public:
size_t index;
filament::backend::SamplerType type;
filament::backend::SamplerFormat format;
bool multisample;
std::ostream& write(std::ostream& os) const;
};
+3 -3
View File
@@ -176,7 +176,7 @@ bool GLSLTools::analyzeComputeShader(const std::string& shaderCode,
bool const ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
utils::slog.e << "ERROR: Unable to parse compute shader:" << utils::io::endl;
utils::slog.e << tShader.getInfoLog() << utils::io::flush;
utils::slog.e << tShader.getInfoLog() << utils::io::endl;
return false;
}
@@ -214,7 +214,7 @@ std::optional<GLSLTools::FragmentShaderInfo> GLSLTools::analyzeFragmentShader(
bool const ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
utils::slog.e << "ERROR: Unable to parse fragment shader:" << utils::io::endl;
utils::slog.e << tShader.getInfoLog() << utils::io::flush;
utils::slog.e << tShader.getInfoLog() << utils::io::endl;
return std::nullopt;
}
@@ -294,7 +294,7 @@ bool GLSLTools::analyzeVertexShader(const std::string& shaderCode,
bool const ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
utils::slog.e << "ERROR: Unable to parse vertex shader" << utils::io::endl;
utils::slog.e << tShader.getInfoLog() << utils::io::flush;
utils::slog.e << tShader.getInfoLog() << utils::io::endl;
return false;
}
+6 -7
View File
@@ -1150,13 +1150,12 @@ char const* CodeGenerator::getOutputTypeName(MaterialBuilder::OutputType type) n
char const* CodeGenerator::getSamplerTypeName(SamplerType type, SamplerFormat format,
bool multisample) const noexcept {
assert(!multisample); // multisample samplers not yet supported.
switch (type) {
case SamplerType::SAMPLER_2D:
switch (format) {
case SamplerFormat::INT: return "isampler2D";
case SamplerFormat::UINT: return "usampler2D";
case SamplerFormat::FLOAT: return "sampler2D";
case SamplerFormat::INT: return multisample ? "isampler2DMS" : "isampler2D";
case SamplerFormat::UINT: return multisample ? "usampler2DMS" : "usampler2D";
case SamplerFormat::FLOAT: return multisample ? "sampler2DMS" : "sampler2D";
case SamplerFormat::SHADOW: return "sampler2DShadow";
}
case SamplerType::SAMPLER_3D:
@@ -1169,9 +1168,9 @@ char const* CodeGenerator::getSamplerTypeName(SamplerType type, SamplerFormat fo
}
case SamplerType::SAMPLER_2D_ARRAY:
switch (format) {
case SamplerFormat::INT: return "isampler2DArray";
case SamplerFormat::UINT: return "usampler2DArray";
case SamplerFormat::FLOAT: return "sampler2DArray";
case SamplerFormat::INT: return multisample ? "isampler2DMSArray": "isampler2DArray";
case SamplerFormat::UINT: return multisample ? "usampler2DMSArray": "usampler2DArray";
case SamplerFormat::FLOAT: return multisample ? "sampler2DMSArray": "sampler2DArray";
case SamplerFormat::SHADOW: return "sampler2DArrayShadow";
}
case SamplerType::SAMPLER_CUBEMAP:
+9 -1
View File
@@ -52,7 +52,7 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream&
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
filament::Variant::isVSMVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_INSTANCED_STEREO",
filament::Variant::isStereoVariant(variant));
hasInstancedStereo(variant, featureLevel));
switch (stage) {
case ShaderStage::VERTEX:
@@ -758,4 +758,12 @@ bool ShaderGenerator::hasSkinningOrMorphing(
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
}
bool ShaderGenerator::hasInstancedStereo(
filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept {
return variant.hasInstancedStereo()
// HACK(exv): Ignore stereo variant when targeting ESSL 1.0. We should properly build a
// system in matc which allows the set of included variants to differ per-feature level.
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
}
} // namespace filament
@@ -115,6 +115,10 @@ private:
filament::Variant variant,
MaterialBuilder::FeatureLevel featureLevel) noexcept;
static bool hasInstancedStereo(
filament::Variant variant,
MaterialBuilder::FeatureLevel featureLevel) noexcept;
MaterialBuilder::PropertyList mProperties;
MaterialBuilder::VariableList mVariables;
MaterialBuilder::OutputList mOutputs;
+1 -2
View File
@@ -60,6 +60,7 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
{ "lodBias", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "refractionLodOffset", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "derivativesScale", 0, Type::FLOAT2 },
{ "oneOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "nearOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
@@ -71,8 +72,6 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
// AO
{ "aoSamplingQualityAndEdgeDistance", 0, Type::FLOAT },
{ "aoBentNormals", 0, Type::FLOAT },
{ "aoReserved0", 0, Type::FLOAT },
{ "aoReserved1", 0, Type::FLOAT },
// ------------------------------------------------------------------------------------
// Dynamic Lighting [variant: DYN]
+30 -10
View File
@@ -72,7 +72,7 @@ TEST(ArgBufferFixup, TextureAndSampler) {
auto argBuffer =
MetalArgumentBuffer::Builder()
.name("myArgumentBuffer")
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT)
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, false)
.sampler(1, "samplerA")
.build();
auto argBufferStr = argBuffer->getMsl();
@@ -88,13 +88,33 @@ TEST(ArgBufferFixup, TextureAndSampler) {
MetalArgumentBuffer::destroy(&argBuffer);
}
TEST(ArgBufferFixup, TextureAndSamplerMS) {
auto argBuffer =
MetalArgumentBuffer::Builder()
.name("myArgumentBuffer")
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, true)
.sampler(1, "samplerA")
.build();
auto argBufferStr = argBuffer->getMsl();
const std::string expected =
"struct myArgumentBuffer {\n"
"texture2d_ms<float> textureA [[id(0)]];\n"
"sampler samplerA [[id(1)]];\n"
"}";
EXPECT_EQ(argBuffer->getMsl(), expected);
MetalArgumentBuffer::destroy(&argBuffer);
}
TEST(ArgBufferFixup, Sorted) {
auto argBuffer =
MetalArgumentBuffer::Builder()
.name("myArgumentBuffer")
.sampler(3, "samplerB")
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT)
.texture(2, "textureB", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT)
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, false)
.texture(2, "textureB", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, false)
.sampler(1, "samplerA")
.build();
auto argBufferStr = argBuffer->getMsl();
@@ -116,12 +136,12 @@ TEST(ArgBufferFixup, TextureTypes) {
auto argBuffer =
MetalArgumentBuffer::Builder()
.name("myArgumentBuffer")
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::INT)
.texture(1, "textureB", SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::UINT)
.texture(2, "textureC", SamplerType::SAMPLER_CUBEMAP, SamplerFormat::FLOAT)
.texture(3, "textureD", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT)
.texture(4, "textureE", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT)
.texture(5, "textureF", SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::SHADOW)
.texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::INT, false)
.texture(1, "textureB", SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::UINT, false)
.texture(2, "textureC", SamplerType::SAMPLER_CUBEMAP, SamplerFormat::FLOAT, false)
.texture(3, "textureD", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, false)
.texture(4, "textureE", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, false)
.texture(5, "textureF", SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::SHADOW, false)
.build();
auto argBufferStr = argBuffer->getMsl();
@@ -147,7 +167,7 @@ TEST(ArgBufferFixup, InvalidType) {
auto argBuffer =
MetalArgumentBuffer::Builder()
.name("myArgumentBuffer")
.texture(0, "textureA", SamplerType::SAMPLER_3D, SamplerFormat::SHADOW)
.texture(0, "textureA", SamplerType::SAMPLER_3D, SamplerFormat::SHADOW, false)
.build();
MetalArgumentBuffer::destroy(&argBuffer);
}, "failed assertion");
+8 -8
View File
@@ -215,7 +215,7 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn,
}
void ApiHandler::addMaterial(MaterialRecord const* material) {
std::unique_lock<utils::Mutex> lock(mStatusMutex);
std::unique_lock const lock(mStatusMutex);
snprintf(statusMaterialId, sizeof(statusMaterialId), "%8.8x", material->key);
mStatusCondition.notify_all();
}
@@ -229,7 +229,7 @@ bool ApiHandler::handleGetStatus(struct mg_connection* conn,
return true;
}
std::unique_lock<utils::Mutex> lock(mStatusMutex);
std::unique_lock<std::mutex> lock(mStatusMutex);
uint64_t const currentStatusCount = mCurrentStatus;
if (mStatusCondition.wait_for(lock, 10s,
[this, currentStatusCount] { return currentStatusCount < mCurrentStatus; })) {
@@ -258,13 +258,13 @@ bool ApiHandler::handlePost(CivetServer* server, struct mg_connection* conn) {
// [material id] [api index] [shader index] [shader source....]
if (uri == "/api/edit") {
struct mg_request_info const* req_info = mg_get_request_info(conn);
size_t msgLen = req_info->content_length;
size_t const msgLen = req_info->content_length;
char buf[1024];
size_t readLen = 0;
std::stringstream sstream;
while (readLen < msgLen) {
int res = mg_read(conn, buf, sizeof(buf));
int const res = mg_read(conn, buf, sizeof(buf));
if (res < 0) {
utils::slog.e << "civet error parsing /api/edit body: " << res << utils::io::endl;
break;
@@ -279,7 +279,7 @@ bool ApiHandler::handlePost(CivetServer* server, struct mg_connection* conn) {
int api;
int shaderIndex;
sstream >> std::hex >> matid >> std::dec >> api >> shaderIndex;
std::string shader = sstream.str().substr(sstream.tellg());
std::string const shader = sstream.str().substr(sstream.tellg());
mServer->handleEditCommand(matid, backend::Backend(api), shaderIndex, shader.c_str(),
shader.size());
@@ -298,7 +298,7 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
mServer->updateActiveVariants();
// Careful not to lock the above line.
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
std::unique_lock const lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "{");
@@ -329,7 +329,7 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
}
if (uri == "/api/matids") {
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
std::unique_lock const lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "[");
int index = 0;
@@ -342,7 +342,7 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
}
if (uri == "/api/materials") {
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
std::unique_lock const lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "[");
int index = 0;
+3 -2
View File
@@ -17,7 +17,8 @@
#ifndef MATDBG_APIHANDLER_H
#define MATDBG_APIHANDLER_H
#include <utils/Mutex.h>
#include <mutex>
#include <condition_variable>
#include <CivetServer.h>
@@ -53,7 +54,7 @@ private:
DebugServer* mServer;
utils::Mutex mStatusMutex;
std::mutex mStatusMutex;
std::condition_variable mStatusCondition;
char statusMaterialId[9] = {};
+5
View File
@@ -12,6 +12,11 @@
margin: 0;
font-family: "Open Sans";
}
@font-face {
font-family: codicon;
src: url(https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/base/browser/ui/codicons/codicon/codicon.ttf);
}
</style>
<script src="api.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.js"></script>
+1 -1
View File
@@ -35,7 +35,7 @@ struct hashCStrings {
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
while (int const c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
+10 -4
View File
@@ -29,7 +29,7 @@ namespace utils::io {
struct ostream_;
class UTILS_PUBLIC ostream : protected utils::PrivateImplementation<ostream_> {
class UTILS_PUBLIC ostream : protected utils::PrivateImplementation<ostream_> {
friend struct ostream_;
public:
@@ -69,6 +69,13 @@ public:
ostream& dec() noexcept;
ostream& hex() noexcept;
/*! @cond PRIVATE */
// Sets a consumer of the log. The consumer is invoked on flush() and replaces the default.
// Thread safe and reentrant.
using ConsumerCallback = void(*)(void*, char const*);
void setConsumer(ConsumerCallback consumer, void* user) noexcept;
/*! @endcond */
protected:
ostream& print(const char* format, ...) noexcept;
@@ -104,7 +111,7 @@ private:
friend ostream& hex(ostream& s) noexcept;
friend ostream& dec(ostream& s) noexcept;
friend ostream& endl(ostream& s) noexcept;
friend ostream& flush(ostream& s) noexcept;
UTILS_PUBLIC friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, FLOAT, DOUBLE,
@@ -132,8 +139,7 @@ inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << '\n'; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
inline ostream& endl(ostream& s) noexcept { return flush(s << '\n'); }
} // namespace utils::io
+25 -25
View File
@@ -31,6 +31,8 @@
#include <emscripten/console.h>
#endif
#include <mutex>
namespace utils {
namespace io {
@@ -46,43 +48,41 @@ public:
ostream& flush() noexcept override;
private:
Priority mPriority;
const Priority mPriority;
};
ostream& LogStream::flush() noexcept {
std::lock_guard lock(mImpl->mLock);
std::lock_guard const lock(mImpl->mLock);
Buffer& buf = getBuffer();
#ifdef __ANDROID__
switch (mPriority) {
case LOG_DEBUG:
__android_log_write(ANDROID_LOG_DEBUG, UTILS_LOG_TAG, buf.get());
break;
case LOG_ERROR:
__android_log_write(ANDROID_LOG_ERROR, UTILS_LOG_TAG, buf.get());
break;
case LOG_WARNING:
__android_log_write(ANDROID_LOG_WARN, UTILS_LOG_TAG, buf.get());
break;
case LOG_INFO:
__android_log_write(ANDROID_LOG_INFO, UTILS_LOG_TAG, buf.get());
break;
case LOG_VERBOSE:
__android_log_write(ANDROID_LOG_VERBOSE, UTILS_LOG_TAG, buf.get());
break;
char const* const str = buf.get();
if (UTILS_UNLIKELY(!str)) {
// this can happen if the log hasn't been written ever
return *this;
}
#ifdef __ANDROID__
int prio = ANDROID_LOG_UNKNOWN;
switch (mPriority) {
case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break;
case LOG_ERROR: prio = ANDROID_LOG_ERROR; break;
case LOG_WARNING: prio = ANDROID_LOG_WARN; break;
case LOG_INFO: prio = ANDROID_LOG_INFO; break;
case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break;
}
__android_log_write(prio, UTILS_LOG_TAG, str);
#elif defined(__EMSCRIPTEN__)
switch (mPriority) {
case LOG_DEBUG:
case LOG_WARNING:
case LOG_INFO:
_emscripten_out(buf.get());
_emscripten_out(str);
break;
case LOG_ERROR:
_emscripten_err(buf.get());
_emscripten_err(str);
break;
case LOG_VERBOSE:
#ifndef NFIL_DEBUG
_emscripten_out(buf.get());
_emscripten_out(str);
#endif
break;
}
@@ -91,14 +91,14 @@ ostream& LogStream::flush() noexcept {
case LOG_DEBUG:
case LOG_WARNING:
case LOG_INFO:
fprintf(stdout, "%s", buf.get());
fprintf(stdout, "%s", str);
break;
case LOG_ERROR:
fprintf(stderr, "%s", buf.get());
fprintf(stderr, "%s", str);
break;
case LOG_VERBOSE:
#ifndef NDEBUG
fprintf(stdout, "%s", buf.get());
fprintf(stdout, "%s", str);
#endif
break;
}
+38 -6
View File
@@ -24,10 +24,14 @@
#include <utils/PrivateImplementation-impl.h>
#include <algorithm>
#include <mutex>
#include <string>
#include <string_view>
#include <utility>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
template class utils::PrivateImplementation<utils::io::ostream_>;
@@ -35,6 +39,34 @@ namespace utils::io {
ostream::~ostream() = default;
void ostream::setConsumer(ConsumerCallback consumer, void* user) noexcept {
auto* const pImpl = mImpl;
std::lock_guard const lock(pImpl->mLock);
pImpl->mConsumer = { consumer, user };
}
ostream& flush(ostream& s) noexcept {
auto* const pImpl = s.mImpl;
pImpl->mLock.lock();
auto const callback = pImpl->mConsumer;
if (UTILS_UNLIKELY(callback.first)) {
auto& buf = s.getBuffer();
char const* const data = buf.get();
if (UTILS_LIKELY(data)) {
char* const str = strdup(data);
buf.reset();
pImpl->mLock.unlock();
// call ConsumerCallback without lock held
callback.first(callback.second, str);
::free(str);
return s;
}
}
pImpl->mLock.unlock();
return s.flush();
}
ostream::Buffer& ostream::getBuffer() noexcept {
return mImpl->mData;
}
@@ -69,12 +101,12 @@ ostream& ostream::print(const char* format, ...) noexcept {
// figure out how much size to we need
va_start(args0, format);
va_copy(args1, args0);
ssize_t s = vsnprintf(nullptr, 0, format, args0);
ssize_t const s = vsnprintf(nullptr, 0, format, args0);
va_end(args0);
{ // scope for the lock
std::lock_guard lock(mImpl->mLock);
std::lock_guard const lock(mImpl->mLock);
Buffer& buf = getBuffer();
@@ -205,14 +237,14 @@ ostream::Buffer::~Buffer() noexcept {
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t written = n < size ? size_t(n) : size;
size_t const written = n < size ? size_t(n) : size;
curr += written;
size -= written;
}
}
void ostream::Buffer::reserve(size_t newSize) noexcept {
size_t offset = curr - buffer;
size_t const offset = curr - buffer;
if (buffer == nullptr) {
buffer = (char*)malloc(newSize);
} else {
@@ -237,8 +269,8 @@ void ostream::Buffer::reset() noexcept {
std::pair<char*, size_t> ostream::Buffer::grow(size_t s) noexcept {
if (UTILS_UNLIKELY(size < s)) {
size_t used = curr - buffer;
size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
size_t const used = curr - buffer;
size_t const newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
reserve(newCapacity);
assert(size >= s);
}
+1
View File
@@ -25,6 +25,7 @@ namespace utils::io {
struct ostream_ {
std::mutex mLock;
ostream::Buffer mData;
std::pair<ostream::ConsumerCallback, void*> mConsumer{};
bool mShowHex = false;
};
+25
View File
@@ -18,8 +18,33 @@
#include <utils/sstream.h>
#include <utils/Log.h>
using namespace utils;
using namespace utils::io;
TEST(ostream, setConsumer) {
slog.d.setConsumer(+[](void*, char const*) {
GTEST_FAIL();
}, nullptr);
// we test that we don't crash if the log is empty and that we don't call the consumer.
flush(slog.d);
slog.d.setConsumer(nullptr, nullptr);
slog.d << "hello world";
// we test that after resetting the consumer, it's not called on flush.
flush(slog.d);
const char* str = "hello world!";
slog.d.setConsumer(+[](void* user, char const* str) {
ASSERT_STREQ(str, (const char*)user);
}, (void*)str);
slog.d << str;
// we test that the comsumer is called with the right string
flush(slog.d);
slog.d.setConsumer(nullptr, nullptr);
}
TEST(sstream, EmptyString) {
sstream ss;
EXPECT_STREQ("", ss.c_str());
+96 -1
View File
@@ -391,6 +391,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, DepthOfFieldOpt
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "cocScale") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->cocScale);
} else if (compare(tok, jsonChunk, "cocAspectRatio") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->cocAspectRatio);
} else if (compare(tok, jsonChunk, "maxApertureDiameter") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->maxApertureDiameter);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
@@ -424,6 +426,7 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, DepthOfFieldOpt
std::ostream& operator<<(std::ostream& out, const DepthOfFieldOptions& in) {
return out << "{\n"
<< "\"cocScale\": " << (in.cocScale) << ",\n"
<< "\"cocAspectRatio\": " << (in.cocAspectRatio) << ",\n"
<< "\"maxApertureDiameter\": " << (in.maxApertureDiameter) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"filter\": " << (in.filter) << ",\n"
@@ -647,6 +650,65 @@ std::ostream& operator<<(std::ostream& out, const MultiSampleAntiAliasingOptions
<< "}";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxType* out) {
if (0 == compare(tokens[i], jsonChunk, "AABB")) { *out = TemporalAntiAliasingOptions::BoxType::AABB; }
else if (0 == compare(tokens[i], jsonChunk, "VARIANCE")) { *out = TemporalAntiAliasingOptions::BoxType::VARIANCE; }
else if (0 == compare(tokens[i], jsonChunk, "AABB_VARIANCE")) { *out = TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE; }
else {
slog.w << "Invalid TemporalAntiAliasingOptions::BoxType: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxType in) {
switch (in) {
case TemporalAntiAliasingOptions::BoxType::AABB: return out << "\"AABB\"";
case TemporalAntiAliasingOptions::BoxType::VARIANCE: return out << "\"VARIANCE\"";
case TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE: return out << "\"AABB_VARIANCE\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxClipping* out) {
if (0 == compare(tokens[i], jsonChunk, "ACCURATE")) { *out = TemporalAntiAliasingOptions::BoxClipping::ACCURATE; }
else if (0 == compare(tokens[i], jsonChunk, "CLAMP")) { *out = TemporalAntiAliasingOptions::BoxClipping::CLAMP; }
else if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = TemporalAntiAliasingOptions::BoxClipping::NONE; }
else {
slog.w << "Invalid TemporalAntiAliasingOptions::BoxClipping: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxClipping in) {
switch (in) {
case TemporalAntiAliasingOptions::BoxClipping::ACCURATE: return out << "\"ACCURATE\"";
case TemporalAntiAliasingOptions::BoxClipping::CLAMP: return out << "\"CLAMP\"";
case TemporalAntiAliasingOptions::BoxClipping::NONE: return out << "\"NONE\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::JitterPattern* out) {
if (0 == compare(tokens[i], jsonChunk, "RGSS_X4")) { *out = TemporalAntiAliasingOptions::JitterPattern::RGSS_X4; }
else if (0 == compare(tokens[i], jsonChunk, "UNIFORM_HELIX_X4")) { *out = TemporalAntiAliasingOptions::JitterPattern::UNIFORM_HELIX_X4; }
else if (0 == compare(tokens[i], jsonChunk, "HALTON_23_X8")) { *out = TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X8; }
else if (0 == compare(tokens[i], jsonChunk, "HALTON_23_X16")) { *out = TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X16; }
else {
slog.w << "Invalid TemporalAntiAliasingOptions::JitterPattern: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::JitterPattern in) {
switch (in) {
case TemporalAntiAliasingOptions::JitterPattern::RGSS_X4: return out << "\"RGSS_X4\"";
case TemporalAntiAliasingOptions::JitterPattern::UNIFORM_HELIX_X4: return out << "\"UNIFORM_HELIX_X4\"";
case TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X8: return out << "\"HALTON_23_X8\"";
case TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X16: return out << "\"HALTON_23_X16\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
@@ -657,8 +719,30 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAli
i = parse(tokens, i + 1, jsonChunk, &out->filterWidth);
} else if (compare(tok, jsonChunk, "feedback") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->feedback);
} else if (compare(tok, jsonChunk, "lodBias") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lodBias);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "upscaling") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->upscaling);
} else if (compare(tok, jsonChunk, "filterHistory") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->filterHistory);
} else if (compare(tok, jsonChunk, "filterInput") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->filterInput);
} else if (compare(tok, jsonChunk, "useYCoCg") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->useYCoCg);
} else if (compare(tok, jsonChunk, "boxType") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->boxType);
} else if (compare(tok, jsonChunk, "boxClipping") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->boxClipping);
} else if (compare(tok, jsonChunk, "jitterPattern") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->jitterPattern);
} else if (compare(tok, jsonChunk, "varianceGamma") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->varianceGamma);
} else if (compare(tok, jsonChunk, "preventFlickering") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->preventFlickering);
} else if (compare(tok, jsonChunk, "historyReprojection") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->historyReprojection);
} else {
slog.w << "Invalid TemporalAntiAliasingOptions key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
@@ -675,7 +759,18 @@ std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& i
return out << "{\n"
<< "\"filterWidth\": " << (in.filterWidth) << ",\n"
<< "\"feedback\": " << (in.feedback) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << "\n"
<< "\"lodBias\": " << (in.lodBias) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"upscaling\": " << to_string(in.upscaling) << ",\n"
<< "\"filterHistory\": " << to_string(in.filterHistory) << ",\n"
<< "\"filterInput\": " << to_string(in.filterInput) << ",\n"
<< "\"useYCoCg\": " << to_string(in.useYCoCg) << ",\n"
<< "\"boxType\": " << (in.boxType) << ",\n"
<< "\"boxClipping\": " << (in.boxClipping) << ",\n"
<< "\"jitterPattern\": " << (in.jitterPattern) << ",\n"
<< "\"varianceGamma\": " << (in.varianceGamma) << ",\n"
<< "\"preventFlickering\": " << to_string(in.preventFlickering) << ",\n"
<< "\"historyReprojection\": " << to_string(in.historyReprojection) << "\n"
<< "}";
}
+9
View File
@@ -69,6 +69,15 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, MultiSampleAntiAliasingOptions* out);
std::ostream& operator<<(std::ostream& out, const MultiSampleAntiAliasingOptions& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxType* out);
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxType in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxClipping* out);
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxClipping in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::JitterPattern* out);
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::JitterPattern in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions* out);
std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& in);
+23
View File
@@ -792,6 +792,28 @@ void ViewerGui::updateUserInterface() {
ImGui::Checkbox("Lens Flare", &mSettings.view.bloom.lensFlare);
}
if (ImGui::CollapsingHeader("TAA Options")) {
ImGui::Checkbox("Upscaling", &mSettings.view.taa.upscaling);
ImGui::Checkbox("History Reprojection", &mSettings.view.taa.historyReprojection);
ImGui::SliderFloat("Feedback", &mSettings.view.taa.feedback, 0.0f, 1.0f);
ImGui::Checkbox("Filter History", &mSettings.view.taa.filterHistory);
ImGui::Checkbox("Filter Input", &mSettings.view.taa.filterInput);
ImGui::SliderFloat("FilterWidth", &mSettings.view.taa.filterWidth, 0.2f, 2.0f);
ImGui::SliderFloat("LOD bias", &mSettings.view.taa.lodBias, -8.0f, 0.0f);
ImGui::Checkbox("Use YCoCg", &mSettings.view.taa.useYCoCg);
ImGui::Checkbox("Prevent Flickering", &mSettings.view.taa.preventFlickering);
int jitterSequence = (int)mSettings.view.taa.jitterPattern;
int boxClipping = (int)mSettings.view.taa.boxClipping;
int boxType = (int)mSettings.view.taa.boxType;
ImGui::Combo("Jitter Pattern", &jitterSequence, "RGSS x4\0Uniform Helix x4\0Halton x8\0Halton x16\0\0");
ImGui::Combo("Box Clipping", &boxClipping, "Accurate\0Clamp\0None\0\0");
ImGui::Combo("Box Type", &boxType, "AABB\0Variance\0Both\0\0");
ImGui::SliderFloat("Variance Gamma", &mSettings.view.taa.varianceGamma, 0.75f, 1.25f);
mSettings.view.taa.boxClipping = (TemporalAntiAliasingOptions::BoxClipping)boxClipping;
mSettings.view.taa.boxType = (TemporalAntiAliasingOptions::BoxType)boxType;
mSettings.view.taa.jitterPattern = (TemporalAntiAliasingOptions::JitterPattern)jitterSequence;
}
if (ImGui::CollapsingHeader("SSAO Options")) {
auto& ssao = mSettings.view.ssao;
@@ -1014,6 +1036,7 @@ void ViewerGui::updateUserInterface() {
ImGui::Checkbox("Enabled##dofEnabled", &mSettings.view.dof.enabled);
ImGui::SliderFloat("Focus distance", &mSettings.viewer.cameraFocusDistance, 0.0f, 30.0f);
ImGui::SliderFloat("Blur scale", &mSettings.view.dof.cocScale, 0.1f, 10.0f);
ImGui::SliderFloat("CoC aspect-ratio", &mSettings.view.dof.cocAspectRatio, 0.25f, 4.0f);
ImGui::SliderInt("Ring count", &dofRingCount, 1, 17);
ImGui::SliderInt("Max CoC", &dofMaxCoC, 1, 32);
ImGui::Checkbox("Native Resolution", &mSettings.view.dof.nativeResolution);
+14 -9
View File
@@ -5,22 +5,27 @@ void main() {
inputs.normalizedUV = position.xy * 0.5 + 0.5;
vec4 position = getPosition();
inputs.position = getPosition();
// GL convention to inverted DX convention
position.z = position.z * -0.5 + 0.5;
// Invoke user code
postProcessVertex(inputs);
// (vertex domain) GL convention to inverted DX convention
inputs.position.z = inputs.position.z * -0.5 + 0.5;
#if defined(TARGET_VULKAN_ENVIRONMENT)
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
inputs.position.y = -inputs.position.y;
#endif
// Adjust clip-space
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT)
// This is not needed in Vulkan or Metal because clipControl is always (1, 0)
// (We don't use a dot() here because it workaround a spirv-opt optimization that in turn
// (We don't use a dot() here because it works around a spirv-opt optimization that in turn
// causes a crash on PowerVR, see #5118)
position.z = position.z * frameUniforms.clipControl.x + position.w * frameUniforms.clipControl.y;
inputs.position.z = inputs.position.z * frameUniforms.clipControl.x + inputs.position.w * frameUniforms.clipControl.y;
#endif
// Invoke user code
postProcessVertex(inputs);
// Handle user-defined interpolated attributes
#if defined(VARIABLE_CUSTOM0)
VARIABLE_CUSTOM_AT0 = inputs.VARIABLE_CUSTOM0;
@@ -36,5 +41,5 @@ void main() {
#endif
// some PowerVR drivers crash when gl_Position is written more than once
gl_Position = position;
gl_Position = inputs.position;
}
+1 -8
View File
@@ -1,11 +1,4 @@
/** @public-api */
vec4 getPosition() {
vec4 pos = position;
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
#if defined(TARGET_VULKAN_ENVIRONMENT)
pos.y = -pos.y;
#endif
return pos;
return position;
}
+3
View File
@@ -5,6 +5,9 @@ struct PostProcessVertexInputs {
// We provide normalized texture coordinates to custom vertex shaders.
vec2 normalizedUV;
// vertex position, can be modified by the user code
vec4 position;
#ifdef VARIABLE_CUSTOM0
vec4 VARIABLE_CUSTOM0;
#endif
+4 -1
View File
@@ -37,8 +37,11 @@ float normalFiltering(float perceptualRoughness, const vec3 worldNormal) {
vec3 du = dFdx(worldNormal);
vec3 dv = dFdy(worldNormal);
float variance = materialParams._specularAntiAliasingVariance * (dot(du, du) + dot(dv, dv));
// specular AA factor to correct for resolution scaling (DSR and TAAx4)
du *= frameUniforms.derivativesScale.x;
dv *= frameUniforms.derivativesScale.y;
float variance = materialParams._specularAntiAliasingVariance * (dot(du, du) + dot(dv, dv));
float roughness = perceptualRoughnessToRoughness(perceptualRoughness);
float kernelRoughness = min(2.0 * variance, materialParams._specularAntiAliasingThreshold);
float squareRoughness = saturate(roughness * roughness + kernelRoughness);
+1 -3
View File
@@ -27,9 +27,7 @@ JsonType JsonishLexer::readIdentifier() noexcept {
consume();
}
const char* lexemeEnd = mCursor - 1;
size_t lexemeSize = lexemeEnd - lexemeStart;
size_t lexemeSize = mCursor - lexemeStart;
// Check what kind of keyword we got here.
if (strncmp("true", lexemeStart, lexemeSize) == 0) {
+3 -1
View File
@@ -229,7 +229,9 @@ static bool reflectParameters(const MaterialBuilder& builder) {
std::cout << R"( "format": ")" <<
Enums::toString(parameter.format) << "\"," << std::endl;
std::cout << R"( "precision": ")" <<
Enums::toString(parameter.precision) << "\"" << std::endl;
Enums::toString(parameter.precision) << "\"," << std::endl;
std::cout << R"( "multisample": ")" <<
(parameter.multisample ? "true" : "false")<< "\"" << std::endl;
} else if (parameter.isUniform()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.uniformType) << "\"," << std::endl;
+22 -20
View File
@@ -160,7 +160,6 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
std::cerr << "parameters: precision must be a STRING." << std::endl;
return false;
}
auto precisionString = precisionValue->toJsonString();
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
return logEnumIssue("parameters", *precisionString, Enums::map<ParameterPrecision>());
@@ -173,20 +172,27 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
std::cerr << "parameters: format must be a STRING." << std::endl;
return false;
}
auto formatString = formatValue->toJsonString();
if (!Enums::isValid<SamplerFormat>(formatString->getString())){
return logEnumIssue("parameters", *formatString, Enums::map<SamplerFormat>());
}
}
const JsonishValue* multiSampleValue = jsonObject.getValue("multisample");
if (multiSampleValue) {
if (multiSampleValue->getType() != JsonishValue::BOOL) {
std::cerr << "parameters: multisample must be a BOOL." << std::endl;
return false;
}
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
size_t arraySize = extractArraySize(typeString);
size_t const arraySize = extractArraySize(typeString);
if (Enums::isValid<UniformType>(typeString)) {
MaterialBuilder::UniformType type = Enums::toEnum<UniformType>(typeString);
MaterialBuilder::UniformType const type = Enums::toEnum<UniformType>(typeString);
ParameterPrecision precision = ParameterPrecision::DEFAULT;
if (precisionValue) {
precision =
@@ -205,22 +211,18 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
return false;
}
MaterialBuilder::SamplerType type = Enums::toEnum<SamplerType>(typeString);
if (precisionValue && formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format, precision);
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format);
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, precision);
} else {
builder.parameter(nameString.c_str(), type);
}
MaterialBuilder::SamplerType const type = Enums::toEnum<SamplerType>(typeString);
auto format = formatValue ? Enums::toEnum<SamplerFormat>(
formatValue->toJsonString()->getString()) : SamplerFormat::FLOAT;
auto precision = precisionValue ? Enums::toEnum<ParameterPrecision>(
precisionValue->toJsonString()->getString()) : ParameterPrecision::DEFAULT;
auto multisample = multiSampleValue ? multiSampleValue->toJsonBool()->getBool() : false;
builder.parameter(nameString.c_str(), type, format, precision, multisample);
} else {
std::cerr << "parameters: the type '" << typeString
<< "' for parameter with name '" << nameString << "' is neither a valid uniform "
+13 -1
View File
@@ -60,6 +60,7 @@ Filament.loadGeneratedExtensions = function() {
Filament.View.prototype.setDepthOfFieldOptionsDefaults = function(overrides) {
const options = {
cocScale: 1.0,
cocAspectRatio: 1.0,
maxApertureDiameter: 0.01,
enabled: false,
filter: Filament.View$DepthOfFieldOptions$Filter.MEDIAN,
@@ -138,8 +139,19 @@ Filament.loadGeneratedExtensions = function() {
Filament.View.prototype.setTemporalAntiAliasingOptionsDefaults = function(overrides) {
const options = {
filterWidth: 1.0,
feedback: 0.04,
feedback: 0.12,
lodBias: -1.0,
enabled: false,
upscaling: false,
filterHistory: true,
filterInput: true,
useYCoCg: false,
boxType: Filament.View$TemporalAntiAliasingOptions$BoxType.AABB,
boxClipping: Filament.View$TemporalAntiAliasingOptions$BoxClipping.ACCURATE,
jitterPattern: Filament.View$TemporalAntiAliasingOptions$JitterPattern.HALTON_23_X16,
varianceGamma: 1.0,
preventFlickering: false,
historyReprojection: true,
};
return Object.assign(options, overrides);
};
+70 -2
View File
@@ -1409,6 +1409,10 @@ export interface View$DepthOfFieldOptions {
* circle of confusion scale factor (amount of blur)
*/
cocScale?: number;
/**
* width/height aspect ratio of the circle of confusion (simulate anamorphic lenses)
*/
cocAspectRatio?: number;
/**
* maximum aperture diameter in meters (zero to disable rotation)
*/
@@ -1614,7 +1618,7 @@ export interface View$AmbientOcclusionOptions {
}
/**
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
* Options for Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
*/
export interface View$MultiSampleAntiAliasingOptions {
@@ -1636,23 +1640,87 @@ export interface View$MultiSampleAntiAliasingOptions {
customResolve?: boolean;
}
export enum View$TemporalAntiAliasingOptions$BoxType {
AABB, // use an AABB neighborhood
VARIANCE, // use the variance of the neighborhood (not recommended)
AABB_VARIANCE, // use both AABB and variance
}
export enum View$TemporalAntiAliasingOptions$BoxClipping {
ACCURATE, // Accurate box clipping
CLAMP, // clamping
NONE, // no rejections (use for debugging)
}
export enum View$TemporalAntiAliasingOptions$JitterPattern {
RGSS_X4,
UNIFORM_HELIX_X4,
HALTON_23_X8,
HALTON_23_X16,
}
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth`, `feedback` and `jitterPattern`, however, could be changed at any time.
*
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
*
* @see setTemporalAntiAliasingOptions()
*/
export interface View$TemporalAntiAliasingOptions {
/**
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
*/
filterWidth?: number;
/**
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
*/
feedback?: number;
/**
* texturing lod bias (typically -1 or -2)
*/
lodBias?: number;
/**
* enables or disables temporal anti-aliasing
*/
enabled?: boolean;
/**
* 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
*/
upscaling?: boolean;
/**
* whether to filter the history buffer
*/
filterHistory?: boolean;
/**
* whether to apply the reconstruction filter to the input
*/
filterInput?: boolean;
/**
* whether to use the YcoCg color-space for history rejection
*/
useYCoCg?: boolean;
/**
* type of color gamut box
*/
boxType?: View$TemporalAntiAliasingOptions$BoxType;
/**
* clipping algorithm
*/
boxClipping?: View$TemporalAntiAliasingOptions$BoxClipping;
jitterPattern?: View$TemporalAntiAliasingOptions$JitterPattern;
varianceGamma?: number;
/**
* adjust the feedback dynamically to reduce flickering
*/
preventFlickering?: boolean;
/**
* whether to apply history reprojection (debug option)
*/
historyReprojection?: boolean;
}
/**
+12
View File
@@ -58,6 +58,7 @@ value_object<View::FogOptions>("View$FogOptions")
value_object<View::DepthOfFieldOptions>("View$DepthOfFieldOptions")
.field("cocScale", &View::DepthOfFieldOptions::cocScale)
.field("cocAspectRatio", &View::DepthOfFieldOptions::cocAspectRatio)
.field("maxApertureDiameter", &View::DepthOfFieldOptions::maxApertureDiameter)
.field("enabled", &View::DepthOfFieldOptions::enabled)
.field("filter", &View::DepthOfFieldOptions::filter)
@@ -119,7 +120,18 @@ value_object<View::MultiSampleAntiAliasingOptions>("View$MultiSampleAntiAliasing
value_object<View::TemporalAntiAliasingOptions>("View$TemporalAntiAliasingOptions")
.field("filterWidth", &View::TemporalAntiAliasingOptions::filterWidth)
.field("feedback", &View::TemporalAntiAliasingOptions::feedback)
.field("lodBias", &View::TemporalAntiAliasingOptions::lodBias)
.field("enabled", &View::TemporalAntiAliasingOptions::enabled)
.field("upscaling", &View::TemporalAntiAliasingOptions::upscaling)
.field("filterHistory", &View::TemporalAntiAliasingOptions::filterHistory)
.field("filterInput", &View::TemporalAntiAliasingOptions::filterInput)
.field("useYCoCg", &View::TemporalAntiAliasingOptions::useYCoCg)
.field("boxType", &View::TemporalAntiAliasingOptions::boxType)
.field("boxClipping", &View::TemporalAntiAliasingOptions::boxClipping)
.field("jitterPattern", &View::TemporalAntiAliasingOptions::jitterPattern)
.field("varianceGamma", &View::TemporalAntiAliasingOptions::varianceGamma)
.field("preventFlickering", &View::TemporalAntiAliasingOptions::preventFlickering)
.field("historyReprojection", &View::TemporalAntiAliasingOptions::historyReprojection)
;
value_object<View::ScreenSpaceReflectionsOptions>("View$ScreenSpaceReflectionsOptions")
+19
View File
@@ -33,6 +33,25 @@ enum_<View::DepthOfFieldOptions::Filter>("View$DepthOfFieldOptions$Filter")
.value("MEDIAN", View::DepthOfFieldOptions::Filter::MEDIAN)
;
enum_<View::TemporalAntiAliasingOptions::BoxType>("View$TemporalAntiAliasingOptions$BoxType")
.value("AABB", View::TemporalAntiAliasingOptions::BoxType::AABB)
.value("VARIANCE", View::TemporalAntiAliasingOptions::BoxType::VARIANCE)
.value("AABB_VARIANCE", View::TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE)
;
enum_<View::TemporalAntiAliasingOptions::BoxClipping>("View$TemporalAntiAliasingOptions$BoxClipping")
.value("ACCURATE", View::TemporalAntiAliasingOptions::BoxClipping::ACCURATE)
.value("CLAMP", View::TemporalAntiAliasingOptions::BoxClipping::CLAMP)
.value("NONE", View::TemporalAntiAliasingOptions::BoxClipping::NONE)
;
enum_<View::TemporalAntiAliasingOptions::JitterPattern>("View$TemporalAntiAliasingOptions$JitterPattern")
.value("RGSS_X4", View::TemporalAntiAliasingOptions::JitterPattern::RGSS_X4)
.value("UNIFORM_HELIX_X4", View::TemporalAntiAliasingOptions::JitterPattern::UNIFORM_HELIX_X4)
.value("HALTON_23_X8", View::TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X8)
.value("HALTON_23_X16", View::TemporalAntiAliasingOptions::JitterPattern::HALTON_23_X16)
;
enum_<View::AntiAliasing>("View$AntiAliasing")
.value("NONE", View::AntiAliasing::NONE)
.value("FXAA", View::AntiAliasing::FXAA)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.49.1",
"version": "1.49.3",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",

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