Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea9fdbeb5c | ||
|
|
5fd7a4e153 | ||
|
|
20ff230b92 | ||
|
|
44ff79ad34 | ||
|
|
102d2db008 | ||
|
|
5c9039e650 | ||
|
|
1203c24f06 | ||
|
|
9704d27aeb | ||
|
|
c6f2c3fc1c | ||
|
|
2faf868341 | ||
|
|
81f6260843 | ||
|
|
1a9063d53a | ||
|
|
6062b3c8c6 | ||
|
|
d9186c44ba | ||
|
|
6d06e00934 | ||
|
|
b4021a1fbc | ||
|
|
2c9559f9b8 | ||
|
|
e2da13f817 | ||
|
|
addae314b4 | ||
|
|
5e3c51822d | ||
|
|
9d817a0cc9 | ||
|
|
95ecc3d1e4 | ||
|
|
cabaa4323e | ||
|
|
d6b2ec49b0 | ||
|
|
93004aa5bc | ||
|
|
27fa8ab46d |
+9
-1
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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
|
||||
*/
|
||||
|
||||
+1
-1
@@ -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,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
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -182,6 +182,10 @@ bool NoopDriver::isParallelShaderCompileSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>";
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
<< "}";
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Vendored
+29
-4
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
;
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user