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
76 changed files with 1110 additions and 746 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.2'
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.2'
pod 'Filament', '~> 1.49.3'
```
### Snapshots
+7
View File
@@ -7,6 +7,13 @@ 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
@@ -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 {
@@ -1878,7 +1882,11 @@ 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` and `feedback`, however, could be changed an any time.
* `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 {
@@ -1888,7 +1896,7 @@ public class View {
*/
AABB,
/**
* use the variance of the neighborhood
* use the variance of the neighborhood (not recommended)
*/
VARIANCE,
/**
@@ -1912,18 +1920,33 @@ public class View {
NONE,
}
public enum JitterPattern {
RGSS_X4,
UNIFORM_HELIX_X4,
HALTON_23_X8,
HALTON_23_X16,
}
/**
* reconstruction filter width typically between 0.2 (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
*/
@@ -1940,12 +1963,15 @@ public class View {
* type of color gamut box
*/
@NonNull
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.VARIANCE;
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
*/
@@ -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.2
VERSION_NAME=1.49.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+18 -17
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
@@ -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;
+16 -12
View File
@@ -730,6 +730,10 @@ bool MetalDriver::isParallelShaderCompileSupported() {
return true;
}
bool MetalDriver::isDepthStencilResolveSupported() {
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1260,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");
@@ -1322,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");
@@ -1381,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;
@@ -1438,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");
}
@@ -1584,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,
@@ -1602,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
};
@@ -820,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);
+17 -4
View File
@@ -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:
@@ -2982,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)) {
@@ -3004,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)) {
@@ -3019,6 +3031,7 @@ void OpenGLDriver::popGroupMarker(int) {
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
#endif
}
void OpenGLDriver::startCapture(int) {
+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;
+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);
+23 -7
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 {
@@ -426,17 +427,23 @@ 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` and `feedback`, however, could be changed an any time.
* `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.2 (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
VARIANCE, //!< use the variance of the neighborhood (not recommended)
AABB_VARIANCE //!< use both AABB and variance
};
@@ -446,11 +453,20 @@ struct TemporalAntiAliasingOptions {
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::VARIANCE; //!< type of color gamut box
BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm
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)
+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;
}
+2 -1
View File
@@ -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)
+177 -49
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,15 +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 {
@@ -169,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) },
@@ -186,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),
@@ -261,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 {
@@ -757,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);
@@ -914,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 {
@@ -958,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;
@@ -1119,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,
@@ -1165,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 | ...
@@ -1329,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);
/*
@@ -1381,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);
@@ -1392,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);
@@ -1771,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{
@@ -1878,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,
@@ -1917,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
@@ -1961,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) {
@@ -1969,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
@@ -1991,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;
@@ -2500,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,
@@ -2512,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 });
@@ -2554,6 +2625,7 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial(
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
@@ -2593,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);
@@ -2627,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) {
@@ -2660,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);
@@ -2980,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;
@@ -3001,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);
@@ -3024,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 {
@@ -3063,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 };
+22 -13
View File
@@ -162,7 +162,7 @@ public:
FrameGraphId<FrameGraphTexture> depth,
const CameraInfo& cameraInfo,
bool translucent,
float bokehAspectRatio,
math::float2 bokehScale,
const DepthOfFieldOptions& dofOptions) noexcept;
// Bloom
@@ -171,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,
@@ -211,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,
@@ -250,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,
@@ -269,10 +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];
}
class PostProcessMaterial {
public:
PostProcessMaterial() noexcept;
@@ -338,10 +344,6 @@ private:
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,
@@ -357,8 +359,15 @@ private:
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;
+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();
+30 -8
View File
@@ -495,6 +495,14 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// 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;
}
}
}
@@ -525,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();
@@ -614,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
@@ -818,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());
}
@@ -1011,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;
}
}
// --------------------------------------------------------------------------------------------
@@ -1026,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_;
}
+18 -7
View File
@@ -725,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 {
@@ -985,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);
+91 -76
View File
@@ -24,7 +24,12 @@ material {
precision: high
},
{
type : float[9],
type : float2,
name : jitter,
precision: high
},
{
type : float4[9],
name : filterWeights
}
],
@@ -63,6 +68,11 @@ material {
type : bool,
name : preventFlickering,
default : false
},
{
type : float,
name : varianceGamma,
default : 1.0
}
],
variables : [
@@ -81,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
@@ -98,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
@@ -209,25 +208,26 @@ 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 (materialConstants_historyReprojection) {
uv.zw = uvToRenderTargetUV(uv.zw);
// 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);
vec4 history;
if (materialConstants_filterHistory) {
history = sampleTextureCatmullRom(materialParams_history, uv.zw,
@@ -240,80 +240,99 @@ void postProcess(inout PostProcessInputs postProcess) {
history.rgb = RGB_YCoCg(history.rgb);
}
// build the history clamping box
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];
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_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) {
for (int i = 0; i < 9; i++) {
s[i] = RGB_YCoCg(s[i]);
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]);
}
}
color.rgb = s[4].rgb;
}
vec4 filtered;
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++) {
filtered.rgb += s[i] * materialParams.filterWeights[i];
float w = materialParams.filterWeights[i][j];
filtered.rgb += s[i] * w;
confidence = max(confidence, w);
}
} else {
filtered = color;
confidence = float(materialParams.jitter.x * subPixelOffset.x > 0.0 &&
materialParams.jitter.y * subPixelOffset.y > 0.0);
}
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 (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;
// build the history clamping box
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;
}
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 - VARIANCE_GAMMA * sigma;
boxmax = a0 + VARIANCE_GAMMA * sigma;
} else {
boxmin = min(boxmin, a0 - VARIANCE_GAMMA * sigma);
boxmax = max(boxmax, a0 + VARIANCE_GAMMA * sigma);
}
}
// history clamping
history = clipToBox(materialConstants_boxClipping, boxmin, boxmax, filtered, history);
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);
}
float lumaColor = luma(filtered.rgb);
float lumaHistory = luma(history.rgb);
float alpha = materialParams.alpha;
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));
@@ -334,16 +353,12 @@ void postProcess(inout PostProcessInputs postProcess) {
result.rgb = YCoCg_RGB(result.rgb);
}
// 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 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.2"
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.2/filament-v1.49.2-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);
@@ -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;
};
+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");
+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
@@ -111,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,
+36
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"
@@ -685,6 +688,27 @@ std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxClip
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;
@@ -695,8 +719,12 @@ 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) {
@@ -707,6 +735,10 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAli
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) {
@@ -727,12 +759,16 @@ std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& i
return out << "{\n"
<< "\"filterWidth\": " << (in.filterWidth) << ",\n"
<< "\"feedback\": " << (in.feedback) << ",\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"
<< "}";
+3
View File
@@ -75,6 +75,9 @@ std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxType
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);
+8 -1
View File
@@ -793,19 +793,25 @@ void ViewerGui::updateUserInterface() {
}
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.0f, 1.0f);
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")) {
@@ -1030,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);
+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 "
+7 -2
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,13 +139,17 @@ 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.VARIANCE,
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,
};
+29 -4
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 {
@@ -1638,7 +1642,7 @@ export interface View$MultiSampleAntiAliasingOptions {
export enum View$TemporalAntiAliasingOptions$BoxType {
AABB, // use an AABB neighborhood
VARIANCE, // use the variance of the neighborhood
VARIANCE, // use the variance of the neighborhood (not recommended)
AABB_VARIANCE, // use both AABB and variance
}
@@ -1648,26 +1652,45 @@ export enum View$TemporalAntiAliasingOptions$BoxClipping {
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` and `feedback`, however, could be changed an any time.
* `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.2 (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
*/
@@ -1688,6 +1711,8 @@ export interface View$TemporalAntiAliasingOptions {
* clipping algorithm
*/
boxClipping?: View$TemporalAntiAliasingOptions$BoxClipping;
jitterPattern?: View$TemporalAntiAliasingOptions$JitterPattern;
varianceGamma?: number;
/**
* adjust the feedback dynamically to reduce flickering
*/
+5
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,12 +120,16 @@ 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)
;
+7
View File
@@ -45,6 +45,13 @@ enum_<View::TemporalAntiAliasingOptions::BoxClipping>("View$TemporalAntiAliasing
.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.2",
"version": "1.49.3",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",
+3 -1
View File
@@ -203,7 +203,9 @@ set(HTML_FILES
skinning.html
suzanne.html
test-filament-viewer.html
triangle.html)
triangle.html
benchmark-shader-compilation.html
benchmark-shader-compilation.js)
set(ASSET_FILES
assets/favicon.png)
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Shader Compilation Benchmark</title>
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<style>
body { margin: 0; overflow: hidden; }
canvas { touch-action: none; width: 100%; height: 100%; }
.frame-time { position: absolute; color: #ffffff; }
</style>
</head>
<body>
<div class="frame-time">
Average Frame Time: <span id="frame-time-counter">Calculating...</span>
</div>
<canvas></canvas>
<script src="filament.js"></script>
<script src="gl-matrix-min.js"></script>
<script src="benchmark-shader-compilation.js"></script>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
Filament.init(['nonlit.filamat'], () => {
window.VertexAttribute = Filament.VertexAttribute;
window.AttributeType = Filament.VertexBuffer$AttributeType;
window.Projection = Filament.Camera$Projection;
window.app = new App(
document.getElementsByTagName('canvas')[0],
document.getElementById('frame-time-counter'));
});
const NUMBER_OF_TRIANGLES = 100;
// probably 60 fps; record 1 seconds ish worth of frames.
const NUMBER_OF_FRAMES_TO_RECORD_FPS = 1 * 60;
class App {
constructor(canvas, frameTimeCounter) {
this.canvas = canvas;
this.frameTimeCounter = frameTimeCounter;
this.lastFrameTime = null;
this.frameDeltas = [];
for (let i = 0; i < NUMBER_OF_FRAMES_TO_RECORD_FPS; ++i) {
this.frameDeltas.push(0);
}
this.frameDeltasIndex = 0;
this.frameDeltasSum = 0;
this.frameTimeIsValid = false;
const engine = this.engine = Filament.Engine.create(this.canvas);
this.scene = engine.createScene();
this.triangles = [];
for (let i = 0; i < NUMBER_OF_TRIANGLES; ++i) {
const entity = Filament.EntityManager.get().create();
this.triangles.push({entity: entity});
this.scene.addEntity(entity);
}
const TRIANGLE_POSITIONS = new Float32Array([
1,
0,
Math.cos(Math.PI * 2 / 3),
Math.sin(Math.PI * 2 / 3),
Math.cos(Math.PI * 4 / 3),
Math.sin(Math.PI * 4 / 3),
]);
const TRIANGLE_COLORS =
new Uint32Array([0xffff0000, 0xff00ff00, 0xff0000ff]);
this.vb =
Filament.VertexBuffer.Builder()
.vertexCount(3)
.bufferCount(2)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, 8)
.attribute(VertexAttribute.COLOR, 1, AttributeType.UBYTE4, 0, 4)
.normalized(VertexAttribute.COLOR)
.build(engine);
this.vb.setBufferAt(engine, 0, TRIANGLE_POSITIONS);
this.vb.setBufferAt(engine, 1, TRIANGLE_COLORS);
this.ib = Filament.IndexBuffer.Builder()
.indexCount(3)
.bufferType(Filament.IndexBuffer$IndexType.USHORT)
.build(engine);
this.ib.setBuffer(engine, new Uint16Array([0, 1, 2]));
this.swapChain = engine.createSwapChain();
this.renderer = engine.createRenderer();
this.camera = engine.createCamera(Filament.EntityManager.get().create());
this.view = engine.createView();
this.view.setSampleCount(4);
this.view.setCamera(this.camera);
this.view.setScene(this.scene);
this.renderer.setClearOptions(
{clearColor: [0.0, 0.1, 0.2, 1.0], clear: true});
this.resize();
this.render = this.render.bind(this);
this.resize = this.resize.bind(this);
window.addEventListener('resize', this.resize);
window.requestAnimationFrame(this.render);
}
render() {
const frameTime = Date.now();
if (this.lastFrameTime) {
const delta = frameTime - this.lastFrameTime;
this.frameDeltasSum =
this.frameDeltasSum - this.frameDeltas[this.frameDeltasIndex] + delta;
this.frameDeltas[this.frameDeltasIndex] = delta;
this.frameDeltasIndex =
(this.frameDeltasIndex + 1) % NUMBER_OF_FRAMES_TO_RECORD_FPS;
if (this.frameDeltasIndex == 0) {
this.frameTimeIsValid = true;
}
}
this.lastFrameTime = frameTime;
if (this.frameTimeIsValid) {
const averageFrameDelta =
this.frameDeltasSum * 1.0 / NUMBER_OF_FRAMES_TO_RECORD_FPS;
this.frameTimeCounter.innerHTML = averageFrameDelta.toFixed(2) + ' ms';
}
this.triangles.forEach(triangle => {
if (triangle.mat) {
this.engine.destroyMaterial(triangle.mat);
}
triangle.mat = this.engine.createMaterial('nonlit.filamat');
const matinst = triangle.mat.getDefaultInstance();
Filament.RenderableManager.Builder(1)
.boundingBox({center: [-1, -1, -1], halfExtent: [1, 1, 1]})
.material(0, matinst)
.geometry(
0, Filament.RenderableManager$PrimitiveType.TRIANGLES, this.vb,
this.ib)
.build(this.engine, triangle.entity);
});
const radians = Date.now() / 1000;
const transform = mat4.fromRotation(mat4.create(), radians, [0, 0, 1]);
const tcm = this.engine.getTransformManager();
const inst = tcm.getInstance(this.triangles[0].entity);
tcm.setTransform(inst, transform);
inst.delete();
this.renderer.render(this.swapChain, this.view);
window.requestAnimationFrame(this.render);
}
resize() {
const dpr = window.devicePixelRatio;
const width = this.canvas.width = window.innerWidth * dpr;
const height = this.canvas.height = window.innerHeight * dpr;
this.view.setViewport([0, 0, width, height]);
const aspect = width / height;
this.camera.setProjection(Projection.ORTHO, -aspect, aspect, -1, 1, 0, 1);
}
}