Compare commits

..
Author SHA1 Message Date
Benjamin Doherty f172cac8f2 Fix web morphing sample 2023-03-15 13:02:50 -07:00
4249 changed files with 371655 additions and 200011 deletions
+1 -6
View File
@@ -124,16 +124,11 @@ if (LINUX)
add_definitions(-DFILAMENT_SUPPORTS_XCB)
endif()
# Default Swiftshader build does not enable the xlib extension
if (FILAMENT_SUPPORTS_XLIB AND FILAMENT_USE_SWIFTSHADER)
set(FILAMENT_SUPPORTS_XLIB OFF)
endif()
if (FILAMENT_SUPPORTS_XLIB)
add_definitions(-DFILAMENT_SUPPORTS_XLIB)
endif()
if (FILAMENT_SUPPORTS_XCB OR FILAMENT_SUPPORTS_XLIB)
if (FILAMENT_SUPPORTS_XCB OR FILAMENT_SUPORTS_XLIB)
add_definitions(-DFILAMENT_SUPPORTS_X11)
set(FILAMENT_SUPPORTS_X11 TRUE)
endif()
+1 -1
View File
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 The Android Open Source Project
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+3 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.33.0'
implementation 'com.google.android.filament:filament-android:1.32.0'
}
```
@@ -41,6 +41,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
| ------------- | ------------- |
| [![filament-android](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android/badge.svg?subject=filament-android)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android) | The Filament rendering engine itself. |
| [![gltfio-android](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android/badge.svg?subject=gltfio-android)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android) | A glTF 2.0 loader for Filament, depends on `filament-android`. |
| [![gltfio-android-lite](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android-lite/badge.svg?subject=gltfio-android-lite)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android-lite) | Trimmed version of `gltfio` that does not support some glTF extensions. |
| [![filament-utils-android](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-utils-android/badge.svg?subject=filament-utils-android)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-utils-android) | KTX loading, Kotlin math, and camera utilities, depends on `gltfio-android`. |
| [![filamat-android](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android/badge.svg?subject=filamat-android)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android) | A runtime material builder/compiler. This library is large but contains a full shader compiler/validator/optimizer and supports both OpenGL and Vulkan. |
| [![filamat-android-lite](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android-lite/badge.svg?subject=filamat-android-lite)](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android-lite) | A much smaller alternative to `filamat-android` that can only generate OpenGL shaders. It does not provide validation or optimizations. |
@@ -50,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.33.0'
pod 'Filament', '~> 1.32.0'
```
### Snapshots
+58 -13
View File
@@ -13,7 +13,22 @@ Before starting, ensure that each of these branches is up-to-date with origin:
It should have the version corresponding to its name, $RELEASE.
## 1. Bump versions on main to $RELEASE.
## 1. Update RELEASE_NOTES.md on the rc branch.
Checkout the rc/$RELEASE branch. In RELEASE_NOTES.md, locate the header corresponding to $RELEASE
and write release notes. To see which commits make up the release, run:
```
build/common/release.sh -c rc/$RELEASE
```
Commit the changes to rc/$RELEASE with the title:
```
Update RELEASE_NOTES for $RELEASE
```
## 2. Bump versions on main to $RELEASE.
Checkout main and run the following command to bump Filament's version to $RELEASE:
@@ -29,19 +44,49 @@ Release Filament $RELEASE
Do not push to origin yet.
## 2. Update RELEASE_NOTES.md on main.
## 3. Cherry-pick RELEASE_NOTES change from rc branch to main.
Create a new header in RELEASE_NOTES.md for $NEXT_RELEASE. Copy the release notes in
NEW_RELEASE_NOTES.md to RELEASE_NOTES.md under the new header. Clear NEW_RELEASE_NOTES.md.
```
git cherry-pick rc/$RELEASE
```
Amend these changes to the "Release Filament $RELEASE" commit.
Update the headers. The "main branch" header becomes a header for $NEXT_RELEASE, and a new "main
branch" header is added.
For example, this:
```
## main branch
- foo
- bar
## v1.9.3
- baz
- bat
```
becomes:
```
## main branch
## v1.9.4
- foo
- bar
## v1.9.3
- baz
- bat
```
Ammend these changes to the cherry-picked change.
```
git add -u
git commit --amend --no-edit
```
## 3. Run release script.
## 4. Run release script.
```
build/common/release.sh rc/$RELEASE rc/$NEXT_RELEASE
@@ -50,18 +95,18 @@ build/common/release.sh rc/$RELEASE rc/$NEXT_RELEASE
This script will merge rc/$RELEASE into release, delete the rc branch, and create a new rc
branch called rc/$NEXT_RELEASE. Verify that everything looks okay locally.
## 4. Push the release branch.
## 5. Push the release branch.
```
git push origin release
```
## 5. Create the GitHub release.
## 6. Create the GitHub release.
Use the GitHub UI to create a GitHub release corresponding to $RELEASE version.
Make sure the target is set to the release branch.
## 6. Delete the old rc branch (optional).
## 7. Delete the old rc branch (optional).
This step is optional. The old rc branch may be left alive for a few weeks for posterity.
@@ -69,7 +114,7 @@ This step is optional. The old rc branch may be left alive for a few weeks for p
git push origin --delete rc/$RELEASE
```
## 7. Bump the version on the new rc branch to $NEXT_RELEASE.
## 8. Bump the version on the new rc branch to $NEXT_RELEASE.
```
git checkout rc/$NEXT_RELEASE
@@ -82,19 +127,19 @@ Commit the changes to rc/$NEXT_RELEASE with the title:
Bump version to $NEXT_RELEASE
```
## 8. Push main.
## 9. Push main.
```
git push origin main
```
## 9. Push the new rc branch.
## 10. Push the new rc branch.
```
git push origin -u rc/$NEXT_RELEASE
```
## 10. Rebuild the GitHub release (if failed).
## 11. Rebuild the GitHub release (if failed).
Sometimes the GitHub release job will fail. In this case, you can manually re-run the release job.
-28
View File
@@ -7,34 +7,6 @@ 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.33.0
- materials: prepare ES2 support [⚠️ **New Material Version**]
## v1.32.4
- engine: Add support for _constant parameters_, which are constants that can be specialized after material compilation.
- materials: improved size reduction of OpenGL/Metal shaders by ~65% when compiling materials with
size optimizations (`matc -S`) [⚠️ **Recompile Materials**]
- engine: fix potential crash on Metal devices with A8X GPU (iPad Air 2) [⚠️ **Recompile Materials**]
- opengl: support the external image on macOS
## v1.32.3
- fog: added an option to disable the fog after a certain distance [⚠️ **Recompile Materials**].
- fog: fog color now takes exposure and IBL intensity into account [⚠️ **Recompile Materials**].
- materials: implement cascades debugging as a post-process [⚠️ **Recompile Materials**].
- materials: use 9 digits or less for floats [⚠️ **Recompile Materials**].
- gltfio: fix skinning when objects are far from the origin
- materials: remove 4 unneeded variants from `unlit` materials [⚠️ **Recompile Materials**].
## v1.32.2
- lighting: the sun disc was computed in low/medium quality instead of high quality. This will
provide performance improvements to mobile devices [⚠️ **Recompile Materials**]
## v1.32.1
## v1.32.0
- fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**]
-1
View File
@@ -126,7 +126,6 @@ buildscript {
"-fno-asynchronous-unwind-tables",
"-fno-rtti",
"-ffast-math",
"-fno-finite-math-only",
"-ffp-contract=fast",
"-fvisibility-inlines-hidden",
"-fvisibility=hidden",
@@ -315,13 +315,12 @@ Java_com_google_android_filament_View_nSetBloomOptions(JNIEnv*, jclass,
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetFogOptions(JNIEnv *, jclass , jlong nativeView,
jfloat distance, jfloat maximumOpacity, jfloat height, jfloat heightFalloff, jfloat cutOffDistance,
jfloat r, jfloat g, jfloat b, jfloat density, jfloat inScatteringStart,
jfloat distance, jfloat maximumOpacity, jfloat height, jfloat heightFalloff, jfloat r,
jfloat g, jfloat b, jfloat density, jfloat inScatteringStart,
jfloat inScatteringSize, jboolean fogColorFromIbl, jboolean enabled) {
View* view = (View*) nativeView;
View::FogOptions options = {
.distance = distance,
.cutOffDistance = cutOffDistance,
.maximumOpacity = maximumOpacity,
.height = height,
.heightFalloff = heightFalloff,
@@ -356,16 +356,16 @@ public class RenderableManager {
/**
* Specifies the number of draw instance of this renderable. The default is 1 instance and
* the maximum number of instances allowed is 32767. 0 is invalid.
* the maximum number of instances allowed is 65535. 0 is invalid.
* All instances are culled using the same bounding box, so care must be taken to make
* sure all instances render inside the specified bounding box.
* The material can use getInstanceIndex() in the vertex shader to get the instance index and
* possibly adjust the position or transform.
*
* @param instanceCount the number of instances silently clamped between 1 and 32767.
* @param instanceCount the number of instances silently clamped between 1 and 65535.
*/
@NonNull
public Builder instances(@IntRange(from = 1, to = 32767) int instanceCount) {
public Builder instances(@IntRange(from = 1, to = 65535) int instanceCount) {
nBuilderInstances(mNativeBuilder, instanceCount);
return this;
}
@@ -962,8 +962,7 @@ public class View {
assertFloat3In(options.color);
mFogOptions = options;
nSetFogOptions(getNativeObject(), options.distance, options.maximumOpacity, options.height,
options.heightFalloff, options.cutOffDistance,
options.color[0], options.color[1], options.color[2],
options.heightFalloff, options.color[0], options.color[1], options.color[2],
options.density, options.inScatteringStart, options.inScatteringSize,
options.fogColorFromIbl,
options.enabled);
@@ -1158,7 +1157,7 @@ public class View {
private static native void nSetSSCTOptions(long nativeView, float ssctLightConeRad, float ssctStartTraceDistance, float ssctContactDistanceMax, float ssctIntensity, float v, float v1, float v2, float ssctDepthBias, float ssctDepthSlopeBias, int ssctSampleCount, int ssctRayCount, boolean ssctEnabled);
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled, float highlight,
boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold);
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float cutOffDistance, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, boolean enabled);
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, boolean enabled);
private static native void nSetBlendMode(long nativeView, int blendMode);
private static native void nSetDepthOfFieldOptions(long nativeView, float cocScale, float maxApertureDiameter, boolean enabled, int filter,
boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC);
@@ -1393,87 +1392,48 @@ public class View {
}
/**
* Options to control large-scale fog in the scene
* Options to control fog in the scene
*/
public static class FogOptions {
/**
* Distance in world units [m] from the camera to where the fog starts ( >= 0.0 )
* distance in world units from the camera where the fog starts ( >= 0.0 )
*/
public float distance = 0.0f;
/**
* Distance in world units [m] after which the fog calculation is disabled.
* This can be used to exclude the skybox, which is desirable if it already contains clouds or
* fog. The default value is +infinity which applies the fog to everything.
*
* Note: The SkyBox is typically at a distance of 1e19 in world space (depending on the near
* plane distance and projection used though).
*/
public float cutOffDistance = Float.POSITIVE_INFINITY;
/**
* fog's maximum opacity between 0 and 1
*/
public float maximumOpacity = 1.0f;
/**
* Fog's floor in world units [m]. This sets the "sea level".
* fog's floor in world units
*/
public float height = 0.0f;
/**
* How fast the fog dissipates with altitude. heightFalloff has a unit of [1/m].
* It can be expressed as 1/H, where H is the altitude change in world units [m] that causes a
* factor 2.78 (e) change in fog density.
*
* A falloff of 0 means the fog density is constant everywhere and may result is slightly
* faster computations.
* how fast fog dissipates with altitude
*/
public float heightFalloff = 1.0f;
/**
* Fog's color is used for ambient light in-scattering, a good value is
* to use the average of the ambient light, possibly tinted towards blue
* for outdoors environments. Color component's values should be between 0 and 1, values
* above one are allowed but could create a non energy-conservative fog (this is dependant
* on the IBL's intensity as well).
*
* We assume that our fog has no absorption and therefore all the light it scatters out
* becomes ambient light in-scattering and has lost all directionality, i.e.: scattering is
* isotropic. This somewhat simulates Rayleigh scattering.
*
* This value is used as a tint instead, when fogColorFromIbl is enabled.
*
* @see fogColorFromIbl
* fog's color (linear), see fogColorFromIbl
*/
@NonNull @Size(min = 3)
public float[] color = {1.0f, 1.0f, 1.0f};
public float[] color = {0.5f, 0.5f, 0.5f};
/**
* Extinction factor in [1/m] at altitude 'height'. The extinction factor controls how much
* light is absorbed and out-scattered per unit of distance. Each unit of extinction reduces
* the incoming light to 37% of its original value.
*
* Note: The extinction factor is related to the fog density, it's usually some constant K times
* the density at sea level (more specifically at fog height). The constant K depends on
* the composition of the fog/atmosphere.
*
* For historical reason this parameter is called `density`.
* fog's density at altitude given by 'height'
*/
public float density = 0.1f;
/**
* Distance in world units [m] from the camera where the Sun in-scattering starts.
* distance in world units from the camera where in-scattering starts
*/
public float inScatteringStart = 0.0f;
/**
* Very inaccurately simulates the Sun's in-scattering. That is, the light from the sun that
* is scattered (by the fog) towards the camera.
* Size of the Sun in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
* Smaller values result is a larger scattering size.
* size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
*/
public float inScatteringSize = -1.0f;
/**
* The fog color will be sampled from the IBL in the view direction and tinted by `color`.
* Depending on the scene this can produce very convincing results.
* This simulate a more anisotropic phase-function.
* Fog color will be modulated by the IBL color in the view direction.
*/
public boolean fogColorFromIbl = false;
/**
* Enable or disable large-scale fog
* enable or disable fog
*/
public boolean enabled = false;
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.android.filament.utils
package com.google.android.filament.textured
import android.content.res.Resources
import android.graphics.Bitmap
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.33.0
VERSION_NAME=1.32.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+6 -6
View File
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -200,7 +200,7 @@ Energy conservation is one of the key components of a good BRDF for physically b
## Specular BRDF
For the specular term, $f_r$ is a mirror BRDF that can be modeled with the Fresnel law, noted $F$ in the Cook-Torrance approximation of the microfacet model integration:
For the specular term, $f_m$ is a mirror BRDF that can be modeled with the Fresnel law, noted $F$ in the Cook-Torrance approximation of the microfacet model integration:
$$\begin{equation}
f_r(v,l) = \frac{D(h, \alpha) G(v, l, \alpha) F(v, h, f0)}{4(\NoV)(\NoL)}
@@ -485,7 +485,7 @@ void BRDF(...) {
// perceptually linear roughness to roughness (see parameterization)
float roughness = perceptualRoughness * perceptualRoughness;
float D = D_GGX(NoH, roughness);
float D = D_GGX(NoH, a);
vec3 F = F_Schlick(LoH, f0);
float V = V_SmithGGXCorrelated(NoV, NoL, roughness);
@@ -2016,7 +2016,7 @@ I(f(n, v, \alpha)) \equiv \frac{1}{N}\sum_{\forall \, i \in image} f(l_i,
\end{equation}$$
However, in practice we're using _importance sampling_ which needs to take the $pdf$ of the distribution
into account and adds a term $\frac{4\left<\VoH\right>}{D(h_i, \alpha)\left<\NoH\right>}$.
into account and adds a term $\frac{\left<\VoH\right>}{D(h_i, \alpha)\left<\NoH\right>}$.
See Importance Sampling For The IBL section:
$$\begin{equation}\label{iblImportanceSampling}
@@ -3486,15 +3486,15 @@ The _important direction samples_ $l_i$ are calculated as the reflection of $v$
**do not** have the same PDF as $h_i$. The PDF of a transformed distribution is given by:
$$\begin{equation}
p(T_r(x)) = p(x) |J(T_r)|^{-1}
p(T_r(x)) = p(x) |J(T_r)|
\end{equation}$$
Where $|J(T_r)|$ is the determinant of the Jacobian of the transform. In our case we're considering
the transform from $h_i$ to $l_i$ and the determinant of its Jacobian is given in \ref{iblPDF}.
$$\begin{equation}\label{iblPDF}
p(l,v,\Theta) = D(h,\alpha) \left< \NoH \right> |J_{h \rightarrow l}|^{-1} \\
|J_{h \rightarrow l}| = 4 \left< \VoH \right>
p(l,v,\Theta) = D(h,\alpha) \left< \NoH \right> |J_{h \rightarrow l}| \\
|J_{h \rightarrow l}| = \frac{1}{4 \left< \VoH \right>}
\end{equation}$$
### Choosing important directions
+77 -169
View File
File diff suppressed because one or more lines are too long
+1 -58
View File
@@ -1076,63 +1076,6 @@ fragment {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### General: constants
Type
: array of constant objects
Value
: Each entry is an object with the properties `name` and `type`, both of `string` type. The name
must be a valid GLSL identifier. Entries also have an optional `default`, which can either be a
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
described in table [materialConstantsTypes].
Type | Description | Default
:----------------------|:-----------------------------------------|:------------------
int | A signed, 32 bit GLSL int | 0
float | A single-precision GLSL float | 0.0
bool | A GLSL bool | false
[Table [materialConstantsTypes]: Material constants types]
Description
: Lists the constant parameters accepted by your material. These constants can be set, or
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
the same material package with differing constant parameter specializations. Once a material is
loaded from a material package, its constant parameters cannot be changed. Compared to regular
parameters, constant parameters allow the compiler to generate more efficient code. Access
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
a constant parameter named `myConstant` is accessed in the shader as
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
used.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
constants : [
{
name : overrideAlpha,
type : bool
},
{
name : customAlpha,
type : float,
default : 0.5
}
],
shadingModel : lit,
blending : transparent,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
if (materialConstants_overrideAlpha) {
material.baseColor.a = materialConstants_customAlpha;
material.baseColor.rgb *= material.baseColor.a;
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### General: variantFilter
Type
@@ -2363,7 +2306,7 @@ type aliases:
**getUserWorldFromWorldMatrix()** | float4x4 | Matrix that converts from world space to API-level (user) world space.
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
**getUserTime()** | float4 | Current time in seconds: `time`, `(double)time - time`, `0`, `0`
**getUserTimeMod(float m)** | float | Current time modulo m in seconds
**getUserTimeMode(float m)** | float | Current time modulo m in seconds
**getExposure()** | float | Photometric exposure of the camera
**getEV100()** | float | [Exposure value at ISO 100](https://en.wikipedia.org/wiki/Exposure_value) of the camera
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-2
View File
@@ -212,7 +212,6 @@ set(MATERIAL_SRCS
src/materials/colorGrading/colorGrading.mat
src/materials/colorGrading/colorGradingAsSubpass.mat
src/materials/colorGrading/customResolveAsSubpass.mat
src/materials/debugShadowCascades.mat
src/materials/defaultMaterial.mat
src/materials/dof/dof.mat
src/materials/dof/dofCoc.mat
@@ -565,7 +564,6 @@ elseif(WEBGL)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
# TODO: aggressive vectorization is currently broken on Android
# -fslp-vectorize-aggressive
+3 -4
View File
@@ -96,7 +96,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
elseif (APPLE)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
elseif (WEBGL)
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
elseif (LINUX)
@@ -175,19 +174,20 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp
src/vulkan/VulkanHandles.h
src/vulkan/VulkanImageUtility.cpp
src/vulkan/VulkanImageUtility.h
src/vulkan/VulkanMemory.h
src/vulkan/VulkanMemory.cpp
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPlatform.cpp
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp
src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanTaskHandler.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanUtility.cpp
@@ -335,7 +335,6 @@ elseif(WEBGL)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
# TODO: aggressive vectorization is currently broken on Android
# -fslp-vectorize-aggressive
+1 -13
View File
@@ -279,15 +279,6 @@ enum class UniformType : uint8_t {
STRUCT
};
/**
* Supported constant parameter types
*/
enum class ConstantType : uint8_t {
INT,
FLOAT,
BOOL
};
enum class Precision : uint8_t {
LOW,
MEDIUM,
@@ -1126,10 +1117,7 @@ enum class Workaround : uint16_t {
// the whole render pass.
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
A8X_STATIC_TEXTURE_TARGET_ERROR
ADRENO_UNIFORM_ARRAY_CRASH
};
} // namespace filament::backend
@@ -57,10 +57,6 @@ protected:
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImage(ExternalTexture* texture) noexcept override;
void retainExternalImage(void* externalImage) noexcept override;
bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override;
private:
PlatformCocoaGLImpl* pImpl = nullptr;
@@ -73,10 +73,6 @@ struct MetalContext {
uint8_t mac;
} highestSupportedGpuFamily;
struct {
bool a8xStaticTextureTargetError;
} bugs;
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
std::array<uint8_t, MAX_SAMPLE_COUNT + 1> sampleCountLookup;
@@ -117,9 +117,6 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
}
mContext->bugs.a8xStaticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"];
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
mContext->pipelineStateCache.setDevice(mContext->device);
mContext->depthStencilStateCache.setDevice(mContext->device);
@@ -722,8 +719,6 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return true;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.a8xStaticTextureTargetError;
}
return false;
}
@@ -1720,8 +1720,6 @@ bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
return mContext.bugs.allow_read_only_ancillary_feedback_loop;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return mContext.bugs.enable_initialize_non_used_uniform_array;
default:
return false;
}
return false;
}
+7 -19
View File
@@ -40,37 +40,25 @@ static void logCompilationError(utils::io::ostream& out,
static void logProgramLinkError(utils::io::ostream& out,
const char* name, GLuint program) noexcept;
static inline std::string to_string(bool b) noexcept {
return b ? "true" : "false";
}
static inline std::string to_string(int i) noexcept {
return std::to_string(i);
}
static inline std::string to_string(float f) noexcept {
return "float(" + std::to_string(f) + ")";
}
OpenGLProgram::OpenGLProgram() noexcept
: mInitialized(false), mValid(true), mLazyInitializationData(nullptr) {
}
OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
: HwProgram(std::move(program.getName())),
OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& programBuilder) noexcept
: HwProgram(std::move(programBuilder.getName())),
mInitialized(false), mValid(true),
mLazyInitializationData{ new(LazyInitializationData) } {
OpenGLContext& context = gld.getContext();
mLazyInitializationData->uniformBlockInfo = std::move(program.getUniformBlockBindings());
mLazyInitializationData->samplerGroupInfo = std::move(program.getSamplerGroupInfo());
mLazyInitializationData->uniformBlockInfo = std::move(programBuilder.getUniformBlockBindings());
mLazyInitializationData->samplerGroupInfo = std::move(programBuilder.getSamplerGroupInfo());
// this cannot fail because we check compilation status after linking the program
// shaders[] is filled with id of shader stages present.
OpenGLProgram::compileShaders(context,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
std::move(programBuilder.getShadersSource()),
programBuilder.getSpecializationConstants(),
gl.shaders,
mLazyInitializationData->shaderSourceCode);
@@ -121,7 +109,7 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
for (auto const& sc : specializationConstants) {
specializationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
specializationConstantString += std::visit([](auto&& arg) {
return to_string(arg);
return std::to_string(arg);
}, sc.value);
specializationConstantString += '\n';
}
+1 -1
View File
@@ -38,7 +38,7 @@ class OpenGLProgram : public HwProgram {
public:
OpenGLProgram() noexcept;
OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept;
OpenGLProgram(OpenGLDriver& gld, Program&& builder) noexcept;
~OpenGLProgram() noexcept;
bool isValid() const noexcept { return mValid; }
@@ -1,90 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FILAMENT_DRIVER_OPENGL_COCOA_EXTERNAL_IMAGE
#define FILAMENT_DRIVER_OPENGL_COCOA_EXTERNAL_IMAGE
#include <backend/platforms/OpenGLPlatform.h>
#include <CoreVideo/CoreVideo.h>
#include "../gl_headers.h"
namespace filament::backend {
class CocoaExternalImage final : public OpenGLPlatform::ExternalTexture {
public:
/**
* GL objects that can be shared across multiple instances of CocoaExternalImage.
*/
class SharedGl {
public:
SharedGl() noexcept;
~SharedGl() noexcept;
SharedGl(const SharedGl&) = delete;
SharedGl& operator=(const SharedGl&) = delete;
GLuint program = 0;
GLuint sampler = 0;
GLuint fragmentShader = 0;
GLuint vertexShader = 0;
};
CocoaExternalImage(const CVOpenGLTextureCacheRef textureCache,
const SharedGl& sharedGl) noexcept;
~CocoaExternalImage() noexcept;
/**
* Set this external image to the passed-in CVPixelBuffer.
* Afterwards, calling glGetTexture returns the GL texture name backed by the CVPixelBuffer.
*/
bool set(CVPixelBufferRef p) noexcept;
GLuint getGlTexture() const noexcept;
GLuint getInternalFormat() const noexcept;
GLuint getTarget() const noexcept;
private:
void release() noexcept;
CVOpenGLTextureRef createTextureFromImage(CVPixelBufferRef image) noexcept;
GLuint encodeCopyRectangleToTexture2D(GLuint rectangle, size_t width, size_t height) noexcept;
class State {
public:
void save() noexcept;
void restore() noexcept;
private:
GLint activeTexture = 0;
GLint textureBinding = { 0 };
GLint samplerBinding = { 0 };
GLint framebuffer = 0;
GLint viewport[4] = { 0 };
GLint vertexAttrib = 0;
} mState;
GLuint mFBO = 0;
const SharedGl& mSharedGl;
GLuint mRgbaTexture = 0;
const CVOpenGLTextureCacheRef mTextureCache;
CVPixelBufferRef mImage = nullptr;
CVOpenGLTextureRef mTexture = nullptr;
};
} // namespace filament::backend
#endif
@@ -1,235 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define COREVIDEO_SILENCE_GL_DEPRECATION
#include "CocoaExternalImage.h"
#include <utils/Panic.h>
#include "../GLUtils.h"
namespace filament::backend {
static const char *s_vertex = R"SHADER(#version 410 core
void main() {
float x = -1.0 + float(((gl_VertexID & 1) <<2));
float y = -1.0 + float(((gl_VertexID & 2) <<1));
gl_Position=vec4(x, y, 0.0, 1.0);
}
)SHADER";
static const char *s_fragment = R"SHADER(#version 410 core
precision mediump float;
uniform sampler2DRect rectangle;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = texture(rectangle, gl_FragCoord.xy);
}
)SHADER";
CocoaExternalImage::SharedGl::SharedGl() noexcept {
glGenSamplers(1, &sampler);
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
GLint status;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &s_vertex, nullptr);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
assert_invariant(status == GL_TRUE);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &s_fragment, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
assert_invariant(status == GL_TRUE);
program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &status);
assert_invariant(status == GL_TRUE);
// Save current program state.
GLint currentProgram;
glGetIntegerv(GL_CURRENT_PROGRAM, &currentProgram);
glUseProgram(program);
GLint samplerLoc = glGetUniformLocation(program, "rectangle");
glUniform1i(samplerLoc, 0);
// Restore state.
glUseProgram(currentProgram);
}
CocoaExternalImage::SharedGl::~SharedGl() noexcept {
glDeleteSamplers(1, &sampler);
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteProgram(program);
}
CocoaExternalImage::CocoaExternalImage(const CVOpenGLTextureCacheRef textureCache,
const SharedGl &sharedGl) noexcept : mSharedGl(sharedGl), mTextureCache(textureCache) {
glGenFramebuffers(1, &mFBO);
CHECK_GL_ERROR(utils::slog.e)
}
CocoaExternalImage::~CocoaExternalImage() noexcept {
glDeleteFramebuffers(1, &mFBO);
release();
}
bool CocoaExternalImage::set(CVPixelBufferRef image) noexcept {
// Release references to a previous external image, if we're holding any.
release();
if (!image) {
return false;
}
OSType formatType = CVPixelBufferGetPixelFormatType(image);
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA,
"macOS external images must be 32BGRA format.");
// The pixel buffer must be locked whenever we do rendering with it. We'll unlock it before
// releasing.
UTILS_UNUSED_IN_RELEASE CVReturn lockStatus = CVPixelBufferLockBaseAddress(image, 0);
assert_invariant(lockStatus == kCVReturnSuccess);
mImage = image;
mTexture = createTextureFromImage(image);
mRgbaTexture = encodeCopyRectangleToTexture2D(CVOpenGLTextureGetName(mTexture),
CVPixelBufferGetWidth(image), CVPixelBufferGetHeight(image));
CHECK_GL_ERROR(utils::slog.e)
return true;
}
GLuint CocoaExternalImage::getGlTexture() const noexcept {
return mRgbaTexture;
}
GLuint CocoaExternalImage::getInternalFormat() const noexcept {
if (mRgbaTexture) {
return GL_RGBA8;
}
return 0;
}
GLuint CocoaExternalImage::getTarget() const noexcept {
if (mRgbaTexture) {
return GL_TEXTURE_2D;
}
return 0;
}
void CocoaExternalImage::release() noexcept {
if (mImage) {
CVPixelBufferUnlockBaseAddress(mImage, 0);
CVPixelBufferRelease(mImage);
}
if (mTexture) {
CFRelease(mTexture);
}
if (mRgbaTexture) {
glDeleteTextures(1, &mRgbaTexture);
mRgbaTexture = 0;
}
}
CVOpenGLTextureRef CocoaExternalImage::createTextureFromImage(CVPixelBufferRef image) noexcept {
CVOpenGLTextureRef texture = nullptr;
UTILS_UNUSED_IN_RELEASE CVReturn success =
CVOpenGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
mTextureCache, image, nil, &texture);
assert_invariant(success == kCVReturnSuccess);
return texture;
}
GLuint CocoaExternalImage::encodeCopyRectangleToTexture2D(GLuint rectangle,
size_t width, size_t height) noexcept {
GLuint texture;
glGenTextures(1, &texture);
mState.save();
// Create a texture to hold the result of the blit image.
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
CHECK_GL_ERROR(utils::slog.e)
// source textures
glBindSampler(0, mSharedGl.sampler);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, rectangle);
CHECK_GL_ERROR(utils::slog.e)
// destination texture
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
CHECK_GL_ERROR(utils::slog.e)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
CHECK_GL_ERROR(utils::slog.e)
// draw
glViewport(0, 0, width, height);
CHECK_GL_ERROR(utils::slog.e)
glUseProgram(mSharedGl.program);
CHECK_GL_ERROR(utils::slog.e)
glDisableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 3);
CHECK_GL_ERROR(utils::slog.e)
mState.restore();
CHECK_GL_ERROR(utils::slog.e)
return texture;
}
void CocoaExternalImage::State::save() noexcept {
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
glGetIntegerv(GL_SAMPLER_BINDING, &samplerBinding);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &framebuffer);
glGetIntegerv(GL_VIEWPORT, viewport);
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttrib);
}
void CocoaExternalImage::State::restore() noexcept {
glActiveTexture(activeTexture);
glBindTexture(GL_TEXTURE_2D, textureBinding);
glBindSampler(0, samplerBinding);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
if (vertexAttrib) {
glEnableVertexAttribArray(0);
}
}
} // namespace filament::backend
@@ -28,7 +28,6 @@
#include <Cocoa/Cocoa.h>
#include <vector>
#include "CocoaExternalImage.h"
namespace filament::backend {
@@ -50,8 +49,6 @@ struct PlatformCocoaGLImpl {
NSOpenGLContext* mGLContext = nullptr;
CocoaGLSwapChain* mCurrentSwapChain = nullptr;
std::vector<NSView*> mHeadlessSwapChains;
CVOpenGLTextureCacheRef mTextureCache = nullptr;
std::unique_ptr<CocoaExternalImage::SharedGl> mExternalImageSharedGl;
void updateOpenGLContext(NSView *nsView, bool resetView, bool clearView);
};
@@ -162,12 +159,6 @@ Driver* PlatformCocoaGL::createDriver(void* sharedContext, const Platform::Drive
int result = bluegl::bind();
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
UTILS_UNUSED_IN_RELEASE CVReturn success = CVOpenGLTextureCacheCreate(kCFAllocatorDefault, nullptr,
[pImpl->mGLContext CGLContextObj], [pImpl->mGLContext.pixelFormat CGLPixelFormatObj], nullptr,
&pImpl->mTextureCache);
assert_invariant(success == kCVReturnSuccess);
return OpenGLPlatform::createDefaultDriver(this, sharedContext, driverConfig);
}
@@ -176,8 +167,6 @@ int PlatformCocoaGL::getOSVersion() const noexcept {
}
void PlatformCocoaGL::terminate() noexcept {
CFRelease(pImpl->mTextureCache);
pImpl->mExternalImageSharedGl.reset();
pImpl->mGLContext = nil;
bluegl::unbind();
}
@@ -267,9 +256,6 @@ void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
void PlatformCocoaGL::commit(Platform::SwapChain* swapChain) noexcept {
[pImpl->mGLContext flushBuffer];
// This needs to be done periodically.
CVOpenGLTextureCacheFlush(pImpl->mTextureCache, 0);
}
bool PlatformCocoaGL::pumpEvents() noexcept {
@@ -280,45 +266,6 @@ bool PlatformCocoaGL::pumpEvents() noexcept {
return true;
}
OpenGLPlatform::ExternalTexture* PlatformCocoaGL::createExternalImageTexture() noexcept {
if (!pImpl->mExternalImageSharedGl) {
pImpl->mExternalImageSharedGl = std::make_unique<CocoaExternalImage::SharedGl>();
}
ExternalTexture* outTexture = new CocoaExternalImage(pImpl->mTextureCache,
*pImpl->mExternalImageSharedGl);
// the actual id/target will be set in setExternalImage.
outTexture->id = 0;
outTexture->target = GL_TEXTURE_2D;
return outTexture;
}
void PlatformCocoaGL::destroyExternalImage(ExternalTexture* texture) noexcept {
auto* p = static_cast<CocoaExternalImage*>(texture);
delete p;
}
void PlatformCocoaGL::retainExternalImage(void* externalImage) noexcept {
// Take ownership of the passed in buffer. It will be released the next time
// setExternalImage is called, or when the texture is destroyed.
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) externalImage;
CVPixelBufferRetain(pixelBuffer);
}
bool PlatformCocoaGL::setExternalImage(void* externalImage, ExternalTexture* texture) noexcept {
CVPixelBufferRef cvPixelBuffer = (CVPixelBufferRef) externalImage;
CocoaExternalImage* cocoaExternalImage = static_cast<CocoaExternalImage*>(texture);
if (!cocoaExternalImage->set(cvPixelBuffer)) {
return false;
}
texture->target = cocoaExternalImage->getTarget();
texture->id = cocoaExternalImage->getGlTexture();
// we used to set the internalFormat, but it's not used anywhere on the gl backend side
// cocoaExternalImage->getInternalFormat();
return true;
}
void PlatformCocoaGLImpl::updateOpenGLContext(NSView *nsView, bool resetView,
bool clearView) {
NSOpenGLContext* glContext = mGLContext;
@@ -99,6 +99,12 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
0, 0, 0
};
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
0
};
HGLRC tempContext = NULL;
mHWnd = CreateWindowA("STATIC", "dummy", 0, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
@@ -121,20 +127,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
wglCreateContextAttribs =
(PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
// try all versions down, from GL 4.5 to 4.1
for (int minor = 5; minor >= 1; minor--) {
int const attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, minor,
0
};
mContext = wglCreateContextAttribs(whdc, (HGLRC)sharedGLContext, attribs);
if (mContext) {
break;
}
}
mContext = wglCreateContextAttribs(whdc, (HGLRC) sharedGLContext, attribs);
if (!mContext) {
utils::slog.e << "wglCreateContextAttribs() failed, whdc=" << whdc << utils::io::endl;
goto error;
+88 -81
View File
@@ -32,84 +32,11 @@ using namespace utils;
namespace filament::backend {
using ImgUtil = VulkanImageUtility;
namespace {
inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
const VkImageBlit blitRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1},
.srcOffsets = {srcRect[0], srcRect[1]},
.dstSubresource = {aspect, dst.level, dst.layer, 1},
.dstOffsets = {dstRect[0], dstRect[1]}}};
const VkImageResolve resolveRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1},
.srcOffset = srcRect[0],
.dstSubresource = {aspect, dst.level, dst.layer, 1},
.dstOffset = dstRect[0],
.extent = {srcExtent.width, srcExtent.height, 1}}};
const VkImageSubresourceRange srcRange = {
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
};
const VkImageSubresourceRange dstRange = {
.aspectMask = aspect,
.baseMipLevel = dst.level,
.levelCount = 1,
.baseArrayLayer = dst.layer,
.layerCount = 1,
};
if constexpr (FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< "layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
<< "layout=" << dst.getLayout() << utils::io::endl;
}
src.texture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST);
if (src.texture->samples > 1 && dst.texture->samples == 1) {
assert_invariant(
aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported.");
vkCmdResolveImage(cmdbuffer,
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer,
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
1, blitRegions, filter);
}
VulkanLayout newSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage);
VulkanLayout const newDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage);
// In the case of blitting the depth attachment, we transition the source into GENERAL (for
// sampling) and set the copy as ATTACHMENT_OPTIMAL (to be set as the attachment).
if (any(src.texture->usage & TextureUsage::DEPTH_ATTACHMENT)) {
newSrcLayout = VulkanLayout::DEPTH_SAMPLER;
}
src.texture->transitionLayout(cmdbuffer, srcRange, newSrcLayout);
dst.texture->transitionLayout(cmdbuffer, dstRange, newDstLayout);
}
struct BlitterUniforms {
int sampleCount;
float inverseSampleCount;
};
}// anonymous namespace
void VulkanBlitter::blitColor(BlitArgs args) {
const VulkanAttachment src = args.srcTarget->getColor(args.targetIndex);
const VulkanAttachment dst = args.dstTarget->getColor(0);
@@ -129,8 +56,8 @@ void VulkanBlitter::blitColor(BlitArgs args) {
return;
}
#endif
VkCommandBuffer const cmdbuffer = mContext.commands->get().cmdbuffer;
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst,
blitFast(aspect, args.filter, args.srcTarget->getExtent(), src, dst,
args.srcRectPair, args.dstRectPair);
}
@@ -161,11 +88,90 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
args.dstRectPair);
return;
}
VkCommandBuffer const cmdbuffer = mContext.commands->get().cmdbuffer;
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
blitFast(aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
args.dstRectPair);
}
void VulkanBlitter::blitFast(VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffsets = { srcRect[0], srcRect[1] },
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffsets = { dstRect[0], dstRect[1] }
}};
const VkImageResolve resolveRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffset = srcRect[0],
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffset = dstRect[0],
.extent = { srcExtent.width, srcExtent.height, 1 }
}};
const VkImageSubresourceRange srcRange = {
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
};
const VkImageSubresourceRange dstRange = {
.aspectMask = aspect,
.baseMipLevel = dst.level,
.levelCount = 1,
.baseArrayLayer = dst.layer,
.layerCount = 1,
};
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
const VkImageLayout srcLayout = getDefaultImageLayout(src.texture->usage);
transitionImageLayout(cmdbuffer, {
src.getImage(),
srcLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT
});
transitionImageLayout(cmdbuffer, {
dst.getImage(),
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
});
if (src.texture->samples > 1 && dst.texture->samples == 1) {
assert_invariant(aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported.");
vkCmdResolveImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, filter);
}
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
.image = src.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = srcLayout,
.subresources = srcRange
}));
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
.image = dst.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = getDefaultImageLayout(dst.texture->usage),
.subresources = dstRange,
}));
}
void VulkanBlitter::shutdown() noexcept {
if (mContext.device) {
@@ -261,11 +267,12 @@ void VulkanBlitter::blitSlowDepth(VkImageAspectFlags aspect, VkFilter filter,
// BEGIN RENDER PASS
// -----------------
VulkanLayout const layout = VulkanLayout::DEPTH_ATTACHMENT;
const VulkanDepthLayout layout =
fromVkImageLayout(getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT));
const VulkanFboCache::RenderPassKey rpkey = {
.initialColorLayoutMask = 0,
.initialDepthLayout = VulkanLayout::UNDEFINED,
.initialDepthLayout = VulkanDepthLayout::UNDEFINED,
.renderPassDepthLayout = layout,
.finalDepthLayout = layout,
.depthFormat = dst.getFormat(),
@@ -359,14 +366,14 @@ void VulkanBlitter::blitSlowDepth(VkImageAspectFlags aspect, VkFilter filter,
sampler = {
.sampler = vksampler,
.imageView = mContext.emptyTexture->getPrimaryImageView(),
.imageLayout = ImgUtil::getVkLayout(VulkanLayout::READ_WRITE),
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
};
}
samplers[0] = {
.sampler = vksampler,
.imageView = src.getImageView(VK_IMAGE_ASPECT_DEPTH_BIT),
.imageLayout = ImgUtil::getVkLayout(VulkanLayout::DEPTH_ATTACHMENT),
.imageLayout = getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT)
};
mPipelineCache.bindSamplers(samplers,
@@ -53,6 +53,10 @@ public:
private:
void lazyInit() noexcept;
void blitFast(VkImageAspectFlags aspect, VkFilter filter, const VkExtent2D srcExtent,
VulkanAttachment src, VulkanAttachment dst, const VkOffset3D srcRect[2],
const VkOffset3D dstRect[2]);
void blitSlowDepth(VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]);
+42 -28
View File
@@ -69,38 +69,52 @@ void VulkanBuffer::loadFromCpu(VulkanContext& context, VulkanStagePool& stagePoo
// Firstly, ensure that the copy finishes before the next draw call.
// Secondly, in case the user decides to upload another chunk (without ever using the first one)
// we need to ensure that this upload completes first (hence
// dstStageMask=VK_PIPELINE_STAGE_TRANSFER_BIT).
VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
// we need to ensure that this upload completes first.
if (mUsage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) {
dstAccessMask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (mUsage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) {
dstAccessMask |= VK_ACCESS_INDEX_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
dstAccessMask |= VK_ACCESS_UNIFORM_READ_BIT;
// NOTE: ideally dstStageMask would include VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, but
// this seems to be insufficient on Mali devices. To work around this we are using a more
// aggressive ALL_GRAPHICS_BIT barrier.
dstStageMask |= VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
} else if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
// TODO: implement me
VkBufferMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT |
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = mGpuBuffer,
.size = VK_WHOLE_SIZE
};
vkCmdPipelineBarrier(cmdbuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
0, 0, nullptr, 1, &barrier, 0, nullptr);
}
VkBufferMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = dstAccessMask,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = mGpuBuffer,
.size = VK_WHOLE_SIZE,
};
if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
// NOTE: ideally dstStageMask would include VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, but this
// seems to be insufficient on Mali devices. To work around this we are using a more
// aggressive ALL_GRAPHICS_BIT barrier.
vkCmdPipelineBarrier(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 0, nullptr, 1,
&barrier, 0, nullptr);
VkBufferMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_UNIFORM_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = mGpuBuffer,
.size = VK_WHOLE_SIZE
};
vkCmdPipelineBarrier(cmdbuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
0, 0, nullptr, 1, &barrier, 0, nullptr);
}
if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
// TODO: implement me
}
}
} // namespace filament::backend
@@ -24,8 +24,6 @@
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <atomic>
namespace filament::backend {
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
+21 -82
View File
@@ -236,8 +236,8 @@ VkInstance createInstance(const char* const* ppRequiredExtensions, uint32_t requ
if (!enabledLayers.empty()) {
// If layers are supported, Check if VK_EXT_validation_features is supported.
const FixedCapacityVector<VkExtensionProperties> availableExts =
filament::backend::enumerate(vkEnumerateInstanceExtensionProperties,
"VK_LAYER_KHRONOS_validation");
filament::backend::enumerate(vkEnumerateInstanceExtensionProperties,
"VK_LAYER_KHRONOS_validation");
for (const auto& extProps : availableExts) {
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
validationFeaturesSupported = true;
@@ -423,44 +423,15 @@ FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropert
return queueFamiliesProperties;
}
// Provide a preference ordering of device types.
// Enum based on:
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceType.html
inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
switch (deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return 5;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return 4;
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return 3;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return 2;
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return 1;
default:
utils::slog.w << "devcieTypeOrder: Unexpected deviceType: " << deviceType
<< utils::io::endl;
return -1;
}
}
VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
FixedCapacityVector<VkPhysicalDevice> const physicalDevices
= filament::backend::enumerate(vkEnumeratePhysicalDevices, instance);
struct DeviceInfo {
VkPhysicalDevice device = VK_NULL_HANDLE;
VkPhysicalDeviceType deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER;
};
FixedCapacityVector<DeviceInfo> deviceList(physicalDevices.size());
for (size_t deviceInd = 0; deviceInd < physicalDevices.size(); ++deviceInd) {
auto const candidateDevice = physicalDevices[deviceInd];
const FixedCapacityVector<VkPhysicalDevice> physicalDevices = filament::backend::enumerate(
vkEnumeratePhysicalDevices, instance);
for (const auto& candidateDevice : physicalDevices) {
VkPhysicalDeviceProperties targetDeviceProperties;
vkGetPhysicalDeviceProperties(candidateDevice, &targetDeviceProperties);
int const major = VK_VERSION_MAJOR(targetDeviceProperties.apiVersion);
int const minor = VK_VERSION_MINOR(targetDeviceProperties.apiVersion);
const int major = VK_VERSION_MAJOR(targetDeviceProperties.apiVersion);
const int minor = VK_VERSION_MINOR(targetDeviceProperties.apiVersion);
// Does the device support the required Vulkan level?
if (major < VK_REQUIRED_VERSION_MAJOR) {
@@ -473,10 +444,10 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
// Does the device have any command queues that support graphics?
// In theory, we should also ensure that the device supports presentation of our
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
FixedCapacityVector<VkQueueFamilyProperties> const queueFamiliesProperties
= getPhysicalDeviceQueueFamilyPropertiesHelper(candidateDevice);
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties =
getPhysicalDeviceQueueFamilyPropertiesHelper(candidateDevice);
bool foundGraphicsQueue = false;
for (auto const& props: queueFamiliesProperties) {
for (const auto& props : queueFamiliesProperties) {
if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
foundGraphicsQueue = true;
break;
@@ -487,11 +458,11 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
}
// Does the device support the VK_KHR_swapchain extension?
FixedCapacityVector<VkExtensionProperties> const extensions
= filament::backend::enumerate(vkEnumerateDeviceExtensionProperties,
candidateDevice, static_cast<char const*>(nullptr) /* pLayerName */);
const FixedCapacityVector<VkExtensionProperties> extensions = filament::backend::enumerate(
vkEnumerateDeviceExtensionProperties, candidateDevice,
static_cast<const char*>(nullptr) /* pLayerName */);
bool supportsSwapchain = false;
for (auto const& extension: extensions) {
for (const auto& extension : extensions) {
if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
supportsSwapchain = true;
break;
@@ -500,24 +471,12 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
if (!supportsSwapchain) {
continue;
}
deviceList[deviceInd].device = candidateDevice;
deviceList[deviceInd].deviceType = targetDeviceProperties.deviceType;
return candidateDevice;
}
// Sort the found devices
std::sort(deviceList.begin(), deviceList.end(), [](DeviceInfo const& a, DeviceInfo const& b) {
if (a.device == VK_NULL_HANDLE) {
return true;
}
if (b.device == VK_NULL_HANDLE) {
return false;
}
return deviceTypeOrder(a.deviceType) <= deviceTypeOrder(b.deviceType);
});
auto device = deviceList.back().device;
ASSERT_POSTCONDITION(device != VK_NULL_HANDLE, "Unable to find suitable device.");
return device;
PANIC_POSTCONDITION("Unable to find suitable device.");
return VK_NULL_HANDLE;
}
VkFormat findSupportedFormat(VkPhysicalDevice device, utils::Slice<VkFormat> candidates,
@@ -548,8 +507,8 @@ VkFormat VulkanAttachment::getFormat() const {
return texture ? texture->getVkFormat() : VK_FORMAT_UNDEFINED;
}
VulkanLayout VulkanAttachment::getLayout() const {
return texture ? texture->getLayout(layer, level) : VulkanLayout::UNDEFINED;
VkImageLayout VulkanAttachment::getLayout() const {
return texture ? texture->getVkLayout(layer, level) : VK_IMAGE_LAYOUT_UNDEFINED;
}
VkExtent2D VulkanAttachment::getExtent2D() const {
@@ -559,27 +518,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) const {
assert_invariant(texture);
return texture->getAttachmentView(getSubresourceRange(aspect));
}
VkImageSubresourceRange VulkanAttachment::getSubresourceRange(VkImageAspectFlags aspect) const {
assert_invariant(texture);
uint32_t levelCount = 1;
uint32_t layerCount = 1;
// For depth attachments, we consider all the subresource range since layout transitions of
// depth and stencil attachments should always be carried out for all subresources.
if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) {
auto range = texture->getPrimaryRange();
levelCount = range.levelCount;
layerCount = range.layerCount;
}
return {
.aspectMask = aspect,
.baseMipLevel = uint32_t(level),
.levelCount = levelCount,
.baseArrayLayer = uint32_t(layer),
.layerCount = layerCount,
};
return texture->getAttachmentView(level, layer, aspect);
}
void VulkanContext::initialize(const char* const* ppRequiredExtensions,
+1 -4
View File
@@ -20,7 +20,6 @@
#include "VulkanPipelineCache.h"
#include "VulkanCommands.h"
#include "VulkanConstants.h"
#include "VulkanImageUtility.h"
#include <utils/bitset.h>
#include <utils/Slice.h>
@@ -42,11 +41,9 @@ struct VulkanAttachment {
uint16_t layer = 0;
VkImage getImage() const;
VkFormat getFormat() const;
VulkanLayout getLayout() const;
VkImageLayout getLayout() const;
VkExtent2D getExtent2D() const;
VkImageView getImageView(VkImageAspectFlags aspect) const;
// TODO: maybe embed aspect into the attachment or texture itself.
VkImageSubresourceRange getSubresourceRange(VkImageAspectFlags aspect) const;
};
struct VulkanTimestamps {
+73 -252
View File
@@ -22,7 +22,6 @@
#include "VulkanCommands.h"
#include "VulkanDriverFactory.h"
#include "VulkanHandles.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -35,11 +34,6 @@
#include <set>
#endif
#if FILAMENT_VULKAN_VERBOSE
#include <stack>
static std::stack<std::string> renderPassMarkers;
#endif
using namespace bluevk;
using utils::FixedCapacityVector;
@@ -52,8 +46,6 @@ using utils::FixedCapacityVector;
namespace filament::backend {
using ImgUtil = VulkanImageUtility;
Driver* VulkanDriverFactory::create(VulkanPlatform* const platform,
const char* const* ppRequiredExtensions, uint32_t requiredExtensionCount, const Platform::DriverConfig& driverConfig) noexcept {
return VulkanDriver::create(platform, ppRequiredExtensions, requiredExtensionCount, driverConfig);
@@ -83,6 +75,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform,
mContext.commands->setObserver(&mPipelineCache);
mPipelineCache.setDevice(mContext.device, mContext.allocator);
mPipelineCache.setDummyTexture(mContext.emptyTexture->getPrimaryImageView());
mReadPixels.initialize(mContext.device);
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -142,6 +136,9 @@ void VulkanDriver::terminate() {
void VulkanDriver::tick(int) {
mContext.commands->updateFences();
// Handle any posted tasks
runTaskHandler();
}
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
@@ -660,30 +657,9 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
// early exit condition is flattened in EASU code
return deviceProperties.vendorID == 0x5143; // Qualcomm
case Workaround::ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP:
// Supporting depth attachment as both sampler and attachment is only possible if we set
// the depth attachment as read-only (e.g. during SSAO pass), however note that the
// store-ops for attachments wrt VkRenderPass only has VK_ATTACHMENT_STORE_OP_DONT_CARE
// and VK_ATTACHMENT_STORE_OP_STORE for versions below 1.3. Only at 1.3 and above do we
// have a true read-only choice VK_ATTACHMENT_STORE_OP_NONE. That means for < 1.3, we
// will trigger a validation sync error if we use the depth attachment also as a
// sampler. See full error below:
//
// SYNC-HAZARD-WRITE-AFTER-READ(ERROR / SPEC): msgNum: 929810911 - Validation Error:
// [ SYNC-HAZARD-WRITE-AFTER-READ ] Object 0: handle = 0x6160000c3680,
// type = VK_OBJECT_TYPE_RENDER_PASS; | MessageID = 0x376bc9df | vkCmdEndRenderPass:
// Hazard WRITE_AFTER_READ in subpass 0 for attachment 1 depth aspect during store with
// storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage:
// SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage:
// SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE,
// command: vkCmdDrawIndexed, seq_no: 177, reset_no: 1)
//
// Therefore we apply the existing workaround of an extra blit until a better
// resolution.
return false;
return true;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
default:
return false;
}
return false;
}
@@ -879,6 +855,7 @@ void VulkanDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
VulkanRenderTarget* const rt = handle_cast<VulkanRenderTarget*>(rth);
const VkExtent2D extent = rt->getExtent();
assert_invariant(extent.width > 0 && extent.height > 0);
@@ -895,83 +872,55 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
}
VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth();
#if FILAMENT_VULKAN_VERBOSE
if (depth.texture) {
depth.texture->print();
}
#endif
// We need to determine whether the same depth texture is both sampled and set as an attachment.
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
// the non-sampling case.
bool samplingDepthAttachment = false;
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth();
UTILS_NOUNROLL
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT;
samplerGroupIdx++) {
VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupIdx];
if (!vksb) {
continue;
}
SamplerGroup* sb = vksb->sb.get();
for (size_t i = 0; i < sb->getSize(); i++) {
SamplerDescriptor const* boundSampler = sb->data() + i;
if (UTILS_LIKELY(boundSampler->t)) {
VulkanTexture* texture = handle_cast<VulkanTexture*>(boundSampler->t);
if (!any(texture->usage & TextureUsage::DEPTH_ATTACHMENT)) {
continue;
}
samplingDepthAttachment
= depth.texture && texture->getVkImage() == depth.texture->getVkImage();
if (texture->getPrimaryImageLayout() == VulkanLayout::DEPTH_SAMPLER) {
continue;
}
VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = texture->levels,
.baseArrayLayer = 0,
.layerCount = texture->depth,
};
texture->transitionLayout(cmdbuffer, subresources, VulkanLayout::DEPTH_SAMPLER);
break;
}
}
VulkanDepthLayout initialDepthLayout = fromVkImageLayout(depth.getLayout());
VulkanDepthLayout renderPassDepthLayout =
fromVkImageLayout(getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT));
VulkanDepthLayout finalDepthLayout = renderPassDepthLayout;
// Sometimes we need to permit the shader to sample the depth attachment by transitioning the
// layout of all its subresources to a read-only layout. This is especially crucial for SSAO.
//
// We cannot perform this transition using the render pass because the shaders in this render
// pass might sample from multiple miplevels.
//
// We do not use GENERAL here due to the following validation message:
//
// The Vulkan spec states: Image subresources used as attachments in the current render pass
// must not be accessed in any way other than as an attachment by this command, except for
// cases involving read-only access to depth/stencil attachments as described in the Render
// Pass chapter.
//
// https://vulkan.lunarg.com/doc/view/1.2.182.0/mac/1.2-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-04584)
//
if (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
VkImageSubresourceRange range = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = depth.texture->levels,
.baseArrayLayer = 0,
.layerCount = depth.texture->depth,
};
depth.texture->transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);
initialDepthLayout = renderPassDepthLayout = finalDepthLayout = VulkanDepthLayout::READ_ONLY;
}
// currentDepthLayout tracks state of the layout after the (potential) transition in the above block.
VulkanLayout currentDepthLayout = depth.getLayout();
VulkanLayout const renderPassDepthLayout = samplingDepthAttachment
? VulkanLayout::DEPTH_SAMPLER
: VulkanLayout::DEPTH_ATTACHMENT;
VulkanLayout const finalDepthLayout = renderPassDepthLayout;
TargetBufferFlags clearVal = params.flags.clear;
TargetBufferFlags discardEndVal = params.flags.discardEnd;
if (depth.texture) {
if (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
discardEndVal &= ~TargetBufferFlags::DEPTH;
clearVal &= ~TargetBufferFlags::DEPTH;
}
if (currentDepthLayout != renderPassDepthLayout) {
depth.texture->transitionLayout(cmdbuffer,
depth.getSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT), renderPassDepthLayout);
currentDepthLayout = renderPassDepthLayout;
}
depth.texture->trackLayout(depth.level, depth.layer, toVkImageLayout(renderPassDepthLayout));
}
// Create the VkRenderPass or fetch it from cache.
VulkanFboCache::RenderPassKey rpkey = {
.initialColorLayoutMask = 0,
.initialDepthLayout = currentDepthLayout,
.initialDepthLayout = initialDepthLayout,
.renderPassDepthLayout = renderPassDepthLayout,
.finalDepthLayout = finalDepthLayout,
.depthFormat = depth.getFormat(),
.clear = clearVal,
.clear = params.flags.clear,
.discardStart = discardStart,
.discardEnd = discardEndVal,
.discardEnd = params.flags.discardEnd,
.samples = rt->getSamples(),
.subpassMask = uint8_t(params.subpassMask),
};
@@ -979,15 +928,12 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
const VulkanAttachment& info = rt->getColor(i);
if (info.texture) {
rpkey.initialColorLayoutMask |= 1 << i;
info.texture->trackLayout(info.level, info.layer,
getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
rpkey.colorFormat[i] = info.getFormat();
if (rpkey.samples > 1 && info.texture->samples == 1) {
rpkey.needsResolveMask |= (1 << i);
}
if (info.texture->getPrimaryImageLayout() != VulkanLayout::COLOR_ATTACHMENT) {
info.texture->transitionLayout(cmdbuffer,
info.getSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT),
VulkanLayout::COLOR_ATTACHMENT);
}
} else {
rpkey.colorFormat[i] = VK_FORMAT_UNDEFINED;
}
@@ -1129,6 +1075,21 @@ void VulkanDriver::endRenderPass(int) {
VulkanRenderTarget* rt = mContext.currentRenderPass.renderTarget;
assert_invariant(rt);
// In some cases, depth needs to be transitioned from DEPTH_STENCIL_READ_ONLY_OPTIMAL back to
// GENERAL. We did not do this using the render pass because we need to change multiple mips.
if (mContext.currentRenderPass.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
const VulkanAttachment& depth = rt->getDepth();
VkImageSubresourceRange range = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = depth.texture->levels,
.baseArrayLayer = 0,
.layerCount = depth.texture->depth,
};
depth.texture->transitionLayout(cmdbuffer, range,
getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT));
}
// Since we might soon be sampling from the render target that we just wrote to, we need a
// pipeline barrier between framebuffer writes and shader reads. This is a memory barrier rather
// than an image barrier. If we were to use image barriers here, we would potentially need to
@@ -1185,7 +1146,7 @@ void VulkanDriver::nextSubpass(int) {
VulkanAttachment subpassInput = renderTarget->getColor(i);
VkDescriptorImageInfo info = {
.imageView = subpassInput.getImageView(VK_IMAGE_ASPECT_COLOR_BIT),
.imageLayout = ImgUtil::getVkLayout(subpassInput.getLayout()),
.imageLayout = subpassInput.getLayout(),
};
mPipelineCache.bindInputAttachment(i, info);
}
@@ -1331,12 +1292,6 @@ void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
}
void VulkanDriver::pushGroupMarker(char const* string, uint32_t len) {
#if FILAMENT_VULKAN_VERBOSE
renderPassMarkers.push(std::string(string));
utils::slog.d << "----> " << string << utils::io::endl;
#endif
// TODO: Add group marker color to the Driver API
constexpr float MARKER_COLOR[] = { 0.0f, 1.0f, 0.0f, 1.0f };
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
@@ -1358,13 +1313,6 @@ void VulkanDriver::pushGroupMarker(char const* string, uint32_t len) {
}
void VulkanDriver::popGroupMarker(int) {
#if FILAMENT_VULKAN_VERBOSE
std::string const& marker = renderPassMarkers.top();
renderPassMarkers.pop();
utils::slog.d << "<---- " << marker << utils::io::endl;
#endif
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
if (mContext.debugUtilsSupported) {
vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
@@ -1382,144 +1330,18 @@ void VulkanDriver::stopCapture(int) {
}
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) {
const VkDevice device = mContext.device;
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& pbd) {
VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget*>(src);
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
assert_invariant(srcTexture);
const VkFormat srcFormat = srcTexture->getVkFormat();
const bool swizzle = srcFormat == VK_FORMAT_B8G8R8A8_UNORM;
// Create a host visible, linearly tiled image as a staging area.
VkImageCreateInfo imageInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = srcFormat,
.extent = { width, height, 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_LINEAR,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.initialLayout = ImgUtil::getVkLayout(VulkanLayout::UNDEFINED),
};
VkImage stagingImage;
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
#if FILAMENT_VULKAN_VERBOSE
utils::slog.d << "readPixels created image=" << stagingImage
<< " to copy from image=" << srcTexture->getVkImage()
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
#endif
VkMemoryRequirements memReqs;
VkDeviceMemory stagingMemory;
vkGetImageMemoryRequirements(device, stagingImage, &memReqs);
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
};
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
// TODO: don't flush/wait here, this should be asynchronous
mContext.commands->flush();
mContext.commands->wait();
// Transition the staging image layout.
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
ImgUtil::transitionLayout(cmdbuffer, {
.image = stagingImage,
.oldLayout = VulkanLayout::UNDEFINED,
.newLayout = VulkanLayout::TRANSFER_DST,
.subresources = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
});
const VulkanAttachment srcAttachment = srcTarget->getColor(0);
VkImageCopy imageCopyRegion = {
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = srcAttachment.level,
.baseArrayLayer = srcAttachment.layer,
.layerCount = 1,
},
.srcOffset = {
.x = (int32_t) x,
.y = (int32_t) (srcTarget->getExtent().height - (height + y)),
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1,
},
.extent = {
.width = width,
.height = height,
.depth = 1,
},
};
// Transition the source image layout (which might be the swap chain)
const VkImageSubresourceRange srcRange
= srcAttachment.getSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
// Perform the copy into the staging area. At this point we know that the src layout is
// TRANSFER_SRC_OPTIMAL and the staging area is GENERAL.
UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D();
assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width);
assert_invariant(imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(),
ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC), stagingImage,
ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion);
// Restore the source image layout. Between driver API calls, color images are always kept in
// UNDEFINED layout or in their "usage default" layout.
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::COLOR_ATTACHMENT);
// TODO: don't flush/wait here -- we should do this asynchronously
// Flush and wait.
mContext.commands->flush();
mContext.commands->wait();
VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
VkSubresourceLayout subResourceLayout;
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
// Map image memory so we can start copying from it.
const uint8_t* srcPixels;
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
srcPixels += subResourceLayout.offset;
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), getComponentCount(srcFormat),
srcPixels, subResourceLayout.rowPitch, width, height, swizzle)) {
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
}
vkUnmapMemory(device, stagingMemory);
vkDestroyImage(device, stagingImage, nullptr);
vkFreeMemory(device, stagingMemory, nullptr);
scheduleDestroy(std::move(pbd));
mReadPixels.run(
srcTarget, x, y, width, height, mContext.graphicsQueueFamilyIndex, std::move(pbd),
getTaskHandler(),
[&context = mContext](uint32_t reqs, VkFlags flags) {
return context.selectMemoryType(reqs, flags);
},
[this](PixelBufferDescriptor&& pbd) {
this->scheduleDestroy(std::move(pbd));
});
}
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
@@ -1677,7 +1499,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
VkDescriptorImageInfo samplerInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VulkanPipelineCache::UsageFlags usage;
UTILS_NOUNROLL
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; samplerGroupIdx++) {
const auto& samplerGroup = program->samplerGroupInfo[samplerGroupIdx];
const auto& samplers = samplerGroup.samplers;
@@ -1702,7 +1523,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// TODO: can this uninitialized check be checked in a higher layer?
// This fallback path is very flaky because the dummy texture might not have
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VK_IMAGE_LAYOUT_UNDEFINED)) {
#ifndef NDEBUG
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
@@ -1719,7 +1540,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
samplerInfo[sampler.binding] = {
.sampler = vksampler,
.imageView = texture->getPrimaryImageView(),
.imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout())
.imageLayout = texture->getPrimaryImageLayout()
};
}
}
+7 -4
View File
@@ -23,8 +23,10 @@
#include "VulkanConstants.h"
#include "VulkanContext.h"
#include "VulkanFboCache.h"
#include "VulkanReadPixels.h"
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanTaskHandler.h"
#include "VulkanUtility.h"
#include "private/backend/Driver.h"
@@ -39,11 +41,14 @@ namespace filament::backend {
class VulkanPlatform;
struct VulkanSamplerGroup;
class VulkanDriver final : public DriverBase {
class VulkanDriver final : public DriverBase, private VulkanTaskHandler::Host {
public:
static Driver* create(VulkanPlatform* platform,
const char* const* ppEnabledExtensions, uint32_t enabledExtensionCount, const Platform::DriverConfig& driverConfig) noexcept;
VulkanDriver(VulkanDriver const&) = delete;
VulkanDriver& operator = (VulkanDriver const&) = delete;
private:
void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
@@ -73,9 +78,6 @@ private:
#include "private/backend/DriverAPI.inc"
VulkanDriver(VulkanDriver const&) = delete;
VulkanDriver& operator = (VulkanDriver const&) = delete;
private:
HandleAllocatorVK mHandleAllocator;
@@ -151,6 +153,7 @@ private:
VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter;
VulkanSamplerGroup* mSamplerBindings[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VulkanReadPixels mReadPixels;
};
} // namespace filament::backend
+10 -14
View File
@@ -29,8 +29,6 @@ using namespace bluevk;
namespace filament::backend {
using ImgUtil = VulkanImageUtility;
bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
const RenderPassKey& k2) const {
if (k1.initialColorLayoutMask != k2.initialColorLayoutMask) return false;
@@ -196,7 +194,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) {
continue;
}
const VkImageLayout subpassLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT);
const VkImageLayout subpassLayout = getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT);
uint32_t index;
if (!hasSubpasses) {
@@ -241,10 +239,9 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.storeOp = config.samples == 1 ? kEnableStore : kDisableStore,
.stencilLoadOp = kDontCare,
.stencilStoreOp = kDisableStore,
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
? ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT)
: ImgUtil::getVkLayout(VulkanLayout::UNDEFINED),
.finalLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
.initialLayout = (!discard && config.initialColorLayoutMask & (1 << i)) ?
VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT)
};
}
@@ -270,8 +267,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
}
pResolveAttachment->attachment = attachmentIndex;
pResolveAttachment->layout
= ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT_RESOLVE);
pResolveAttachment->layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
++pResolveAttachment;
attachments[attachmentIndex++] = {
@@ -281,8 +277,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.storeOp = kEnableStore,
.stencilLoadOp = kDontCare,
.stencilStoreOp = kDisableStore,
.initialLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
.finalLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT),
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT)
};
}
@@ -291,7 +287,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
const bool clear = any(config.clear & TargetBufferFlags::DEPTH);
const bool discardStart = any(config.discardStart & TargetBufferFlags::DEPTH);
const bool discardEnd = any(config.discardEnd & TargetBufferFlags::DEPTH);
depthAttachmentRef.layout = ImgUtil::getVkLayout(config.renderPassDepthLayout);
depthAttachmentRef.layout = toVkImageLayout(config.renderPassDepthLayout);
depthAttachmentRef.attachment = attachmentIndex;
attachments[attachmentIndex++] = {
.format = config.depthFormat,
@@ -300,8 +296,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.storeOp = discardEnd ? kDisableStore : kEnableStore,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = ImgUtil::getVkLayout(config.initialDepthLayout),
.finalLayout = ImgUtil::getVkLayout(config.finalDepthLayout),
.initialLayout = toVkImageLayout(config.initialDepthLayout),
.finalLayout = toVkImageLayout(config.finalDepthLayout),
};
}
renderPassInfo.attachmentCount = attachmentIndex;
+28 -6
View File
@@ -27,6 +27,13 @@
namespace filament::backend {
// Avoid using VkImageLayout since it requires 4 bytes.
enum class VulkanDepthLayout : uint8_t {
UNDEFINED, // VK_IMAGE_LAYOUT_UNDEFINED
GENERAL, // VK_IMAGE_LAYOUT_GENERAL
READ_ONLY, // VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.
};
// Simple manager for VkFramebuffer and VkRenderPass objects.
//
// Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
@@ -47,12 +54,11 @@ public:
// - For each color target, the pre-existing layout is either UNDEFINED (0) or GENERAL (1).
// - The render pass and final images layout for color buffers is always GENERAL.
uint8_t initialColorLayoutMask;
// Note that if VulkanLayout grows beyond 16, we'd need to up this.
VulkanLayout initialDepthLayout : 4;
VulkanLayout renderPassDepthLayout : 4;
VulkanLayout finalDepthLayout : 4;
uint8_t padding : 4;
VulkanDepthLayout initialDepthLayout : 2;
VulkanDepthLayout renderPassDepthLayout : 2;
VulkanDepthLayout finalDepthLayout : 2; // for now this is always GENERAL
uint8_t padding0 : 2;
uint8_t padding1[2];
VkFormat colorFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 32 bytes
VkFormat depthFormat; // 4 bytes
@@ -126,6 +132,22 @@ private:
uint32_t mCurrentTime = 0;
};
inline VulkanDepthLayout fromVkImageLayout(VkImageLayout layout) {
switch (layout) {
case VK_IMAGE_LAYOUT_GENERAL: return VulkanDepthLayout::GENERAL;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: return VulkanDepthLayout::READ_ONLY;
default: return VulkanDepthLayout::UNDEFINED;
}
}
inline VkImageLayout toVkImageLayout(VulkanDepthLayout layout) {
switch (layout) {
case VulkanDepthLayout::GENERAL: return VK_IMAGE_LAYOUT_GENERAL;
case VulkanDepthLayout::READ_ONLY: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
default: return VK_IMAGE_LAYOUT_UNDEFINED;
}
}
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
@@ -93,9 +93,7 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
pEntries[i] = {
.constantID = specializationConstants[i].id,
.offset = offset,
// Turns out vulkan expects the size of bool to be 4 (verified through
// validation layer). So all expected types are of 4 bytes.
.size = 4,
.size = sizeof(arg)
};
T* const addr = (T*)((char*)pData + offset);
*addr = arg;
@@ -186,13 +184,10 @@ VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context, uint32_t width, u
return;
}
// MSAA depth texture must have the mipmap count of 1
uint8_t const msLevel = 1;
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
VulkanTexture* msTexture = depthTexture->getSidecar();
if (UTILS_UNLIKELY(msTexture == nullptr)) {
msTexture = new VulkanTexture(context, depthTexture->target, msLevel,
msTexture = new VulkanTexture(context, depthTexture->target, depthTexture->levels,
depthTexture->format, samples, depthTexture->width, depthTexture->height,
depthTexture->depth, depthTexture->usage, stagePool);
depthTexture->setSidecar(msTexture);
@@ -200,7 +195,7 @@ VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context, uint32_t width, u
mMsaaDepthAttachment = {
.texture = msTexture,
.level = msLevel,
.level = mDepth.level,
.layer = mDepth.layer,
};
}
@@ -1,245 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanImageUtility.h"
#include "VulkanTexture.h"
#include <utils/Panic.h>
#include <utils/algorithm.h>
#include <utils/debug.h>
#include <tuple>
using namespace bluevk;
namespace filament::backend {
namespace {
inline VkImageLayout getVkImageLayout(VulkanLayout layout) {
switch (layout) {
case VulkanLayout::UNDEFINED:
return VK_IMAGE_LAYOUT_UNDEFINED;
case VulkanLayout::READ_WRITE:
return VK_IMAGE_LAYOUT_GENERAL;
case VulkanLayout::READ_ONLY:
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
case VulkanLayout::TRANSFER_SRC:
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
case VulkanLayout::TRANSFER_DST:
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
case VulkanLayout::DEPTH_ATTACHMENT:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case VulkanLayout::DEPTH_SAMPLER:
return VK_IMAGE_LAYOUT_GENERAL;
case VulkanLayout::PRESENT:
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Filament sometimes samples from one miplevel while writing to another level in the same
// texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive layout
// transitions. So, keep it simple and use GENERAL for all color-attachable textures.
case VulkanLayout::COLOR_ATTACHMENT:
return VK_IMAGE_LAYOUT_GENERAL;
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
}
inline std::tuple<VkAccessFlags, VkAccessFlags, VkPipelineStageFlags, VkPipelineStageFlags,
VkImageLayout, VkImageLayout>
getVkTransition(const VulkanLayoutTransition& transition) {
VkAccessFlags srcAccessMask, dstAccessMask;
VkPipelineStageFlags srcStage, dstStage;
switch (transition.oldLayout) {
case VulkanLayout::UNDEFINED:
srcAccessMask = 0;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VulkanLayout::COLOR_ATTACHMENT:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VulkanLayout::READ_WRITE:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::READ_ONLY:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::TRANSFER_SRC:
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VulkanLayout::TRANSFER_DST:
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VulkanLayout::DEPTH_ATTACHMENT:
srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case VulkanLayout::DEPTH_SAMPLER:
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::PRESENT:
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
}
switch (transition.newLayout) {
case VulkanLayout::COLOR_ATTACHMENT:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VulkanLayout::READ_WRITE:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::READ_ONLY:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::TRANSFER_SRC:
dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VulkanLayout::TRANSFER_DST:
dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VulkanLayout::DEPTH_ATTACHMENT:
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break;
case VulkanLayout::DEPTH_SAMPLER:
dstAccessMask
= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break;
case VulkanLayout::PRESENT:
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
case VulkanLayout::UNDEFINED:
dstAccessMask = 0;
dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
}
return std::make_tuple(srcAccessMask, dstAccessMask, srcStage, dstStage,
getVkImageLayout(transition.oldLayout), getVkImageLayout(transition.newLayout));
}
}// anonymous namespace
VkImageViewType VulkanImageUtility::getViewType(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_CUBEMAP:
return VK_IMAGE_VIEW_TYPE_CUBE;
case SamplerType::SAMPLER_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case SamplerType::SAMPLER_3D:
return VK_IMAGE_VIEW_TYPE_3D;
default:
return VK_IMAGE_VIEW_TYPE_2D;
}
}
void VulkanImageUtility::transitionLayout(VkCommandBuffer cmdbuffer,
VulkanLayoutTransition transition) {
if (transition.oldLayout == transition.newLayout) {
return;
}
auto [srcAccessMask, dstAccessMask, srcStage, dstStage, oldLayout, newLayout]
= getVkTransition(transition);
assert_invariant(transition.image != VK_NULL_HANDLE && "No image for transition");
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = srcAccessMask,
.dstAccessMask = dstAccessMask,
.oldLayout = oldLayout,
.newLayout = newLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = transition.image,
.subresourceRange = transition.subresources,
};
vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
VkImageLayout VulkanImageUtility::getVkLayout(VulkanLayout layout) {
return getVkImageLayout(layout);
}
}// namespace filament::backend
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
if (a.aspectMask < b.aspectMask) return true;
if (a.aspectMask > b.aspectMask) return false;
if (a.baseMipLevel < b.baseMipLevel) return true;
if (a.baseMipLevel > b.baseMipLevel) return false;
if (a.levelCount < b.levelCount) return true;
if (a.levelCount > b.levelCount) return false;
if (a.baseArrayLayer < b.baseArrayLayer) return true;
if (a.baseArrayLayer > b.baseArrayLayer) return false;
if (a.layerCount < b.layerCount) return true;
if (a.layerCount > b.layerCount) return false;
return false;
}
#if FILAMENT_VULKAN_VERBOSE
#define CASE(VALUE) \
case filament::backend::VulkanLayout::VALUE: { \
out << #VALUE; \
out << " [" \
<< filament::backend::VulkanImageUtility::getVkLayout( \
filament::backend::VulkanLayout::VALUE) \
<< "]"; \
break; \
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const filament::backend::VulkanLayout& layout) {
switch (layout) {
CASE(UNDEFINED)
CASE(READ_WRITE)
CASE(TRANSFER_SRC)
CASE(TRANSFER_DST)
CASE(DEPTH_ATTACHMENT)
CASE(DEPTH_SAMPLER)
CASE(PRESENT)
CASE(COLOR_ATTACHMENT)
CASE(COLOR_ATTACHMENT_RESOLVE)
}
return out;
}
#undef CASE
#endif
@@ -1,90 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANIMAGEUTILITY_H
#define TNT_FILAMENT_BACKEND_VULKANIMAGEUTILITY_H
#include <backend/DriverEnums.h>
#include <utils/Log.h>
#include <bluevk/BlueVK.h>
namespace filament::backend {
struct VulkanTexture;
enum class VulkanLayout : uint8_t {
// The initial layout after the creation of the VkImage. We use this to denote the state before
// any transition.
UNDEFINED,
// Fragment/vertex shader accessible layout for reading and writing.
READ_WRITE,
// Fragment/vertex shader accessible layout for reading only.
READ_ONLY,
// For the source of a copy operation.
TRANSFER_SRC,
// For the destination of a copy operation.
TRANSFER_DST,
// For using a depth texture as an attachment.
DEPTH_ATTACHMENT,
// For using a depth texture both as an attachment and as a sampler.
DEPTH_SAMPLER,
// For swapchain images that will be presented.
PRESENT,
// For color attachments, but also used when the image is a sampler.
// TODO: explore separate layout policies for attachment+sampling and just attachment.
COLOR_ATTACHMENT,
// For color attachment MSAA resolves.
COLOR_ATTACHMENT_RESOLVE,
};
struct VulkanLayoutTransition {
VkImage image;
VulkanLayout oldLayout;
VulkanLayout newLayout;
VkImageSubresourceRange subresources;
};
class VulkanImageUtility {
public:
static VkImageViewType getViewType(SamplerType target);
inline static VulkanLayout getDefaultLayout(TextureUsage usage) {
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
return VulkanLayout::DEPTH_ATTACHMENT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
return VulkanLayout::COLOR_ATTACHMENT;
}
// Finally, the layout for an immutable texture is optimal read-only.
return VulkanLayout::READ_ONLY;
}
static VkImageLayout getVkLayout(VulkanLayout layout);
static void transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition);
};
} // namespace filament::backend
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::VulkanLayout& layout);
#endif // TNT_FILAMENT_BACKEND_VULKANIMAGEUTILITY_H
@@ -74,7 +74,7 @@ VulkanPipelineCache::VulkanPipelineCache() : mDefaultRasterState(createDefaultRa
mDummyBufferWriteInfo.pBufferInfo = &mDummyBufferInfo;
mDummyBufferWriteInfo.pTexelBufferView = nullptr;
mDummyTargetInfo.imageLayout = VulkanImageUtility::getVkLayout(VulkanLayout::READ_ONLY);
mDummyTargetInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
mDummyTargetWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummyTargetWriteInfo.pNext = nullptr;
mDummyTargetWriteInfo.dstArrayElement = 0;
@@ -0,0 +1,261 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanReadPixels.h"
#include "DataReshaper.h"
#include "VulkanHandles.h"
#include "VulkanTaskHandler.h"
#include "VulkanTexture.h"
#include <utils/Log.h>
using namespace bluevk;
namespace filament::backend {
VulkanReadPixels::~VulkanReadPixels() noexcept {
assert_invariant(mDevice != VK_NULL_HANDLE);
if (mCommandPool == VK_NULL_HANDLE) {
return;
}
vkDestroyCommandPool(mDevice, mCommandPool, VKALLOC);
}
void VulkanReadPixels::initialize(VkDevice device) {
mDevice = device;
}
void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x, uint32_t const y,
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
PixelBufferDescriptor&& pbd, VulkanTaskHandler& taskHandler,
SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc) {
assert_invariant(mDevice != VK_NULL_HANDLE);
VkDevice device = mDevice;
if (mCommandPool == VK_NULL_HANDLE) {
// Create a command pool if one has not been created.
VkCommandPoolCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
| VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = graphicsQueueFamilyIndex,
};
vkCreateCommandPool(device, &createInfo, VKALLOC, &mCommandPool);
}
VkCommandPool cmdpool = mCommandPool;
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
assert_invariant(srcTexture);
VkFormat const srcFormat = srcTexture->getVkFormat();
bool const swizzle
= srcFormat == VK_FORMAT_B8G8R8A8_UNORM || srcFormat == VK_FORMAT_B8G8R8A8_SRGB;
// Create a host visible, linearly tiled image as a staging area.
VkImageCreateInfo const imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = srcFormat,
.extent = {width, height, 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_LINEAR,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImage stagingImage;
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
VkMemoryRequirements memReqs;
VkDeviceMemory stagingMemory;
vkGetImageMemoryRequirements(device, stagingImage, &memReqs);
VkMemoryAllocateInfo const allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT)};
vkAllocateMemory(device, &allocInfo, VKALLOC, &stagingMemory);
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
VkCommandBuffer cmdbuffer;
VkCommandBufferAllocateInfo const allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = cmdpool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer);
VkCommandBufferBeginInfo const binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(cmdbuffer, &binfo);
transitionImageLayout(cmdbuffer, {
.image = stagingImage,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.subresources = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
.srcAccessMask = 0,
.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
});
VulkanAttachment const srcAttachment = srcTarget->getColor(0);
VkImageCopy const imageCopyRegion = {
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = srcAttachment.level,
.baseArrayLayer = srcAttachment.layer,
.layerCount = 1,
},
.srcOffset = {
.x = (int32_t) x,
.y = (int32_t) (srcTarget->getExtent().height - (height + y)),
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1,
},
.extent = {
.width = width,
.height = height,
.depth = 1,
},
};
// Transition the source image layout (which might be the swap chain)
VkImageSubresourceRange const srcRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = srcAttachment.level,
.levelCount = 1,
.baseArrayLayer = srcAttachment.layer,
.layerCount = 1,
};
srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// Perform the copy into the staging area. At this point we know that the src layout is
// TRANSFER_SRC_OPTIMAL and the staging area is GENERAL.
UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D();
assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width);
assert_invariant(
imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
stagingImage, VK_IMAGE_LAYOUT_GENERAL, 1, &imageCopyRegion);
// Restore the source image layout. Between driver API calls, color images are always kept in
// UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout).
srcTexture->transitionLayout(cmdbuffer, srcRange,
getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
vkEndCommandBuffer(cmdbuffer);
VkQueue queue;
vkGetDeviceQueue(device, graphicsQueueFamilyIndex, 0, &queue);
VkSubmitInfo const submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = VK_NULL_HANDLE,
.pWaitDstStageMask = VK_NULL_HANDLE,
.commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = VK_NULL_HANDLE,
};
VkFence fence;
VkFenceCreateInfo const fenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
};
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
vkQueueSubmit(queue, 1, &submitInfo, fence);
auto* const pUserBuffer = new PixelBufferDescriptor(std::move(pbd));
auto const waitTaskId = taskHandler.createTask(
[device, width, height, swizzle, srcFormat, fence, stagingImage, stagingMemory, cmdpool,
cmdbuffer, pUserBuffer, readCompleteFunc,
&taskHandler](VulkanTaskHandler::TaskId taskId, void* data) mutable {
PixelBufferDescriptor& p = *pUserBuffer;
vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
VkResult status = vkGetFenceStatus(device, fence);
// Fence hasn't been reached. Try waiting again.
if (status == VK_NOT_READY) {
taskHandler.post(taskId);
return;
}
// Need to abort the readPixels if the device is lost.
if (status == VK_ERROR_DEVICE_LOST) {
utils::slog.e << "Device lost while in VulkanReadPixels::run"
<< utils::io::endl;
taskHandler.completed(taskId);
// Try to free the pbd anyway
readCompleteFunc(std::move(p));
delete pUserBuffer;
return;
}
VkImageSubresource subResource{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT};
VkSubresourceLayout subResourceLayout;
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
// Map image memory so that we can start copying from it.
uint8_t const* srcPixels;
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
srcPixels += subResourceLayout.offset;
if (!DataReshaper::reshapeImage(&p, getComponentType(srcFormat),
getComponentCount(srcFormat), srcPixels,
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
static_cast<int>(height), swizzle)) {
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType"
<< utils::io::endl;
}
vkUnmapMemory(device, stagingMemory);
vkDestroyImage(device, stagingImage, VKALLOC);
vkFreeMemory(device, stagingMemory, VKALLOC);
vkDestroyFence(device, fence, VKALLOC);
vkFreeCommandBuffers(device, cmdpool, 1, &cmdbuffer);
readCompleteFunc(std::move(p));
delete pUserBuffer;
taskHandler.completed(taskId);
},
nullptr);
taskHandler.post(waitTaskId);
}
}// namespace filament::backend
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#include "VulkanTaskHandler.h"
#include "private/backend/Driver.h"
#include <bluevk/BlueVK.h>
#include <math/vec4.h>
namespace filament::backend {
struct VulkanContext;
struct VulkanRenderTarget;
class VulkanReadPixels {
public:
using OnReadCompleteFunction = std::function<void(PixelBufferDescriptor&&)>;
using SelecteMemoryFunction = std::function<uint32_t(uint32_t, VkFlags)>;
~VulkanReadPixels() noexcept;
void initialize(VkDevice device);
void run(VulkanRenderTarget const* srcTarget, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
VulkanTaskHandler& taskHandler, SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc);
private:
VkDevice mDevice = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
};
}// namespace filament::backend
#endif//TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
@@ -116,12 +116,13 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
// VK_IMAGE_LAYOUT_PREINITIALIZED or VK_IMAGE_LAYOUT_GENERAL layout. Calling
// vkGetImageSubresourceLayout for a linear image returns a subresource layout mapping that is
// valid for either of those image layouts."
VulkanImageUtility::transitionLayout(cmdbuffer, {
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
.image = image->image,
.oldLayout = VulkanLayout::UNDEFINED,
.newLayout = VulkanLayout::READ_WRITE, // (= VK_IMAGE_LAYOUT_GENERAL)
.subresources = { aspectFlags, 0, 1, 0, 1 },
});
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.subresources = { aspectFlags, 0, 1, 0, 1 }
}));
return image;
}
+26 -14
View File
@@ -33,14 +33,15 @@ bool VulkanSwapChain::acquire() {
if (headlessQueue) {
mCurrentSwapIndex = (mCurrentSwapIndex + 1) % mColor.size();
UTILS_UNUSED_IN_RELEASE VulkanLayout const layout = getColorTexture().getLayout(0, 0);
UTILS_UNUSED_IN_RELEASE const VkImageLayout layout = getColorTexture().getVkLayout(0, 0);
// Next we perform a quick sanity check on layout for headless swap chains. It's easier to
// catch errors here than with validation. If this is the first time a particular image has
// been acquired, it should be in an UNDEFINED state. If this is not the first time, then it
// should be in the normal layout that we use for color attachments.
assert_invariant(
layout == VulkanLayout::UNDEFINED || layout == VulkanLayout::COLOR_ATTACHMENT);
assert_invariant(layout == VK_IMAGE_LAYOUT_UNDEFINED ||
layout == getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
return true;
}
@@ -56,11 +57,12 @@ bool VulkanSwapChain::acquire() {
suboptimal = true;
}
UTILS_UNUSED_IN_RELEASE VulkanLayout const layout = getColorTexture().getLayout(0, 0);
UTILS_UNUSED_IN_RELEASE const VkImageLayout layout = getColorTexture().getVkLayout(0, 0);
// Next perform a quick sanity check on the image layout. Similar to attachable textures, we
// immediately transition the swap chain image layout during the first render pass of the frame.
assert_invariant(layout == VulkanLayout::UNDEFINED || layout == VulkanLayout::PRESENT);
assert_invariant(layout == VK_IMAGE_LAYOUT_UNDEFINED ||
layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
// To ensure that the next command buffer submission does not write into the image before
// it has been acquired, push the image available semaphore into the command buffer manager.
@@ -223,16 +225,26 @@ void VulkanSwapChain::makePresentable() {
return;
}
VulkanTexture& texture = getColorTexture();
VkCommandBuffer const cmdbuffer = mContext.commands->get().cmdbuffer;
VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
VkImageMemoryBarrier barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = texture.getVkLayout(0, 0),
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = texture.getVkImage(),
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1,
},
};
texture.transitionLayout(cmdbuffer, subresources, VulkanLayout::PRESENT);
vkCmdPipelineBarrier(mContext.commands->get().cmdbuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
texture.trackLayout(0, 0, barrier.newLayout);
}
static void getHeadlessQueue(VulkanContext& mContext, VulkanSwapChain& sc) {
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
#define TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
#include "utils/Panic.h"
#include <functional>
#include <map>
#include <memory>
#include <queue>
namespace filament::backend {
class VulkanTaskHandler {
public:
// The Host class is meant to be called on the thread that processes the tasks. It has access to
// the handle function.
class Host {
public:
Host(Host const&) = delete;
Host& operator=(Host const&) = delete;
protected:
Host()
: mHandler(std::make_unique<VulkanTaskHandler>()) {}
void runTaskHandler() noexcept {
mHandler->handle();
}
VulkanTaskHandler& getTaskHandler() noexcept {
return *mHandler;
}
private:
std::unique_ptr<VulkanTaskHandler> mHandler;
};
using TaskId = uint32_t;
using TaskFunc = std::function<void(TaskId, void*)>;
using Task = std::pair<TaskFunc, void*>;
TaskId createTask(TaskFunc const& func, void* data) noexcept {
TaskId const id = mNextTaskId++;
mTasks[id] = std::pair(func, data);
return id;
}
// Task that will never be put on the queue again should be marked as completed by calling this
// function.
void completed(TaskId taskId) noexcept {
assert_invariant(mTasks.find(taskId) != mTasks.end());
mTasks.erase(taskId);
}
void post(TaskId taskId) noexcept {
assert_invariant(mTasks.find(taskId) != mTasks.end());
mTaskQueue.push(taskId);
}
VulkanTaskHandler() = default;
VulkanTaskHandler(VulkanTaskHandler const&) = delete;
VulkanTaskHandler& operator=(VulkanTaskHandler const&) = delete;
private:
inline void handle() {
while (!mTaskQueue.empty()) {
auto taskId = mTaskQueue.front();
mTaskQueue.pop();
// It is possible for taskIds in the queue to refer to a task that has already been
// completed. Just ignore.
if (mTasks.find(taskId) == mTasks.end()) {
continue;
}
auto& [func, data] = mTasks[taskId];
func(taskId, data);
}
}
std::queue<TaskId> mTaskQueue;
std::map<TaskId, Task> mTasks;
uint32_t mNextTaskId = 0;
friend class Host;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
+98 -97
View File
@@ -28,21 +28,13 @@ using namespace bluevk;
namespace filament::backend {
using ImgUtil = VulkanImageUtility;
VulkanTexture::VulkanTexture(VulkanContext& context, VkImage image, VkFormat format, uint8_t samples,
uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool) :
HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, tusage),
mVkFormat(format),
mViewType(ImgUtil::getViewType(target)),
mViewType(getImageViewType(target)),
mSwizzle({}),
mTextureImage(image),
mPrimaryViewRange{
.aspectMask = getImageAspect(),
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
mContext(context),
mStagePool(stagePool) {}
@@ -55,7 +47,7 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
mVkFormat(tformat == TextureFormat::DEPTH24 ? context.finalDepthFormat :
backend::getVkFormat(tformat)),
mViewType(ImgUtil::getViewType(target)),
mViewType(getImageViewType(target)),
mSwizzle(swizzle), mContext(context), mStagePool(stagePool) {
@@ -136,16 +128,13 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
// any kind of attachment (color or depth).
const auto& limits = context.physicalDeviceProperties.limits;
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
samples = reduceSampleCount(samples, isDepthFormat(mVkFormat)
? limits.sampledImageDepthSampleCounts
: limits.sampledImageColorSampleCounts);
samples = reduceSampleCount(samples, isDepthFormat(mVkFormat) ?
limits.sampledImageDepthSampleCounts : limits.sampledImageColorSampleCounts);
}
if (imageInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
samples = reduceSampleCount(samples, limits.framebufferColorSampleCounts);
}
if (imageInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
samples = reduceSampleCount(samples, limits.sampledImageDepthSampleCounts);
if (imageInfo.usage & (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
samples = reduceSampleCount(samples, limits.framebufferDepthSampleCounts &
limits.framebufferColorSampleCounts);
}
this->samples = samples;
imageInfo.samples = (VkSampleCountFlagBits) samples;
@@ -153,15 +142,12 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
VkResult error = vkCreateImage(context.device, &imageInfo, VKALLOC, &mTextureImage);
if (error || FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "vkCreateImage: "
<< "image = " << mTextureImage << ", "
<< "result = " << error << ", "
<< "handle = " << utils::io::hex << mTextureImage << utils::io::dec << ", "
<< "extent = " << w << "x" << h << "x"<< depth << ", "
<< "mipLevels = " << int(levels) << ", "
<< "usage = " << imageInfo.usage << ", "
<< "samples = " << imageInfo.samples << ", "
<< "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", "
<< "format = " << mVkFormat << utils::io::endl;
}
ASSERT_POSTCONDITION(!error, "Unable to create image.");
@@ -198,7 +184,7 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
}
// Go ahead and create the primary image view, no need to do it lazily.
getImageView(mPrimaryViewRange, mViewType, mSwizzle);
getImageView(mPrimaryViewRange);
// Transition the layout of each image slice that might be used as a render target.
// We do not transition images that are merely SAMPLEABLE, this is deferred until upload time
@@ -206,8 +192,9 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
if (any(usage & (TextureUsage::COLOR_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT))) {
const uint32_t layers = mPrimaryViewRange.layerCount;
VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers };
VkImageLayout layout = getDefaultImageLayout(usage);
VkCommandBuffer commands = mContext.commands->get().cmdbuffer;
transitionLayout(commands, range, ImgUtil::getDefaultLayout(usage));
transitionLayout(commands, range, layout);
}
}
@@ -293,26 +280,19 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
transitionRange.layerCount = depth;
}
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
VulkanLayout nextLayout = getLayout(0, miplevel);
VkImageLayout const newVkLayout = ImgUtil::getVkLayout(newLayout);
transitionLayout(cmdbuffer, transitionRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (nextLayout == VulkanLayout::UNDEFINED) {
nextLayout = VulkanLayout::READ_WRITE;
}
vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);
transitionLayout(cmdbuffer, transitionRange, newLayout);
vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, newVkLayout, 1, &copyRegion);
transitionLayout(cmdbuffer, transitionRange, nextLayout);
transitionLayout(cmdbuffer, transitionRange, getDefaultImageLayout(usage));
}
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
uint32_t height, uint32_t depth, uint32_t miplevel) {
void* mapped = nullptr;
VulkanStageImage const* stage
= mStagePool.acquireImage(hostData.format, hostData.type, width, height);
VulkanStageImage const* stage = mStagePool.acquireImage(
hostData.format, hostData.type, width, height);
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
memcpy(mapped, hostData.buffer, hostData.size);
vmaUnmapMemory(mContext.allocator, stage->memory);
@@ -336,32 +316,30 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
const VkImageSubresourceRange range = { aspect, miplevel, 1, 0, 1 };
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
VulkanLayout const oldLayout = getLayout(0, miplevel);
transitionLayout(cmdbuffer, range, newLayout);
transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdBlitImage(cmdbuffer, stage->image, ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
mTextureImage, ImgUtil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
vkCmdBlitImage(cmdbuffer, stage->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mTextureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, VK_FILTER_NEAREST);
transitionLayout(cmdbuffer, range, oldLayout);
transitionLayout(cmdbuffer, range, getDefaultImageLayout(usage));
}
void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel) {
maxMiplevel = filament::math::min(int(maxMiplevel), int(this->levels - 1));
mPrimaryViewRange.baseMipLevel = minMiplevel;
mPrimaryViewRange.levelCount = maxMiplevel - minMiplevel + 1;
getImageView(mPrimaryViewRange, mViewType, mSwizzle);
getImageView(mPrimaryViewRange);
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
// Attachments should only have one mipmap level and one layer.
range.levelCount = 1;
range.layerCount = 1;
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
}
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
VkImageView VulkanTexture::getAttachmentView(int singleLevel, int singleLayer,
VkImageAspectFlags aspect) {
VkImageSubresourceRange range = {
.aspectMask = aspect,
.baseMipLevel = uint32_t(singleLevel),
.levelCount = uint32_t(1),
.baseArrayLayer = uint32_t(singleLayer),
.layerCount = uint32_t(1),
};
auto iter = mCachedImageViews.find(range);
if (iter != mCachedImageViews.end()) {
return iter->second;
@@ -371,10 +349,31 @@ VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageVi
.pNext = nullptr,
.flags = 0,
.image = mTextureImage,
.viewType = viewType,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = mVkFormat,
.components = swizzle,
.subresourceRange = range,
.components = VkComponentMapping{},
.subresourceRange = range
};
VkImageView imageView;
vkCreateImageView(mContext.device, &viewInfo, VKALLOC, &imageView);
mCachedImageViews.emplace(range, imageView);
return imageView;
}
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range) {
auto iter = mCachedImageViews.find(range);
if (iter != mCachedImageViews.end()) {
return iter->second;
}
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = mTextureImage,
.viewType = mViewType,
.format = mVkFormat,
.components = mSwizzle,
.subresourceRange = range
};
VkImageView imageView;
vkCreateImageView(mContext.device, &viewInfo, VKALLOC, &imageView);
@@ -388,68 +387,70 @@ VkImageAspectFlags VulkanTexture::getImageAspect() const {
}
void VulkanTexture::transitionLayout(VkCommandBuffer commands, const VkImageSubresourceRange& range,
VulkanLayout newLayout) {
VulkanLayout oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel);
VkImageLayout newLayout) {
// In debug builds, ensure that all subresources in the given range have the same layout.
// It's easier to catch a mistake here than with validation, which waits until submission time.
VkImageLayout oldLayout = getVkLayout(range.baseArrayLayer, range.baseMipLevel);
#ifndef NDEBUG
if (oldLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
for (uint32_t layer = 0; layer < range.layerCount; ++layer) {
for (uint32_t level = 0; level < range.levelCount; ++level) {
assert_invariant(getVkLayout(layer + range.baseArrayLayer,
level + range.baseMipLevel) == oldLayout);
}
}
}
#endif
#if FILAMENT_VULKAN_VERBOSE
utils::slog.i << "transition layout of " << mTextureImage << ",layer=" << range.baseArrayLayer
<< ",level=" << range.baseMipLevel << " from=" << oldLayout << " to=" << newLayout
<< " depth=" << isDepthFormat(mVkFormat) << utils::io::endl;
#endif
ImgUtil::transitionLayout(commands, {
transitionImageLayout(commands, textureTransitionHelper({
.image = mTextureImage,
.oldLayout = oldLayout,
.newLayout = newLayout,
.subresources = range,
});
}));
uint32_t const firstLayer = range.baseArrayLayer;
uint32_t const lastLayer = firstLayer + range.layerCount;
uint32_t const firstLevel = range.baseMipLevel;
uint32_t const lastLevel = firstLevel + range.levelCount;
const uint32_t first_layer = range.baseArrayLayer;
const uint32_t last_layer = first_layer + range.layerCount;
const uint32_t first_level = range.baseMipLevel;
const uint32_t last_level = first_level + range.levelCount;
assert_invariant(firstLevel <= 0xffff && lastLevel <= 0xffff);
assert_invariant(firstLayer <= 0xffff && lastLayer <= 0xffff);
assert_invariant(first_level <= 0xffff && last_level <= 0xffff);
assert_invariant(first_layer <= 0xffff && last_layer <= 0xffff);
if (newLayout == VulkanLayout::UNDEFINED) {
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
for (uint32_t layer = first_layer; layer < last_layer; ++layer) {
const uint32_t first = (layer << 16) | first_level;
const uint32_t last = (layer << 16) | last_level;
mSubresourceLayouts.clear(first, last);
}
} else {
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
for (uint32_t layer = first_layer; layer < last_layer; ++layer) {
const uint32_t first = (layer << 16) | first_level;
const uint32_t last = (layer << 16) | last_level;
mSubresourceLayouts.add(first, last, newLayout);
}
}
}
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
// Notifies the texture that a particular subresource's layout has changed.
void VulkanTexture::trackLayout(uint32_t miplevel, uint32_t layer, VkImageLayout layout) {
assert_invariant((miplevel + 1) <= 0xffff && layer <= 0xffff);
const uint32_t first = (layer << 16) | miplevel;
const uint32_t last = (layer << 16) | (miplevel + 1);
if (UTILS_UNLIKELY(layout == VK_IMAGE_LAYOUT_UNDEFINED)) {
mSubresourceLayouts.clear(first, last);
} else {
mSubresourceLayouts.add(first, last, layout);
}
}
VkImageLayout VulkanTexture::getVkLayout(uint32_t layer, uint32_t level) const {
assert_invariant(level <= 0xffff && layer <= 0xffff);
const uint32_t key = (layer << 16) | level;
if (!mSubresourceLayouts.has(key)) {
return VulkanLayout::UNDEFINED;
return VK_IMAGE_LAYOUT_UNDEFINED;
}
return mSubresourceLayouts.get(key);
}
#if FILAMENT_VULKAN_VERBOSE
void VulkanTexture::print() const {
const uint32_t firstLayer = 0;
const uint32_t lastLayer = firstLayer + mPrimaryViewRange.layerCount;
const uint32_t firstLevel = 0;
const uint32_t lastLevel = firstLevel + mPrimaryViewRange.levelCount;
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
for (uint32_t level = firstLevel; level < lastLevel; ++level) {
utils::slog.d << "[" << mTextureImage << "]: (" << layer << "," << level
<< ")=" << getLayout(layer, level) << utils::io::endl;
}
}
}
#endif
} // namespace filament::backend
+15 -18
View File
@@ -19,7 +19,7 @@
#include "VulkanDriver.h"
#include "VulkanBuffer.h"
#include "VulkanImageUtility.h"
#include "VulkanUtility.h"
#include <utils/RangeMap.h>
@@ -51,42 +51,39 @@ struct VulkanTexture : public HwTexture {
VkImageSubresourceRange getPrimaryRange() const { return mPrimaryViewRange; }
VulkanLayout getPrimaryImageLayout() const {
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
VkImageLayout getPrimaryImageLayout() const {
return getVkLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
}
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
// and the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange range);
VkImageView getAttachmentView(int singleLevel, int singleLayer, VkImageAspectFlags aspect);
VkFormat getVkFormat() const { return mVkFormat; }
VkImage getVkImage() const { return mTextureImage; }
VulkanLayout getLayout(uint32_t layer, uint32_t level) const;
VkImageLayout getVkLayout(uint32_t layer, uint32_t level) const;
void setSidecar(VulkanTexture* sidecar) { mSidecarMSAA = sidecar; }
VulkanTexture* getSidecar() const { return mSidecarMSAA; }
void transitionLayout(VkCommandBuffer commands, const VkImageSubresourceRange& range,
VulkanLayout newLayout);
VkImageLayout newLayout);
// Notifies the texture that a particular subresource's layout has changed.
void trackLayout(uint32_t miplevel, uint32_t layer, VkImageLayout layout);
// Gets or creates a cached VkImageView for a range of miplevels and array layers.
VkImageView getImageView(VkImageSubresourceRange range);
// Returns the preferred data plane of interest for all image views.
// For now this always returns either DEPTH or COLOR.
VkImageAspectFlags getImageAspect() const;
#if FILAMENT_VULKAN_VERBOSE
void print() const;
#endif
private:
// Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and
// swizzle (or not).
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height,
uint32_t depth, uint32_t miplevel);
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
uint32_t height, uint32_t depth, uint32_t miplevel);
VulkanTexture* mSidecarMSAA = nullptr;
const VkFormat mVkFormat;
@@ -96,7 +93,7 @@ private:
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
// Track the image layout of each subresource using a sparse range map.
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
utils::RangeMap<uint32_t, VkImageLayout> mSubresourceLayouts;
// Track the range of subresources that define the "primary" image view, which is the special
// image view that gets bound to an actual texture sampler.
@@ -611,6 +611,45 @@ VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]) {
return map;
}
VkImageViewType getImageViewType(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_CUBEMAP:
return VK_IMAGE_VIEW_TYPE_CUBE;
case SamplerType::SAMPLER_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case SamplerType::SAMPLER_3D:
return VK_IMAGE_VIEW_TYPE_3D;
default:
return VK_IMAGE_VIEW_TYPE_2D;
}
}
// Between Driver API calls, non-presentable texture images are generally kept either in the
// UNDEFINED layout, or in the usage-specific layout specified by this function. This simple
// convention allows the use of a bitfield to represent layout in RenderPassKey. However there are
// exceptions for depth and for transient use of specialized layouts, which is why VulkanTexture
// tracks actual layout at the subresource level.
VkImageLayout getDefaultImageLayout(TextureUsage usage) {
// Filament sometimes samples from depth while it is bound to the current render target, (e.g.
// SSAO does this while depth writes are disabled) so let's keep it simple and use GENERAL for
// all depth textures.
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
return VK_IMAGE_LAYOUT_GENERAL;
}
// Filament sometimes samples from one miplevel while writing to another level in the same
// texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive layout
// transitions. So, keep it simple and use GENERAL for all color-attachable textures.
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
return VK_IMAGE_LAYOUT_GENERAL;
}
// Finally, the layout for an immutable texture is optimal read-only.
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
VkShaderStageFlags flags = 0x0;
if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT;
@@ -618,6 +657,87 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
return flags;
}
void transitionImageLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition) {
if (transition.oldLayout == transition.newLayout) {
return;
}
assert_invariant(transition.image != VK_NULL_HANDLE && "Please call bindToSwapChain.");
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = transition.oldLayout;
barrier.newLayout = transition.newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = transition.image;
barrier.subresourceRange = transition.subresources;
barrier.srcAccessMask = transition.srcAccessMask;
barrier.dstAccessMask = transition.dstAccessMask;
vkCmdPipelineBarrier(cmdbuffer, transition.srcStage, transition.dstStage, 0, 0, nullptr, 0,
nullptr, 1, &barrier);
}
VulkanLayoutTransition blitterTransitionHelper(VulkanLayoutTransition transition) {
switch (transition.newLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
default:
transition.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
transition.dstAccessMask = 0;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
}
return transition;
}
VulkanLayoutTransition textureTransitionHelper(VulkanLayoutTransition transition) {
switch (transition.newLayout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
transition.srcAccessMask = 0;
transition.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
transition.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
transition.srcAccessMask = 0;
transition.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
transition.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
// We support PRESENT as a target layout to allow blitting from the swap chain.
// See also SwapChain::makePresentable().
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
transition.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
transition.dstAccessMask = 0;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
default:
PANIC_POSTCONDITION("Unsupported layout transition.");
}
return transition;
}
bool equivalent(const VkRect2D& a, const VkRect2D& b) {
// These are all integers so there's no need for an epsilon.
return a.extent.width == b.extent.width && a.extent.height == b.extent.height &&
@@ -645,6 +765,7 @@ bool isDepthFormat(VkFormat format) {
static uint32_t mostSignificantBit(uint32_t x) { return 1ul << (31ul - utils::clz(x)); }
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
assert_invariant(utils::popcount(sampleCount) == 1);
if (sampleCount & mask) {
return sampleCount;
}
@@ -652,3 +773,17 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
}
} // namespace filament::backend
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
if (a.aspectMask < b.aspectMask) return true;
if (a.aspectMask > b.aspectMask) return false;
if (a.baseMipLevel < b.baseMipLevel) return true;
if (a.baseMipLevel > b.baseMipLevel) return false;
if (a.levelCount < b.levelCount) return true;
if (a.levelCount > b.levelCount) return false;
if (a.baseArrayLayer < b.baseArrayLayer) return true;
if (a.baseArrayLayer > b.baseArrayLayer) return false;
if (a.layerCount < b.layerCount) return true;
if (a.layerCount > b.layerCount) return false;
return false;
}
@@ -25,6 +25,17 @@
namespace filament::backend {
struct VulkanLayoutTransition {
VkImage image;
VkImageLayout oldLayout;
VkImageLayout newLayout;
VkImageSubresourceRange subresources;
VkPipelineStageFlags srcStage;
VkAccessFlags srcAccessMask;
VkPipelineStageFlags dstStage;
VkAccessFlags dstAccessMask;
};
void createSemaphore(VkDevice device, VkSemaphore* semaphore);
VkFormat getVkFormat(ElementType type, bool normalized, bool integer);
VkFormat getVkFormat(TextureFormat format);
@@ -38,8 +49,20 @@ VkFrontFace getFrontFace(bool inverseFrontFaces);
PixelDataType getComponentType(VkFormat format);
uint32_t getComponentCount(VkFormat format);
VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]);
VkImageViewType getImageViewType(SamplerType target);
VkImageLayout getDefaultImageLayout(TextureUsage usage);
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
void transitionImageLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition);
// Helper function for populating barrier fields based on the desired image layout.
// This logic is specific to blitting.
VulkanLayoutTransition blitterTransitionHelper(VulkanLayoutTransition transition);
// Helper function for populating barrier fields based on the desired image layout.
// This logic is specific to texturing.
VulkanLayoutTransition textureTransitionHelper(VulkanLayoutTransition transition);
bool equivalent(const VkRect2D& a, const VkRect2D& b);
bool equivalent(const VkExtent2D& a, const VkExtent2D& b);
bool isDepthFormat(VkFormat format);
@@ -89,4 +112,6 @@ utils::FixedCapacityVector<OutType> enumerate(
} // namespace filament::backend
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b);
#endif // TNT_FILAMENT_BACKEND_VULKANUTILITY_H
-28
View File
@@ -105,34 +105,6 @@ public:
*/
Builder& package(const void* payload, size_t size);
template<typename T>
using is_supported_constant_parameter_t = typename std::enable_if<
std::is_same<int32_t, T>::value ||
std::is_same<float, T>::value ||
std::is_same<bool, T>::value>::type;
/**
* Specialize a constant parameter specified in the material definition with a concrete
* value for this material. Once build() is called, this constant cannot be changed.
* Will throw an exception if the name does not match a constant specified in the
* material definition or if the type provided does not match.
*
* @tparam T The type of constant parameter, either int32_t, float, or bool.
* @param name The name of the constant parameter specified in the material definition, such
* as "myConstant".
* @param nameLength Length in `char` of the name parameter.
* @param value The value to use for the constant parameter, must match the type specified
* in the material definition.
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
Builder& constant(const char* name, size_t nameLength, T value);
/** inline helper to provide the constant name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
inline Builder& constant(const char* name, T value) {
return constant(name, strlen(name), value);
}
/**
* Creates the Material object and returns a pointer to it.
*
+11 -92
View File
@@ -21,8 +21,6 @@
#include <stdint.h>
#include <math.h>
namespace filament {
class Texture;
@@ -153,98 +151,19 @@ struct BloomOptions {
};
/**
* Options to control large-scale fog in the scene
* Options to control fog in the scene
*/
struct FogOptions {
/**
* Distance in world units [m] from the camera to where the fog starts ( >= 0.0 )
*/
float distance = 0.0f;
/**
* Distance in world units [m] after which the fog calculation is disabled.
* This can be used to exclude the skybox, which is desirable if it already contains clouds or
* fog. The default value is +infinity which applies the fog to everything.
*
* Note: The SkyBox is typically at a distance of 1e19 in world space (depending on the near
* plane distance and projection used though).
*/
float cutOffDistance = INFINITY;
/**
* fog's maximum opacity between 0 and 1
*/
float maximumOpacity = 1.0f;
/**
* Fog's floor in world units [m]. This sets the "sea level".
*/
float height = 0.0f;
/**
* How fast the fog dissipates with altitude. heightFalloff has a unit of [1/m].
* It can be expressed as 1/H, where H is the altitude change in world units [m] that causes a
* factor 2.78 (e) change in fog density.
*
* A falloff of 0 means the fog density is constant everywhere and may result is slightly
* faster computations.
*/
float heightFalloff = 1.0f;
/**
* Fog's color is used for ambient light in-scattering, a good value is
* to use the average of the ambient light, possibly tinted towards blue
* for outdoors environments. Color component's values should be between 0 and 1, values
* above one are allowed but could create a non energy-conservative fog (this is dependant
* on the IBL's intensity as well).
*
* We assume that our fog has no absorption and therefore all the light it scatters out
* becomes ambient light in-scattering and has lost all directionality, i.e.: scattering is
* isotropic. This somewhat simulates Rayleigh scattering.
*
* This value is used as a tint instead, when fogColorFromIbl is enabled.
*
* @see fogColorFromIbl
*/
LinearColor color = { 1.0f, 1.0f, 1.0f };
/**
* Extinction factor in [1/m] at altitude 'height'. The extinction factor controls how much
* light is absorbed and out-scattered per unit of distance. Each unit of extinction reduces
* the incoming light to 37% of its original value.
*
* Note: The extinction factor is related to the fog density, it's usually some constant K times
* the density at sea level (more specifically at fog height). The constant K depends on
* the composition of the fog/atmosphere.
*
* For historical reason this parameter is called `density`.
*/
float density = 0.1f;
/**
* Distance in world units [m] from the camera where the Sun in-scattering starts.
*/
float inScatteringStart = 0.0f;
/**
* Very inaccurately simulates the Sun's in-scattering. That is, the light from the sun that
* is scattered (by the fog) towards the camera.
* Size of the Sun in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
* Smaller values result is a larger scattering size.
*/
float inScatteringSize = -1.0f;
/**
* The fog color will be sampled from the IBL in the view direction and tinted by `color`.
* Depending on the scene this can produce very convincing results.
* This simulate a more anisotropic phase-function.
*/
bool fogColorFromIbl = false;
/**
* Enable or disable large-scale fog
*/
bool enabled = false;
float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
float height = 0.0f; //!< fog's floor in world units
float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
LinearColor color = {0.5f, 0.5f, 0.5f};//!< fog's color (linear), see fogColorFromIbl
float density = 0.1f; //!< fog's density at altitude given by 'height'
float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100).
bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction.
bool enabled = false; //!< enable or disable fog
};
/**
@@ -411,16 +411,14 @@ public:
/**
* Specifies the number of draw instance of this renderable. The default is 1 instance and
* the maximum number of instances allowed is 32767. 0 is invalid.
* the maximum number of instances allowed is 65535. 0 is invalid.
* All instances are culled using the same bounding box, so care must be taken to make
* sure all instances render inside the specified bounding box.
* The material must set its `instanced` parameter to `true` in order to use
* getInstanceIndex() in the vertex or fragment shader to get the instance index and
* possibly adjust the position or transform.
* It generally doesn't make sense to use VERTEX_DOMAIN_OBJECT in the material, since it
* would pull the same transform for all instances.
*
* @param instanceCount the number of instances silently clamped between 1 and 32767.
* @param instanceCount the number of instances silently clamped between 1 and 65535.
*/
Builder& instances(size_t instanceCount) noexcept;
+2 -2
View File
@@ -143,7 +143,7 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
while (ppp.l > 0) {
const size_t pi = QuadTreeUtils::parent(ppp.l, ppp.code);
ppp = NodeId{ int8_t(ppp.l - 1), uint8_t(ppp.code >> 2) };
Node const& node = mQuadTree[pi];
Node& node = mQuadTree[pi];
assert_invariant(!node.isAllocated());
assert_invariant(node.hasChildren());
}
@@ -154,7 +154,7 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
NodeId found{ -1, 0 };
QuadTree::traverse(candidate.l, candidate.code,
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
size_t const i = index(curr.l, curr.code);
size_t i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
if (curr.l == n) {
found = curr;
+9 -9
View File
@@ -35,14 +35,14 @@ float3 Color::linearToSRGB(float3 color) noexcept {
LinearColor Color::cct(float K) {
// temperature to CIE 1960
float const K2 = K * K;
float const u = (0.860117757f + 1.54118254e-4f * K + 1.28641212e-7f * K2) /
float K2 = K * K;
float u = (0.860117757f + 1.54118254e-4f * K + 1.28641212e-7f * K2) /
(1.0f + 8.42420235e-4f * K + 7.08145163e-7f * K2);
float const v = (0.317398726f + 4.22806245e-5f * K + 4.20481691e-8f * K2) /
float v = (0.317398726f + 4.22806245e-5f * K + 4.20481691e-8f * K2) /
(1.0f - 2.89741816e-5f * K + 1.61456053e-7f * K2);
float const d = 1.0f / (2.0f * u - 8.0f * v + 4.0f);
float3 const linear = XYZ_to_sRGB * xyY_to_XYZ({3.0f * u * d, 2.0f * v * d, 1.0f});
float d = 1.0f / (2.0f * u - 8.0f * v + 4.0f);
float3 linear = XYZ_to_sRGB * xyY_to_XYZ({3.0f * u * d, 2.0f * v * d, 1.0f});
// normalize and saturate
return saturate(linear / max(1e-5f, max(linear)));
}
@@ -50,13 +50,13 @@ LinearColor Color::cct(float K) {
LinearColor Color::illuminantD(float K) {
// temperature to xyY
const float iK = 1.0f / K;
float const iK2 = iK * iK;
float const x = K <= 7000.0f ?
float iK2 = iK * iK;
float x = K <= 7000.0f ?
0.244063f + 0.09911e3f * iK + 2.9678e6f * iK2 - 4.6070e9f * iK2 * iK :
0.237040f + 0.24748e3f * iK + 1.9018e6f * iK2 - 2.0064e9f * iK2 * iK;
float const y = -3.0f * x * x + 2.87f * x - 0.275f;
float y = -3.0f * x * x + 2.87f * x - 0.275f;
float3 const linear = XYZ_to_sRGB * xyY_to_XYZ({x, y, 1.0f});
float3 linear = XYZ_to_sRGB * xyY_to_XYZ({x, y, 1.0f});
// normalize and saturate
return saturate(linear / max(1e-5f, max(linear)));
}
+70 -70
View File
@@ -98,30 +98,30 @@ static float compute_max_saturation(float a, float b) noexcept {
// is close to infinite
// this should be sufficient for most applications, otherwise do two/three steps
float const k_l = +0.3963377774f * a + 0.2158037573f * b;
float const k_m = -0.1055613458f * a - 0.0638541728f * b;
float const k_s = -0.0894841775f * a - 1.2914855480f * b;
float k_l = +0.3963377774f * a + 0.2158037573f * b;
float k_m = -0.1055613458f * a - 0.0638541728f * b;
float k_s = -0.0894841775f * a - 1.2914855480f * b;
{
float const l_ = 1.f + S * k_l;
float const m_ = 1.f + S * k_m;
float const s_ = 1.f + S * k_s;
float l_ = 1.f + S * k_l;
float m_ = 1.f + S * k_m;
float s_ = 1.f + S * k_s;
float const l = l_ * l_ * l_;
float const m = m_ * m_ * m_;
float const s = s_ * s_ * s_;
float l = l_ * l_ * l_;
float m = m_ * m_ * m_;
float s = s_ * s_ * s_;
float const l_dS = 3.f * k_l * l_ * l_;
float const m_dS = 3.f * k_m * m_ * m_;
float const s_dS = 3.f * k_s * s_ * s_;
float l_dS = 3.f * k_l * l_ * l_;
float m_dS = 3.f * k_m * m_ * m_;
float s_dS = 3.f * k_s * s_ * s_;
float const l_dS2 = 6.f * k_l * k_l * l_;
float const m_dS2 = 6.f * k_m * k_m * m_;
float const s_dS2 = 6.f * k_s * k_s * s_;
float l_dS2 = 6.f * k_l * k_l * l_;
float m_dS2 = 6.f * k_m * k_m * m_;
float s_dS2 = 6.f * k_s * k_s * s_;
float const f = wl * l + wm * m + ws * s;
float const f1 = wl * l_dS + wm * m_dS + ws * s_dS;
float const f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
float f = wl * l + wm * m + ws * s;
float f1 = wl * l_dS + wm * m_dS + ws * s_dS;
float f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
S = S - f * f1 / (f1 * f1 - 0.5f * f * f2);
}
@@ -133,14 +133,14 @@ static float compute_max_saturation(float a, float b) noexcept {
// a and b must be normalized so a^2 + b^2 == 1
static float2 find_cusp(float a, float b) noexcept {
// First, find the maximum saturation (saturation S = C/L)
float const S_cusp = compute_max_saturation(a, b);
float S_cusp = compute_max_saturation(a, b);
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
float3 const rgb_at_max = OkLab_to_sRGB({1.0f, S_cusp * a, S_cusp * b});
float const L_cusp = std::cbrt(1.0f / max(rgb_at_max));
float const C_cusp = L_cusp * S_cusp;
float3 rgb_at_max = OkLab_to_sRGB({1.0f, S_cusp * a, S_cusp * b});
float L_cusp = std::cbrt(1.0f / max(rgb_at_max));
float C_cusp = L_cusp * S_cusp;
return { L_cusp, C_cusp };
return {L_cusp, C_cusp};
}
// Finds intersection of the line defined by
@@ -149,7 +149,7 @@ static float2 find_cusp(float a, float b) noexcept {
// a and b must be normalized so a^2 + b^2 == 1
static float find_gamut_intersection(float a, float b, float L1, float C1, float L0) noexcept {
// Find the cusp of the gamut triangle
float2 const cusp = find_cusp(a, b);
float2 cusp = find_cusp(a, b);
// Find the intersection for upper and lower half separately
float t;
@@ -164,59 +164,59 @@ static float find_gamut_intersection(float a, float b, float L1, float C1, float
// Then one step Halley's method
{
float const dL = L1 - L0;
float const dC = C1;
float dL = L1 - L0;
float dC = C1;
float const k_l = +0.3963377774f * a + 0.2158037573f * b;
float const k_m = -0.1055613458f * a - 0.0638541728f * b;
float const k_s = -0.0894841775f * a - 1.2914855480f * b;
float k_l = +0.3963377774f * a + 0.2158037573f * b;
float k_m = -0.1055613458f * a - 0.0638541728f * b;
float k_s = -0.0894841775f * a - 1.2914855480f * b;
float const l_dt = dL + dC * k_l;
float const m_dt = dL + dC * k_m;
float const s_dt = dL + dC * k_s;
float l_dt = dL + dC * k_l;
float m_dt = dL + dC * k_m;
float s_dt = dL + dC * k_s;
// If higher accuracy is required, 2 or 3 iterations of the
// following block can be used:
{
float const L = L0 * (1.f - t) + t * L1;
float const C = t * C1;
float L = L0 * (1.f - t) + t * L1;
float C = t * C1;
float const l_ = L + C * k_l;
float const m_ = L + C * k_m;
float const s_ = L + C * k_s;
float l_ = L + C * k_l;
float m_ = L + C * k_m;
float s_ = L + C * k_s;
float const l = l_ * l_ * l_;
float const m = m_ * m_ * m_;
float const s = s_ * s_ * s_;
float l = l_ * l_ * l_;
float m = m_ * m_ * m_;
float s = s_ * s_ * s_;
float const ldt = 3 * l_dt * l_ * l_;
float const mdt = 3 * m_dt * m_ * m_;
float const sdt = 3 * s_dt * s_ * s_;
float ldt = 3 * l_dt * l_ * l_;
float mdt = 3 * m_dt * m_ * m_;
float sdt = 3 * s_dt * s_ * s_;
float const ldt2 = 6 * l_dt * l_dt * l_;
float const mdt2 = 6 * m_dt * m_dt * m_;
float const sdt2 = 6 * s_dt * s_dt * s_;
float ldt2 = 6 * l_dt * l_dt * l_;
float mdt2 = 6 * m_dt * m_dt * m_;
float sdt2 = 6 * s_dt * s_dt * s_;
float const r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s - 1;
float const r1 = 4.0767416621f * ldt - 3.3077115913f * mdt + 0.2309699292f * sdt;
float const r2 = 4.0767416621f * ldt2 - 3.3077115913f * mdt2 + 0.2309699292f * sdt2;
float r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s - 1;
float r1 = 4.0767416621f * ldt - 3.3077115913f * mdt + 0.2309699292f * sdt;
float r2 = 4.0767416621f * ldt2 - 3.3077115913f * mdt2 + 0.2309699292f * sdt2;
float const u_r = r1 / (r1 * r1 - 0.5f * r * r2);
float u_r = r1 / (r1 * r1 - 0.5f * r * r2);
float t_r = -r * u_r;
float const g = -1.2681437731f * l + 2.6097574011f * m - 0.3413193965f * s - 1;
float const g1 = -1.2681437731f * ldt + 2.6097574011f * mdt - 0.3413193965f * sdt;
float const g2 = -1.2681437731f * ldt2 + 2.6097574011f * mdt2 - 0.3413193965f * sdt2;
float g = -1.2681437731f * l + 2.6097574011f * m - 0.3413193965f * s - 1;
float g1 = -1.2681437731f * ldt + 2.6097574011f * mdt - 0.3413193965f * sdt;
float g2 = -1.2681437731f * ldt2 + 2.6097574011f * mdt2 - 0.3413193965f * sdt2;
float const u_g = g1 / (g1 * g1 - 0.5f * g * g2);
float u_g = g1 / (g1 * g1 - 0.5f * g * g2);
float t_g = -g * u_g;
float const b0 = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s - 1;
float const b1 = -0.0041960863f * ldt - 0.7034186147f * mdt + 1.7076147010f * sdt;
float const b2 = -0.0041960863f * ldt2 - 0.7034186147f * mdt2 + 1.7076147010f * sdt2;
float b0 = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s - 1;
float b1 = -0.0041960863f * ldt - 0.7034186147f * mdt + 1.7076147010f * sdt;
float b2 = -0.0041960863f * ldt2 - 0.7034186147f * mdt2 + 1.7076147010f * sdt2;
float const u_b = b1 / (b1 * b1 - 0.5f * b0 * b2);
float u_b = b1 / (b1 * b1 - 0.5f * b0 * b2);
float t_b = -b0 * u_b;
t_r = u_r >= 0.f ? t_r : std::numeric_limits<float>::max();
@@ -247,21 +247,21 @@ inline float3 gamut_clip_adaptive_L0_0_5(float3 rgb,
return rgb;
}
float3 const lab = sRGB_to_OkLab(rgb);
float3 lab = sRGB_to_OkLab(rgb);
float const L = lab.x;
float const eps = 0.00001f;
float const C = max(eps, std::sqrt(lab.y * lab.y + lab.z * lab.z));
float const a_ = lab.y / C;
float const b_ = lab.z / C;
float L = lab.x;
float eps = 0.00001f;
float C = max(eps, std::sqrt(lab.y * lab.y + lab.z * lab.z));
float a_ = lab.y / C;
float b_ = lab.z / C;
float const Ld = L - 0.5f;
float const e1 = 0.5f + std::abs(Ld) + alpha * C;
float const L0 = 0.5f * (1.0f + sgn(Ld) * (e1 - std::sqrt(e1 * e1 - 2.0f * std::abs(Ld))));
float Ld = L - 0.5f;
float e1 = 0.5f + std::abs(Ld) + alpha * C;
float L0 = 0.5f * (1.0f + sgn(Ld) * (e1 - std::sqrt(e1 * e1 - 2.0f * std::abs(Ld))));
float const t = find_gamut_intersection(a_, b_, L, C, L0);
float const L_clipped = L0 * (1.f - t) + t * L;
float const C_clipped = t * C;
float t = find_gamut_intersection(a_, b_, L, C, L0);
float L_clipped = L0 * (1.f - t) + t * L;
float C_clipped = t * C;
return OkLab_to_sRGB({L_clipped, C_clipped * a_, C_clipped * b_});
}
-2
View File
@@ -560,8 +560,6 @@ void Froxelizer::froxelizeLoop(FEngine& engine,
spheres, directions, instances, &viewMatrix, &lcm ]
(size_t count, size_t offset, size_t stride) {
SYSTRACE_NAME("FroxelizeLoop Job");
const mat4f& projection = mProjection;
const mat3f& vn = viewMatrix.upperLeft();
+1 -41
View File
@@ -29,7 +29,6 @@
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <private/filament/Variant.h>
#include <private/filament/ConstantInfo.h>
#include <utils/CString.h>
@@ -178,13 +177,6 @@ bool MaterialParser::getSamplerBlockBindings(
pSamplerGroupInfoList, pSamplerBindingToNameMap);
}
bool MaterialParser::getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept {
auto [start, end] = mImpl.mChunkContainer.getChunkRange(filamat::MaterialConstants);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkMaterialConstants::unflatten(unflattener, value);
}
bool MaterialParser::getDepthWriteSet(bool* value) const noexcept {
return mImpl.getFromSimpleChunk(ChunkType::MaterialDepthWriteSet, value);
}
@@ -319,7 +311,7 @@ bool MaterialParser::getReflectionMode(ReflectionMode* value) const noexcept {
bool MaterialParser::getShader(ShaderContent& shader,
ShaderModel shaderModel, Variant variant, ShaderStage stage) noexcept {
return mImpl.mMaterialChunk.getShader(shader,
mImpl.mBlobDictionary, shaderModel, variant, stage);
mImpl.mBlobDictionary, uint8_t(shaderModel), variant, uint8_t(stage));
}
// ------------------------------------------------------------------------------------------------
@@ -554,36 +546,4 @@ bool ChunkSamplerBlockBindings::unflatten(Unflattener& unflattener,
return true;
}
bool ChunkMaterialConstants::unflatten(filaflat::Unflattener& unflattener,
utils::FixedCapacityVector<MaterialConstant>* materialConstants) {
assert_invariant(materialConstants);
// Read number of constants.
uint64_t numConstants = 0;
if (!unflattener.read(&numConstants)) {
return false;
}
materialConstants->reserve(numConstants);
materialConstants->resize(numConstants);
for (uint64_t i = 0; i < numConstants; i++) {
CString constantName;
uint8_t constantType = 0;
if (!unflattener.read(&constantName)) {
return false;
}
if (!unflattener.read(&constantType)) {
return false;
}
(*materialConstants)[i].name = constantName;
(*materialConstants)[i].type = static_cast<backend::ConstantType>(constantType);
}
return true;
}
} // namespace filament
-9
View File
@@ -43,7 +43,6 @@ namespace filament {
class BufferInterfaceBlock;
class SamplerInterfaceBlock;
struct SubpassInfo;
struct MaterialConstant;
class MaterialParser {
public:
@@ -72,7 +71,6 @@ public:
bool getUniformBlockBindings(utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>* value) const noexcept;
bool getSamplerBlockBindings(SamplerGroupBindingInfoList* pSamplerGroupInfoList,
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept;
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
bool getDepthWriteSet(bool* value) const noexcept;
bool getDepthWrite(bool* value) const noexcept;
@@ -105,8 +103,6 @@ public:
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
Variant variant, backend::ShaderStage stage) noexcept;
filaflat::MaterialChunk const& getMaterialChunk() const noexcept { return mImpl.mMaterialChunk; }
private:
struct MaterialParserDetails {
MaterialParserDetails(backend::Backend backend, const void* data, size_t size);
@@ -172,11 +168,6 @@ struct ChunkSamplerBlockBindings {
SamplerBindingToNameMap* pSamplerBindingToNameMap);
};
struct ChunkMaterialConstants {
static bool unflatten(filaflat::Unflattener& unflattener,
utils::FixedCapacityVector<MaterialConstant>* materialConstants);
};
} // namespace filament
#endif // TNT_FILAMENT_MATERIALPARSER_H
-1
View File
@@ -139,7 +139,6 @@ void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const&
s.fogMaxOpacity = options.maximumOpacity;
s.fogHeight = options.height;
s.fogHeightFalloff = heightFalloff;
s.fogCutOffDistance = options.cutOffDistance;
s.fogColor = options.color;
s.fogDensity = { options.density, density, options.density * std::exp(density) };
s.fogInscatteringStart = options.inScatteringStart;
+53 -92
View File
@@ -78,7 +78,7 @@ PostProcessManager::PostProcessMaterial::PostProcessMaterial() noexcept {
mData = nullptr;
}
PostProcessManager::PostProcessMaterial::PostProcessMaterial(FEngine&,
PostProcessManager::PostProcessMaterial::PostProcessMaterial(FEngine& engine,
uint8_t const* data, int size) noexcept
: PostProcessMaterial() {
mData = data; // aliased to mMaterial
@@ -241,7 +241,6 @@ static const MaterialInfo sMaterialList[] = {
{ "fsr_easu_mobile", MATERIAL(FSR_EASU_MOBILE) },
{ "fsr_easu_mobileF", MATERIAL(FSR_EASU_MOBILEF) },
{ "fsr_rcas", MATERIAL(FSR_RCAS) },
{ "debugShadowCascades", MATERIAL(DEBUGSHADOWCASCADES) },
};
void PostProcessManager::init() noexcept {
@@ -270,7 +269,7 @@ void PostProcessManager::init() noexcept {
PixelBufferDescriptor dataStarburst(driver.allocate(256), 256, PixelDataFormat::R, PixelDataType::UBYTE);
std::generate_n((uint8_t*)dataStarburst.buffer, 256,
[&dist = mUniformDistribution, &gen = mEngine.getRandomEngine()]() {
float const r = 0.5f + 0.5f * dist(gen);
float r = 0.5f + 0.5f * dist(gen);
return uint8_t(r * 255.0f);
});
@@ -316,10 +315,10 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out,
&& !pipeline.rasterState.depthWrite)
|| !(out.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH));
FEngine const& engine = mEngine;
Handle<HwRenderPrimitive> const fullScreenQuad = engine.getFullScreenRenderPrimitive();
FEngine& engine = mEngine;
Handle<HwRenderPrimitive> fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, fullScreenQuad, 1);
driver.draw(pipeline, fullScreenRenderPrimitive, 1);
driver.endRenderPass();
}
@@ -357,8 +356,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
};
// sanitize a bit the user provided scaling factor
width = std::max(32u, (uint32_t)std::ceil(float(width) * scale));
height = std::max(32u, (uint32_t)std::ceil(float(height) * scale));
width = std::max(32u, (uint32_t)std::ceil(width * scale));
height = std::max(32u, (uint32_t)std::ceil(height * scale));
// We limit the lowest lod size to 32 pixels (which is where the -5 comes from)
const size_t levelCount = std::min(8, FTexture::maxLevelCount(width, height) - 5);
@@ -393,7 +392,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
});
},
[=, renderPass = pass](FrameGraphResources const& resources,
auto const&, DriverApi&) mutable {
auto const& data, DriverApi& driver) mutable {
Variant structureVariant(Variant::DEPTH_VARIANT);
structureVariant.setPicking(config.picking);
@@ -523,12 +522,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
resources.getTexture(data.structure) : getOneTexture());
// set screen-space reflections and screen-space refractions
mat4f const uvFromViewMatrix = uvFromClipMatrix * projection;
mat4f const reprojection = mat4f{ uvFromClipMatrix * historyProjection
mat4f uvFromViewMatrix = uvFromClipMatrix * projection;
mat4f reprojection = mat4f{ uvFromClipMatrix * historyProjection
* inverse(userViewMatrix) };
// the history sampler is a regular texture2D
TextureHandle const history = data.history ?
TextureHandle history = data.history ?
resources.getTexture(data.history) : getZeroTexture();
uniforms.prepareHistorySSR(history, reprojection, uvFromViewMatrix, options);
@@ -554,7 +553,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
}
FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(FrameGraph& fg,
filament::Viewport const&, const CameraInfo& cameraInfo,
filament::Viewport const& svp, const CameraInfo& cameraInfo,
FrameGraphId<FrameGraphTexture> depth,
AmbientOcclusionOptions const& options) noexcept {
assert_invariant(depth);
@@ -799,7 +798,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
float2{ options.ssct.depthBias, options.ssct.depthSlopeBias });
mi->setParameter("ssctSampleCount", uint32_t(options.ssct.sampleCount));
mi->setParameter("ssctRayCount",
float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) });
float2{ options.ssct.rayCount, 1.0f / options.ssct.rayCount });
mi->commit(driver);
mi->use(driver);
@@ -895,15 +894,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
[kernelArraySize](float* outKernel, size_t gaussianWidth, float stdDev) -> uint32_t {
const size_t gaussianSampleCount = std::min(kernelArraySize, (gaussianWidth + 1u) / 2u);
for (size_t i = 0; i < gaussianSampleCount; i++) {
float const x = float(i);
float const g = std::exp(-(x * x) / (2.0f * stdDev * stdDev));
float x = i;
float g = std::exp(-(x * x) / (2.0f * stdDev * stdDev));
outKernel[i] = g;
}
return uint32_t(gaussianSampleCount);
};
float kGaussianSamples[kernelArraySize];
uint32_t const kGaussianCount = gaussianKernel(kGaussianSamples,
uint32_t kGaussianCount = gaussianKernel(kGaussianSamples,
config.kernelSize, config.standardDeviation);
auto& material = config.bentNormals ?
@@ -987,10 +986,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
float totalWeight = kernel[0].x;
for (size_t i = 1; i < m; i++) {
float const x0 = float(i * 2 - 1);
float const x1 = float(i * 2);
float const k0 = std::exp(-alpha * x0 * x0);
float const k1 = std::exp(-alpha * x1 * x1);
float x0 = float(i * 2 - 1);
float x1 = float(i * 2);
float k0 = std::exp(-alpha * x0 * x0);
float k1 = std::exp(-alpha * x1 * x1);
// k * textureLod(..., o) with bilinear sampling is equivalent to:
// k * (s[0] * (1 - o) + s[1] * o)
@@ -998,8 +997,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
// k0 = k * (1 - o)
// k1 = k * o
float const k = k0 + k1;
float const o = k1 / k;
float k = k0 + k1;
float o = k1 / k;
kernel[i].x = k;
kernel[i].y = o;
totalWeight += (k0 + k1) * 2.0f;
@@ -1079,7 +1078,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
const size_t kernelStorageSize = mi->getMaterial()->reflect("kernel")->size;
float2 kernel[64];
size_t const m = computeGaussianCoefficients(kernel,
size_t m = computeGaussianCoefficients(kernel,
std::min(sizeof(kernel) / sizeof(*kernel), kernelStorageSize));
// horizontal pass
@@ -1509,9 +1508,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("cocClamp", float2{
-(dofOptions.maxForegroundCOC ? dofOptions.maxForegroundCOC : DOF_DEFAULT_MAX_COC),
dofOptions.maxBackgroundCOC ? dofOptions.maxBackgroundCOC : DOF_DEFAULT_MAX_COC});
mi->setParameter("texelSize", float2{
1.0f / float(colorDesc.width),
1.0f / float(colorDesc.height) });
mi->setParameter("texelSize", float2{ 1.0f / colorDesc.width, 1.0f / colorDesc.height });
commitAndRender(out, material, driver);
});
@@ -1734,7 +1731,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
bokehAspectRatio / (inputDesc.width * dofResolution),
1.0 / (inputDesc.height * dofResolution)
});
mi->setParameter("cocToPixelScale", (1.0f / float(dofResolution)));
mi->setParameter("cocToPixelScale", (1.0f / dofResolution));
mi->setParameter("ringCounts", float4{
dofOptions.foregroundRingCount ? dofOptions.foregroundRingCount : DOF_DEFAULT_RING_COUNT,
dofOptions.backgroundRingCount ? dofOptions.backgroundRingCount : DOF_DEFAULT_RING_COUNT,
@@ -2004,10 +2001,10 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
{ .level = uint8_t(i) });
auto staging = builder.createSubresource(data.stage,
auto stage = builder.createSubresource(data.stage,
"Bloom Stage Texture mip", { .level = uint8_t(i) });
builder.declareRenderPass(out, &data.outRT[i]);
builder.declareRenderPass(staging, &data.stageRT[i]);
builder.declareRenderPass(stage, &data.stageRT[i]);
}
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
@@ -2098,7 +2095,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::flarePass(FrameGraph& fg,
auto const& data, DriverApi& driver) {
auto in = resources.getTexture(data.in);
auto out = resources.getRenderPassInfo(0);
const float aspectRatio = float(width) / float(height);
const float aspectRatio = float(width) / height;
auto const& material = getPostProcessMaterial("flare");
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
@@ -2134,21 +2131,21 @@ static float4 getVignetteParameters(VignetteOptions const& options,
// Vignette params
// From 0.0 to 0.5 the vignette is a rounded rect that turns into an oval
// From 0.5 to 1.0 the vignette turns from oval to circle
float const oval = min(options.roundness, 0.5f) * 2.0f;
float const circle = (max(options.roundness, 0.5f) - 0.5f) * 2.0f;
float const roundness = (1.0f - oval) * 6.0f + oval;
float oval = min(options.roundness, 0.5f) * 2.0f;
float circle = (max(options.roundness, 0.5f) - 0.5f) * 2.0f;
float roundness = (1.0f - oval) * 6.0f + oval;
// Mid point varies during the oval/rounded section of roundness
// We also modify it to emphasize feathering
float const midPoint = (1.0f - options.midPoint) * mix(2.2f, 3.0f, oval)
float midPoint = (1.0f - options.midPoint) * mix(2.2f, 3.0f, oval)
* (1.0f - 0.1f * options.feather);
// Radius of the rounded corners as a param to pow()
float const radius = roundness *
float radius = roundness *
mix(1.0f + 4.0f * (1.0f - options.feather), 1.0f, std::sqrt(oval));
// Factor to transform oval into circle
float const aspect = mix(1.0f, float(width) / float(height), circle);
float aspect = mix(1.0f, float(width) / float(height), circle);
return float4{ midPoint, radius, aspect, options.feather };
}
@@ -2161,7 +2158,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
const FColorGrading* colorGrading, ColorGradingConfig const& colorGradingConfig,
VignetteOptions const& vignetteOptions, uint32_t width, uint32_t height) noexcept {
float4 const vignetteParameters = getVignetteParameters(vignetteOptions, width, height);
float4 vignetteParameters = getVignetteParameters(vignetteOptions, width, height);
auto const& material = getPostProcessMaterial("colorGradingAsSubpass");
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
@@ -2195,7 +2192,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
void PostProcessManager::colorGradingSubpass(DriverApi& driver,
ColorGradingConfig const& colorGradingConfig) noexcept {
FEngine const& engine = mEngine;
FEngine& engine = mEngine;
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
auto const& material = getPostProcessMaterial("colorGradingAsSubpass");
@@ -2218,7 +2215,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
}
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
FEngine const& engine = mEngine;
FEngine& engine = mEngine;
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
// the UBO has been set and committed in colorGradingPrepareSubpass()
@@ -2241,7 +2238,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
.attachments = { .color = { data.inout }}
});
},
[=](FrameGraphResources const& resources, auto const&, DriverApi& driver) {
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
customResolvePrepareSubpass(driver, CustomResolveOp::UNCOMPRESS);
auto out = resources.getRenderPassInfo();
out.params.subpassMask = 1;
@@ -2269,7 +2266,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
bloomStrength = clamp(bloomOptions.strength, 0.0f, 1.0f);
if (bloomOptions.dirt) {
FTexture* fdirt = downcast(bloomOptions.dirt);
FrameGraphTexture const frameGraphTexture{ .handle = fdirt->getHwHandle() };
FrameGraphTexture frameGraphTexture{ .handle = fdirt->getHwHandle() };
bloomDirt = fg.import("dirt", {
.width = (uint32_t)fdirt->getWidth(0u),
.height = (uint32_t)fdirt->getHeight(0u),
@@ -2318,18 +2315,18 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
}
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto colorTexture = resources.getTexture(data.input);
Handle<HwTexture> colorTexture = resources.getTexture(data.input);
auto bloomTexture =
Handle<HwTexture> bloomTexture =
data.bloom ? resources.getTexture(data.bloom) : getZeroTexture();
auto flareTexture =
Handle<HwTexture> flareTexture =
data.flare ? resources.getTexture(data.flare) : getZeroTexture();
auto dirtTexture =
Handle<HwTexture> dirtTexture =
data.dirt ? resources.getTexture(data.dirt) : getOneTexture();
auto starburstTexture =
Handle<HwTexture> starburstTexture =
data.starburst ? resources.getTexture(data.starburst) : getOneTexture();
auto const& out = resources.getRenderPassInfo();
@@ -2378,7 +2375,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
auto const& input = resources.getDescriptor(data.input);
auto const& output = resources.getDescriptor(data.output);
float4 const vignetteParameters = getVignetteParameters(
float4 vignetteParameters = getVignetteParameters(
vignetteOptions, output.width, output.height);
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
@@ -2466,10 +2463,10 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
current.frameId = previous.frameId + 1;
// sample position within a pixel [-0.5, 0.5]
float2 const jitter = halton(previous.frameId) - 0.5f;
const float2 jitter = halton(previous.frameId) - 0.5f;
current.jitter = jitter;
float2 const jitterInClipSpace = jitter * (2.0f / float2{ svp.width, svp.height });
float2 jitterInClipSpace = jitter * (2.0f / float2{ svp.width, svp.height });
// update projection matrix
inoutCameraInfo->projection[2].xy -= jitterInClipSpace;
@@ -2599,7 +2596,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
if (colorGradingConfig.asSubpass) {
out.params.subpassMask = 1;
}
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
PipelineState pipeline(material.getPipelineState(mEngine, variant));
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 1);
if (colorGradingConfig.asSubpass) {
@@ -2661,7 +2658,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
.viewport = vp
});
},
[=](FrameGraphResources const& resources, auto const&, DriverApi& driver) {
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto out = resources.getRenderPassInfo(0);
auto in = resources.getRenderPassInfo(1);
driver.blit(TargetBufferFlags::COLOR,
@@ -2782,7 +2779,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
{ // just a scope to not leak local variables
const std::string_view blitterNames[4] = {
"blitLow", "fsr_easu_mobile", "fsr_easu_mobile", "fsr_easu" };
unsigned const index = std::min(3u, (unsigned)dsrOptions.quality);
unsigned index = std::min(3u, (unsigned)dsrOptions.quality);
easuMaterial = &getPostProcessMaterial(blitterNames[index]);
auto* mi = easuMaterial->getMaterialInstance(mEngine);
if (dsrOptions.quality != QualityLevel::LOW) {
@@ -2868,7 +2865,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
const uint8_t variant = uint8_t(translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
PipelineState pipeline(material.getPipelineState(mEngine, variant));
render(out, pipeline, driver);
});
@@ -2994,7 +2991,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
auto const& inDesc = resources.getDescriptor(data.in);
auto width = inDesc.width;
assert_invariant(width == inDesc.height);
int const dim = width >> (level + 1);
int dim = width >> (level + 1);
driver.setMinMaxLevels(in, level, level);
@@ -3012,7 +3009,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
});
mi->setParameter("level", uint32_t(level));
mi->setParameter("layer", uint32_t(layer));
mi->setParameter("uvscale", 1.0f / float(dim));
mi->setParameter("uvscale", 1.0f / dim);
mi->commit(driver);
mi->use(driver);
render(out, pipeline, driver);
@@ -3025,40 +3022,4 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
return depthMipmapPass->in;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> depth) noexcept {
// new pass for showing the cascades
struct DebugShadowCascadesData {
FrameGraphId<FrameGraphTexture> color;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> output;
};
auto& debugShadowCascadePass = fg.addPass<DebugShadowCascadesData>("ShadowCascades",
[&](FrameGraph::Builder& builder, auto& data) {
auto desc = builder.getDescriptor(input);
data.color = builder.sample(input);
data.depth = builder.sample(depth);
data.output = builder.createTexture("Shadow Cascade Debug", desc);
builder.declareRenderPass(data.output);
},
[=](FrameGraphResources const& resources, auto const& data, backend::DriverApi& driver) {
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
auto out = resources.getRenderPassInfo();
auto& material = getPostProcessMaterial("debugShadowCascades");
PipelineState const pipeline(material.getPipelineState(mEngine));
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {}); // nearest
mi->setParameter("depth", depth, {}); // nearest
mi->commit(driver);
mi->use(driver);
render(out, pipeline, driver);
});
return debugShadowCascadePass->output;
}
} // namespace filament
-4
View File
@@ -242,10 +242,6 @@ public:
FrameGraphId<FrameGraphTexture> output,
bool reinhard, size_t kernelWidth, float sigma) noexcept;
FrameGraphId<FrameGraphTexture> debugShadowCascades(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> depth) noexcept;
backend::Handle<backend::HwTexture> getOneTexture() const;
backend::Handle<backend::HwTexture> getZeroTexture() const;
backend::Handle<backend::HwTexture> getOneTextureArray() const;
+11 -16
View File
@@ -334,9 +334,9 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
keyBlending |= uint64_t(Pass::BLENDED);
keyBlending |= uint64_t(CustomCommand::PASS);
BlendingMode const blendingMode = ma->getBlendingMode();
bool const hasScreenSpaceRefraction = ma->getRefractionMode() == RefractionMode::SCREEN_SPACE;
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
BlendingMode blendingMode = ma->getBlendingMode();
bool hasScreenSpaceRefraction = ma->getRefractionMode() == RefractionMode::SCREEN_SPACE;
bool isBlendingCommand = !hasScreenSpaceRefraction &&
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
uint64_t keyDraw = cmdDraw.key;
@@ -516,7 +516,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
cmdColor.primitive.index = (uint16_t)i;
cmdColor.primitive.instanceCount = soaInstanceCount[i] | PrimitiveInfo::USER_INSTANCE_MASK;
cmdColor.primitive.instanceCount = soaInstanceCount[i];
// if we are already an SSR variant, the SRE bit is already set,
// there is no harm setting it again
@@ -532,7 +532,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
cmdDepth.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
cmdDepth.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
cmdDepth.primitive.index = (uint16_t)i;
cmdDepth.primitive.instanceCount = soaInstanceCount[i] | PrimitiveInfo::USER_INSTANCE_MASK;
cmdDepth.primitive.instanceCount = soaInstanceCount[i];
cmdDepth.primitive.materialVariant.setSkinning(hasSkinningOrMorphing);
cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
}
@@ -691,7 +691,7 @@ void RenderPass::updateSummedPrimitiveCounts(
auto const* const UTILS_RESTRICT primitives = renderableData.data<FScene::PRIMITIVES>();
uint32_t* const UTILS_RESTRICT summedPrimitiveCount = renderableData.data<FScene::SUMMED_PRIMITIVE_COUNT>();
uint32_t count = 0;
for (uint32_t const i : vr) {
for (uint32_t i : vr) {
summedPrimitiveCount[i] = count;
count += primitives[i].size();
}
@@ -718,7 +718,7 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noe
mScissor = scissor;
}
void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept {
void RenderPass::Executor::execute(FEngine& engine, const char* name) const noexcept {
execute(engine.getDriverApi(), mCommands.begin(), mCommands.end());
}
@@ -742,7 +742,7 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
auto* const pScissor =
mScissorOverride ? &dummyPipeline.scissor : &pipeline.scissor;
Handle<HwBufferObject> const uboHandle = mUboHandle;
Handle<HwBufferObject> uboHandle = mUboHandle;
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
FMaterial const* UTILS_RESTRICT ma = nullptr;
auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data();
@@ -756,7 +756,7 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
*/
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size());
pCustomCommands[index]();
continue;
@@ -812,14 +812,9 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
// bind per-renderable uniform block. there is no need to attempt to skip this command
// because the backends already do this.
bool const userInstancing = (info.instanceCount & PrimitiveInfo::USER_INSTANCE_MASK) != 0u;
uint16_t const instanceCount = info.instanceCount & PrimitiveInfo::INSTANCE_COUNT_MASK;
auto const perObjectUboHandle = (!userInstancing && instanceCount > 1)
? mInstancedUboHandle : uboHandle;
assert_invariant(perObjectUboHandle);
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE,
perObjectUboHandle,
(info.instanceCount > 1) ? mInstancedUboHandle : uboHandle,
info.index * sizeof(PerRenderableData),
sizeof(PerRenderableUib));
@@ -844,7 +839,7 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
info.morphTargetBuffer);
}
driver.draw(pipeline, info.primitiveHandle, instanceCount);
driver.draw(pipeline, info.primitiveHandle, info.instanceCount);
}
}
+2 -5
View File
@@ -203,7 +203,7 @@ public:
* The variant is inserted while building the commands, because we don't know it before that
*/
static CommandKey makeMaterialSortingKey(uint32_t materialId, uint32_t instanceId) noexcept {
CommandKey const key = ((materialId << MATERIAL_ID_SHIFT) & MATERIAL_ID_MASK) |
CommandKey key = ((materialId << MATERIAL_ID_SHIFT) & MATERIAL_ID_MASK) |
((instanceId << MATERIAL_INSTANCE_ID_SHIFT) & MATERIAL_INSTANCE_ID_MASK);
return (key << MATERIAL_SHIFT) & MATERIAL_MASK;
}
@@ -236,12 +236,9 @@ public:
backend::Handle<backend::HwSamplerGroup> morphTargetBuffer; // 4 bytes
uint32_t index = 0; // 4 bytes
uint32_t skinningOffset = 0; // 4 bytes
uint16_t instanceCount; // 2 bytes [MSb: user]
uint16_t instanceCount; // 2 bytes
Variant materialVariant; // 1 byte
uint8_t reserved[1] = {}; // 1 byte
static const uint16_t USER_INSTANCE_MASK = 0x8000u;
static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu;
};
static_assert(sizeof(PrimitiveInfo) == 40);
+8 -9
View File
@@ -70,7 +70,7 @@ void ShadowMapManager::terminate(FEngine& engine) {
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept {
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
ShadowTechnique shadowTechnique = {};
calculateTextureRequirements(engine, view, lightData);
@@ -436,7 +436,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept {
FScene::LightSoa& lightData, ShadowMap::SceneInfo sceneInfo) noexcept {
FScene* scene = view.getScene();
auto& lcm = engine.getLightManager();
@@ -585,6 +585,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
}
uint32_t cascades = 0;
if (engine.debug.shadowmap.visualize_cascades) {
cascades |= 0x10u;
}
cascades |= uint32_t(mCascadeShadowMaps.size());
cascades |= cascadeHasVisibleShadows << 8u;
@@ -785,13 +788,9 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
FScene::LightSoa const& lightData) noexcept {
FScene::LightSoa& lightData) noexcept {
// The const_cast here is a little ugly, but conceptually lightData should be const,
// it's just that we're using it to store some temporary data. with SoA we can't have
// a `mutable` element, so that's a workaround.
FScene::ShadowInfo* const shadowInfo = const_cast<FScene::ShadowInfo*>(
lightData.data<FScene::SHADOW_INFO>());
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
ShadowTechnique shadowTechnique{};
if (!mSpotShadowMaps.empty()) {
@@ -825,7 +824,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
}
void ShadowMapManager::calculateTextureRequirements(FEngine&, FView& view,
FScene::LightSoa const&) noexcept {
FScene::LightSoa&) noexcept {
// Lay out the shadow maps. For now, we take the largest requested dimension and allocate a
// texture of that size. Each cascade / shadow map gets its own layer in the array texture.
+5 -5
View File
@@ -83,7 +83,7 @@ public:
// Returns true if any of the shadow maps have visible shadows.
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept;
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
// Renders all the shadow maps.
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
@@ -110,13 +110,13 @@ public:
private:
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
FScene::LightSoa& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
FScene::LightSoa const& lightData) noexcept;
FScene::LightSoa& lightData) noexcept;
void calculateTextureRequirements(FEngine&, FView& view,
FScene::LightSoa const&) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view,
FScene::LightSoa& lightData) noexcept;
void prepareSpotShadowMap(ShadowMap& shadowMap,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
+3 -3
View File
@@ -45,7 +45,7 @@ void FCameraManager::terminate() noexcept {
<< " leaked Camera components" << io::endl;
#endif
while (!manager.empty()) {
Instance const ci = manager.end() - 1;
Instance ci = manager.end() - 1;
destroy(manager.getEntity(ci));
}
}
@@ -65,7 +65,7 @@ FCamera* FCameraManager::create(Entity entity) {
if (UTILS_UNLIKELY(manager.hasComponent(entity))) {
destroy(entity);
}
Instance const i = manager.addComponent(entity);
Instance i = manager.addComponent(entity);
FCamera* camera = engine.getHeapAllocator().make<FCamera>(engine, entity);
manager.elementAt<CAMERA>(i) = camera;
@@ -80,7 +80,7 @@ FCamera* FCameraManager::create(Entity entity) {
void FCameraManager::destroy(Entity e) noexcept {
auto& manager = mManager;
Instance const i = manager.getInstance(e);
Instance i = manager.getInstance(e);
if (i) {
FCamera* camera = manager.elementAt<CAMERA>(i);
assert_invariant(camera);
+22 -22
View File
@@ -163,7 +163,7 @@ FLightManager::~FLightManager() {
assert_invariant(mManager.getComponentCount() == 0);
}
void FLightManager::init(FEngine&) noexcept {
void FLightManager::init(FEngine& engine) noexcept {
}
void FLightManager::create(const FLightManager::Builder& builder, utils::Entity entity) {
@@ -172,7 +172,7 @@ void FLightManager::create(const FLightManager::Builder& builder, utils::Entity
if (UTILS_UNLIKELY(manager.hasComponent(entity))) {
destroy(entity);
}
Instance const i = manager.addComponent(entity);
Instance i = manager.addComponent(entity);
assert_invariant(i);
if (i) {
@@ -202,11 +202,11 @@ void FLightManager::create(const FLightManager::Builder& builder, utils::Entity
}
}
void FLightManager::prepare(backend::DriverApi&) const noexcept {
void FLightManager::prepare(backend::DriverApi& driver) const noexcept {
}
void FLightManager::destroy(utils::Entity e) noexcept {
Instance const i = getInstance(e);
Instance i = getInstance(e);
if (i) {
auto& manager = mManager;
manager.removeComponent(e);
@@ -221,7 +221,7 @@ void FLightManager::terminate() noexcept {
<< " leaked Light components" << io::endl;
#endif
while (!manager.empty()) {
Instance const ci = manager.end() - 1;
Instance ci = manager.end() - 1;
manager.removeComponent(manager.getEntity(ci));
}
}
@@ -289,7 +289,7 @@ void FLightManager::setColor(Instance i, const LinearColor& color) noexcept {
void FLightManager::setIntensity(Instance i, float intensity, IntensityUnit unit) noexcept {
auto& manager = mManager;
if (i) {
Type const type = getLightType(i).type;
Type type = getLightType(i).type;
float luminousPower = intensity;
float luminousIntensity;
switch (type) {
@@ -312,7 +312,7 @@ void FLightManager::setIntensity(Instance i, float intensity, IntensityUnit unit
case Type::FOCUSED_SPOT: {
SpotParams& spotParams = manager[i].spotParams;
float const cosOuter = std::sqrt(spotParams.cosOuterSquared);
float cosOuter = std::sqrt(spotParams.cosOuterSquared);
if (unit == IntensityUnit::LUMEN_LUX) {
// li = lp / (2 * pi * (1 - cos(cone_outer / 2)))
luminousIntensity = luminousPower / (f::TAU * (1.0f - cosOuter));
@@ -344,7 +344,7 @@ void FLightManager::setIntensity(Instance i, float intensity, IntensityUnit unit
void FLightManager::setFalloff(Instance i, float falloff) noexcept {
auto& manager = mManager;
if (i && !isDirectionalLight(i)) {
float const sqFalloff = falloff * falloff;
float sqFalloff = falloff * falloff;
SpotParams& spotParams = manager[i].spotParams;
manager[i].squaredFallOffInv = sqFalloff > 0.0f ? (1 / sqFalloff) : 0;
spotParams.radius = falloff;
@@ -356,16 +356,16 @@ void FLightManager::setSpotLightCone(Instance i, float inner, float outer) noexc
if (i && isSpotLight(i)) {
// clamp the inner/outer angles to [0.5 degrees, 90 degrees]
float innerClamped = std::clamp(std::abs(inner), 0.5f * f::DEG_TO_RAD, f::PI_2);
float const outerClamped = std::clamp(std::abs(outer), 0.5f * f::DEG_TO_RAD, f::PI_2);
float outerClamped = std::clamp(std::abs(outer), 0.5f * f::DEG_TO_RAD, f::PI_2);
// inner must always be smaller than outer
innerClamped = std::min(innerClamped, outerClamped);
float const cosOuter = fast::cos(outerClamped);
float const cosInner = fast::cos(innerClamped);
float const cosOuterSquared = cosOuter * cosOuter;
float const scale = 1.0f / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float const offset = -cosOuter * scale;
float cosOuter = fast::cos(outerClamped);
float cosInner = fast::cos(innerClamped);
float cosOuterSquared = cosOuter * cosOuter;
float scale = 1.0f / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float offset = -cosOuter * scale;
SpotParams& spotParams = manager[i].spotParams;
spotParams.outerClamped = outerClamped;
@@ -374,11 +374,11 @@ void FLightManager::setSpotLightCone(Instance i, float inner, float outer) noexc
spotParams.scaleOffset = { scale, offset };
// we need to recompute the luminous intensity
Type const type = getLightType(i).type;
Type type = getLightType(i).type;
if (type == Type::FOCUSED_SPOT) {
// li = lp / (2 * pi * (1 - cos(cone_outer / 2)))
float const luminousPower = spotParams.luminousPower;
float const luminousIntensity = luminousPower / (f::TAU * (1.0f - cosOuter));
float luminousPower = spotParams.luminousPower;
float luminousIntensity = luminousPower / (f::TAU * (1.0f - cosOuter));
manager[i].intensity = luminousIntensity;
}
}
@@ -412,9 +412,9 @@ void FLightManager::setShadowCaster(Instance i, bool shadowCaster) noexcept {
float FLightManager::getSpotLightInnerCone(Instance i) const noexcept {
const auto& spotParams = getSpotParams(i);
float const cosOuter = std::cos(spotParams.outerClamped);
float const scale = spotParams.scaleOffset.x;
float const inner = std::acos((1.0f / scale) + cosOuter);
float cosOuter = std::cos(spotParams.outerClamped);
float scale = spotParams.scaleOffset.x;
float inner = std::acos((1.0f / scale) + cosOuter);
return inner;
}
@@ -426,7 +426,7 @@ void LightManager::ShadowCascades::computeUniformSplits(float splitPositions[3],
size_t s = 0;
cascades = max(cascades, (uint8_t) 4u);
for (size_t c = 1; c < cascades; c++) {
splitPositions[s++] = float(c) / float(cascades);
splitPositions[s++] = (float) c / cascades;
}
}
@@ -436,7 +436,7 @@ void LightManager::ShadowCascades::computeLogSplits(float splitPositions[3], uin
cascades = max(cascades, (uint8_t) 4u);
for (size_t c = 1; c < cascades; c++) {
splitPositions[s++] =
(near * std::pow(far / near, float(c) / float(cascades)) - near) / (far - near);
(near * std::pow(far / near, (float) c / cascades) - near) / (far - near);
}
}
+14 -14
View File
@@ -207,7 +207,7 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(size_t targetCo
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex,
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept {
std::vector<Entry>& entries = mImpl->mEntries;
if (primitiveIndex < entries.size()) {
@@ -277,8 +277,8 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
// this can't be an error because (1) those values are not immutable, so the caller
// could fix later, and (2) the material's shader will work (i.e. compile), and
// use the default values for this attribute, which maybe be acceptable.
AttributeBitset const declared = downcast(entry.vertices)->getDeclaredAttributes();
AttributeBitset const required = material->getRequiredAttributes();
AttributeBitset declared = downcast(entry.vertices)->getDeclaredAttributes();
AttributeBitset required = material->getRequiredAttributes();
if ((declared & required) != required) {
slog.w << "[entity=" << entity.getId() << ", primitive @ " << i
<< "] missing required attributes ("
@@ -301,7 +301,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
}
RenderableManager::Builder& RenderableManager::Builder::instances(size_t instanceCount) noexcept {
mImpl->mInstanceCount = clamp((unsigned int)instanceCount, 1u, 32767u);
mImpl->mInstanceCount = clamp((unsigned int)instanceCount, 1u, 65535u);
return *this;
}
@@ -326,7 +326,7 @@ void FRenderableManager::create(
if (UTILS_UNLIKELY(manager.hasComponent(entity))) {
destroy(entity);
}
Instance const ci = manager.addComponent(entity);
Instance ci = manager.addComponent(entity);
assert_invariant(ci);
if (ci) {
@@ -460,7 +460,7 @@ void FRenderableManager::create(
// this destroys a single component from an entity
void FRenderableManager::destroy(utils::Entity e) noexcept {
Instance const ci = getInstance(e);
Instance ci = getInstance(e);
if (ci) {
destroyComponent(ci);
mManager.removeComponent(e);
@@ -476,7 +476,7 @@ void FRenderableManager::terminate() noexcept {
<< " leaked Renderable components" << io::endl;
#endif
while (!manager.empty()) {
Instance const ci = manager.end() - 1;
Instance ci = manager.end() - 1;
destroyComponent(ci);
manager.removeComponent(manager.getEntity(ci));
}
@@ -521,7 +521,7 @@ void FRenderableManager::destroyComponentPrimitives(
delete[] primitives.data();
}
void FRenderableManager::destroyComponentMorphTargets(FEngine&,
void FRenderableManager::destroyComponentMorphTargets(FEngine& engine,
utils::Slice<MorphTargets>& morphTargets) noexcept {
delete[] morphTargets.data();
}
@@ -540,8 +540,8 @@ void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level,
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
primitives[primitiveIndex].setMaterialInstance(mi);
AttributeBitset const required = material->getRequiredAttributes();
AttributeBitset const declared = primitives[primitiveIndex].getEnabledAttributes();
AttributeBitset required = material->getRequiredAttributes();
AttributeBitset declared = primitives[primitiveIndex].getEnabledAttributes();
if (UTILS_UNLIKELY((declared & required) != required)) {
slog.w << "[instance=" << instance.asValue() << ", primitive @ " << primitiveIndex
<< "] missing required attributes ("
@@ -610,7 +610,7 @@ void FRenderableManager::setGeometryAt(Instance instance, uint8_t level, size_t
void FRenderableManager::setBones(Instance ci,
Bone const* UTILS_RESTRICT transforms, size_t boneCount, size_t offset) {
if (ci) {
Bones const& bones = mManager[ci].bones;
Bones& bones = mManager[ci].bones;
ASSERT_PRECONDITION(!bones.skinningBufferMode,
"Disable skinning buffer mode to use this API");
@@ -626,7 +626,7 @@ void FRenderableManager::setBones(Instance ci,
void FRenderableManager::setBones(Instance ci,
mat4f const* UTILS_RESTRICT transforms, size_t boneCount, size_t offset) {
if (ci) {
Bones const& bones = mManager[ci].bones;
Bones& bones = mManager[ci].bones;
ASSERT_PRECONDITION(!bones.skinningBufferMode,
"Disable skinning buffer mode to use this API");
@@ -688,7 +688,7 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights
"Only %d morph targets are supported (count=%d, offset=%d)",
CONFIG_MAX_MORPH_TARGET_COUNT, count, offset);
MorphWeights const& morphWeights = mManager[instance].morphWeights;
MorphWeights& morphWeights = mManager[instance].morphWeights;
if (morphWeights.handle) {
updateMorphWeights(mEngine, morphWeights.handle, weights, count, offset);
}
@@ -702,7 +702,7 @@ void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level
if (instance) {
assert_invariant(morphTargetBuffer);
MorphWeights const& morphWeights = mManager[instance].morphWeights;
MorphWeights& morphWeights = mManager[instance].morphWeights;
ASSERT_PRECONDITION(morphWeights.count == morphTargetBuffer->getCount(),
"Only %d morph targets can be set (count=%d)",
morphWeights.count, morphTargetBuffer->getCount());
+14 -7
View File
@@ -35,7 +35,6 @@
#include <cmath>
#include <cstdlib>
#include <tuple>
namespace filament {
@@ -572,15 +571,21 @@ static float3 luminanceScaling(float3 x,
// Quality
//------------------------------------------------------------------------------
static std::tuple<TextureFormat, PixelDataFormat, PixelDataType>
selectLutTextureParams(ColorGrading::LutFormat lutFormat) noexcept {
static void selectLutTextureParams(ColorGrading::LutFormat lutFormat,
TextureFormat& internalFormat, PixelDataFormat& format, PixelDataType& type) noexcept {
// We use RGBA16F for high quality modes instead of RGB16F because RGB16F
// is not supported everywhere
switch (lutFormat) {
case ColorGrading::LutFormat::INTEGER:
return { TextureFormat::RGB10_A2, PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV };
internalFormat = TextureFormat::RGB10_A2;
format = PixelDataFormat::RGBA;
type = PixelDataType::UINT_2_10_10_10_REV;
break;
case ColorGrading::LutFormat::FLOAT:
return { TextureFormat::RGBA16F, PixelDataFormat::RGBA, PixelDataType::HALF };
internalFormat = TextureFormat::RGBA16F;
format = PixelDataFormat::RGBA;
type = PixelDataType::HALF;
break;
}
}
@@ -665,8 +670,10 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
size_t elementSize = sizeof(half4);
void* data = malloc(lutElementCount * elementSize);
auto [textureFormat, format, type] = selectLutTextureParams(builder->format);
assert_invariant(FTexture::isTextureFormatSupported(engine, textureFormat));
TextureFormat textureFormat;
PixelDataFormat format;
PixelDataType type;
selectLutTextureParams(builder->format, textureFormat, format, type);
assert_invariant(FTexture::validatePixelFormatAndType(textureFormat, format, type));
void* converted = nullptr;
+29 -94
View File
@@ -32,11 +32,8 @@
#include <backend/Program.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <unordered_map>
namespace filament {
using namespace backend;
@@ -47,7 +44,7 @@ static MaterialParser* createParser(Backend backend, const void* data, size_t si
// unique_ptr so we don't leak MaterialParser on failures below
auto materialParser = std::make_unique<MaterialParser>(backend, data, size);
MaterialParser::ParseResult const materialResult = materialParser->parse();
MaterialParser::ParseResult materialResult = materialParser->parse();
if (backend == Backend::NOOP) {
return materialParser.release();
@@ -74,7 +71,6 @@ struct Material::BuilderDetails {
size_t mSize = 0;
MaterialParser* mMaterialParser = nullptr;
bool mDefaultMaterial = false;
std::unordered_map<std::string, std::variant<int32_t, float, bool>> mConstantSpecializations;
};
FMaterial::DefaultMaterialBuilder::DefaultMaterialBuilder() : Material::Builder() {
@@ -95,17 +91,6 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size)
return *this;
}
template<typename T, typename>
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
ASSERT_PRECONDITION(name != nullptr, "name cannot be null");
mImpl->mConstantSpecializations[{name, nameLength}] = value;
return *this;
}
template Material::Builder& Material::Builder::constant<int32_t>(const char*, size_t, int32_t);
template Material::Builder& Material::Builder::constant<float>(const char*, size_t, float);
template Material::Builder& Material::Builder::constant<bool>(const char*, size_t, bool);
Material* Material::Builder::build(Engine& engine) {
std::unique_ptr<MaterialParser> materialParser{ createParser(
downcast(engine).getBackend(), mImpl->mPayload, mImpl->mSize) };
@@ -119,7 +104,7 @@ Material* Material::Builder::build(Engine& engine) {
utils::bitset32 shaderModels;
shaderModels.setValue(v);
ShaderModel const shaderModel = downcast(engine).getShaderModel();
ShaderModel shaderModel = downcast(engine).getShaderModel();
if (!shaderModels.test(static_cast<uint32_t>(shaderModel))) {
CString name;
materialParser->getName(&name);
@@ -149,7 +134,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
MaterialParser* parser = builder->mMaterialParser;
mMaterialParser = parser;
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
UTILS_UNUSED_IN_RELEASE bool nameOk = parser->getName(&mName);
assert_invariant(nameOk);
uint8_t featureLevel = 1;
@@ -194,48 +179,6 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSubpassInfo.isValid = false;
}
utils::FixedCapacityVector<MaterialConstant> constants;
// Older materials won't have a constants chunk, but that's okay.
parser->getConstants(&constants);
// Verify that all the constant specializations exist in the material and that their types match.
// The first specialization constants are defined internally by Filament.
// The subsequent constants are user-defined in the material.
mSpecializationConstants.reserve(constants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
mSpecializationConstants.push_back({0, (int)mEngine.getSupportedFeatureLevel()});
mSpecializationConstants.push_back({1, (int)CONFIG_MAX_INSTANCES});
const bool staticTextureWorkaround =
mEngine.getDriverApi().isWorkaroundNeeded(Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR);
mSpecializationConstants.push_back({2, (bool)staticTextureWorkaround});
for (const auto& [name, value] : builder->mConstantSpecializations) {
auto found = std::find_if(
constants.begin(), constants.end(), [name = name](const auto& constant) {
return strncmp(constant.name.data(), name.data(), name.length()) == 0;
});
ASSERT_PRECONDITION(found != constants.end(),
"The material %s does not have a constant parameter named %s.", mName.c_str_safe(), name.c_str());
const char* const types[3] = {"an int", "a float", "a bool"};
const char* const errorMessage =
"The constant parameter %s on material %s is of type %s, but %s was "
"provided.";
switch (found->type) {
case ConstantType::INT:
ASSERT_PRECONDITION(std::holds_alternative<int32_t>(value), errorMessage,
name.c_str(), mName.c_str_safe(), "int", types[value.index()]);
break;
case ConstantType::FLOAT:
ASSERT_PRECONDITION(std::holds_alternative<float>(value), errorMessage,
name.c_str(), mName.c_str_safe(), "float", types[value.index()]);
break;
case ConstantType::BOOL:
ASSERT_PRECONDITION(std::holds_alternative<bool>(value), errorMessage,
name.c_str(), mName.c_str_safe(), "bool", types[value.index()]);
break;
}
uint32_t index = std::distance(constants.begin(), found) + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
mSpecializationConstants.push_back({index, value});
}
parser->getShading(&mShading);
parser->getMaterialProperties(&mMaterialProperties);
parser->getBlendingMode(&mBlendingMode);
@@ -338,28 +281,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
parser->hasCustomDepthShader(&mHasCustomDepthShader);
mIsDefaultMaterial = builder->mDefaultMaterial;
if (UTILS_UNLIKELY(mIsDefaultMaterial)) {
filaflat::MaterialChunk const& materialChunk{ mMaterialParser->getMaterialChunk() };
auto variants = FixedCapacityVector<Variant>::with_capacity(materialChunk.getShaderCount());
materialChunk.visitShaders([&variants](
ShaderModel model, Variant variant, ShaderStage stage) {
if (Variant::isValidDepthVariant(variant)) {
variants.push_back(variant);
}
});
std::sort(variants.begin(), variants.end(),
[](Variant lhs, Variant rhs) { return lhs.key < rhs.key; });
auto pos = std::unique(variants.begin(), variants.end());
variants.resize(std::distance(variants.begin(), pos));
std::swap(mDepthVariants, variants);
}
// pre-cache the shared variants -- these variants are shared with the default material.
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
auto& cachedPrograms = mCachedPrograms;
for (Variant variant : pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
for (Variant::type_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
const Variant variant(k);
if (Variant::isValidDepthVariant(variant)) {
FMaterial const* const pMaterial = engine.getDefaultMaterial();
pMaterial->prepareProgram(variant);
cachedPrograms[k] = pMaterial->getProgram(variant);
}
}
}
@@ -447,19 +378,19 @@ void FMaterial::getSurfaceProgramSlow(Variant variant) const noexcept {
assert_invariant(!Variant::isReserved(variant));
Variant const vertexVariant = Variant::filterVariantVertex(variant);
Variant const fragmentVariant = Variant::filterVariantFragment(variant);
Variant vertexVariant = Variant::filterVariantVertex(variant);
Variant fragmentVariant = Variant::filterVariantFragment(variant);
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) };
Program pb{ getProgramBuilderWithVariants(variant, vertexVariant, fragmentVariant) };
createAndCacheProgram(std::move(pb), variant);
}
void FMaterial::getPostProcessProgramSlow(Variant variant) const noexcept {
Program pb{ getProgramWithVariants(variant, variant, variant) };
Program pb{ getProgramBuilderWithVariants(variant, variant, variant) };
createAndCacheProgram(std::move(pb), variant);
}
Program FMaterial::getProgramWithVariants(
Program FMaterial::getProgramBuilderWithVariants(
Variant variant,
Variant vertexVariant,
Variant fragmentVariant) const noexcept {
@@ -472,7 +403,7 @@ Program FMaterial::getProgramWithVariants(
ShaderContent& vsBuilder = mEngine.getVertexShaderContent();
UTILS_UNUSED_IN_RELEASE bool const vsOK = mMaterialParser->getShader(vsBuilder, sm,
UTILS_UNUSED_IN_RELEASE bool vsOK = mMaterialParser->getShader(vsBuilder, sm,
vertexVariant, ShaderStage::VERTEX);
ASSERT_POSTCONDITION(isNoop || (vsOK && !vsBuilder.empty()),
@@ -486,7 +417,7 @@ Program FMaterial::getProgramWithVariants(
ShaderContent& fsBuilder = mEngine.getFragmentShaderContent();
UTILS_UNUSED_IN_RELEASE bool const fsOK = mMaterialParser->getShader(fsBuilder, sm,
UTILS_UNUSED_IN_RELEASE bool fsOK = mMaterialParser->getShader(fsBuilder, sm,
fragmentVariant, ShaderStage::FRAGMENT);
ASSERT_POSTCONDITION(isNoop || (fsOK && !fsBuilder.empty()),
@@ -506,12 +437,12 @@ Program FMaterial::getProgramWithVariants(
UTILS_NOUNROLL
for (size_t i = 0; i < Enum::count<SamplerBindingPoints>(); i++) {
SamplerBindingPoints const bindingPoint = (SamplerBindingPoints)i;
SamplerBindingPoints bindingPoint = (SamplerBindingPoints)i;
auto const& info = mSamplerGroupBindingInfoList[i];
if (info.count) {
std::array<Program::Sampler, backend::MAX_SAMPLER_COUNT> samplers{};
for (size_t j = 0, c = info.count; j < c; ++j) {
uint8_t const binding = info.bindingOffset + j;
uint8_t binding = info.bindingOffset + j;
samplers[j] = { mSamplerBindingToNameMap[binding], binding };
}
program.setSamplerGroup(+bindingPoint, info.shaderStageFlags,
@@ -519,7 +450,10 @@ Program FMaterial::getProgramWithVariants(
}
}
program.specializationConstants(mSpecializationConstants);
program.specializationConstants({
{ 0, (int)mEngine.getSupportedFeatureLevel() },
{ 1, (int)CONFIG_MAX_INSTANCES }
});
return program;
}
@@ -535,7 +469,7 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
const auto& uniforms = mUniformInterfaceBlock.getFieldInfoList();
size_t i = 0;
size_t const uniformCount = std::min(count, size_t(uniforms.size()));
size_t uniformCount = std::min(count, size_t(uniforms.size()));
for ( ; i < uniformCount; i++) {
ParameterInfo& info = parameters[i];
const auto& uniformInfo = uniforms[i];
@@ -548,7 +482,7 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
}
const auto& samplers = mSamplerInterfaceBlock.getSamplerInfoList();
size_t const samplerCount = samplers.size();
size_t samplerCount = samplers.size();
for (size_t j = 0; i < count && j < samplerCount; i++, j++) {
ParameterInfo& info = parameters[i];
const auto& samplerInfo = samplers[j];
@@ -568,13 +502,12 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
info.subpassType = mSubpassInfo.type;
info.count = 1;
info.precision = mSubpassInfo.precision;
i++;
}
return count;
}
#if FILAMENT_ENABLE_MATDBG
// Swaps in an edited version of the original package that was used to create the material. The
// edited package was stashed in response to a debugger event. This is invoked only when the
// Material Debugger is attached. The only editable features of a material package are the shader
@@ -598,6 +531,8 @@ void FMaterial::applyPendingEdits() noexcept {
* @{
*/
#if FILAMENT_ENABLE_MATDBG
void FMaterial::onEditCallback(void* userdata, const utils::CString&, const void* packageData,
size_t packageSize) {
FMaterial* material = downcast((Material*) userdata);
+6 -10
View File
@@ -27,7 +27,6 @@
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <private/filament/Variant.h>
#include <private/filament/ConstantInfo.h>
#include <utils/compiler.h>
#include <utils/Mutex.h>
@@ -100,6 +99,9 @@ public:
return mCachedPrograms[variant.key];
}
backend::Program getProgramBuilderWithVariants(Variant variant, Variant vertexVariant,
Variant fragmentVariant) const noexcept;
bool isVariantLit() const noexcept { return mIsVariantLit; }
const utils::CString& getName() const noexcept { return mName; }
@@ -147,11 +149,11 @@ public:
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
void applyPendingEdits() noexcept;
void destroyPrograms(FEngine& engine);
#if FILAMENT_ENABLE_MATDBG
void applyPendingEdits() noexcept;
/**
* Callback handlers for the debug server, potentially called from any thread. The userdata
* argument has the same value that was passed to DebugServer::addMaterial(), which should
@@ -185,9 +187,6 @@ private:
void prepareProgramSlow(Variant variant) const noexcept;
void getSurfaceProgramSlow(Variant variant) const noexcept;
void getPostProcessProgramSlow(Variant variant) const noexcept;
backend::Program getProgramWithVariants(Variant variant, Variant vertexVariant,
Variant fragmentVariant) const noexcept;
void createAndCacheProgram(backend::Program&& p,
Variant variant) const noexcept;
@@ -229,17 +228,13 @@ private:
BufferInterfaceBlock mUniformInterfaceBlock;
SubpassInfo mSubpassInfo;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mUniformBlockBindings;
utils::FixedCapacityVector<Variant> mDepthVariants; // only populated with default material
SamplerGroupBindingInfoList mSamplerGroupBindingInfoList;
SamplerBindingToNameMap mSamplerBindingToNameMap;
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
#if FILAMENT_ENABLE_MATDBG
matdbg::MaterialKey mDebuggerId;
mutable utils::Mutex mActiveProgramsLock;
mutable VariantList mActivePrograms;
std::atomic<MaterialParser*> mPendingEdits = {};
#endif
utils::CString mName;
@@ -247,6 +242,7 @@ private:
const uint32_t mMaterialId;
mutable uint32_t mMaterialInstanceId = 0;
MaterialParser* mMaterialParser = nullptr;
std::atomic<MaterialParser*> mPendingEdits = {};
};
+3 -3
View File
@@ -173,7 +173,7 @@ void FMaterialInstance::commitSlow(DriverApi& driver) const {
void FMaterialInstance::setParameter(std::string_view name,
backend::Handle<backend::HwTexture> texture, backend::SamplerParams params) noexcept {
size_t const index = mMaterial->getSamplerInterfaceBlock().getSamplerInfo(name)->offset;
size_t index = mMaterial->getSamplerInterfaceBlock().getSamplerInfo(name)->offset;
mSamplers.setSampler(index, { texture, params });
}
@@ -184,8 +184,8 @@ void FMaterialInstance::setParameterImpl(std::string_view name,
// Per GLES3.x specification, depth texture can't be filtered unless in compare mode.
if (texture && isDepthFormat(texture->getFormat())) {
if (sampler.getCompareMode() == SamplerCompareMode::NONE) {
SamplerMinFilter const minFilter = sampler.getMinFilter();
SamplerMagFilter const magFilter = sampler.getMagFilter();
SamplerMinFilter minFilter = sampler.getMinFilter();
SamplerMagFilter magFilter = sampler.getMagFilter();
if (magFilter == SamplerMagFilter::LINEAR ||
minFilter == SamplerMinFilter::LINEAR ||
minFilter == SamplerMinFilter::LINEAR_MIPMAP_LINEAR ||
+1 -1
View File
@@ -220,7 +220,7 @@ void FMorphTargetBuffer::updateDataAt(backend::DriverApi& driver,
// 'out' buffer is going to be used up to 3 times, so for simplicity we use a shared_buffer
// to manage its lifetime. One side effect of this is that the callbacks below will allocate
// a small object on the heap.
std::shared_ptr<void> const allocation((void*)out, ::free);
std::shared_ptr<void> allocation((void*)out, ::free);
// Note: because the texture width is up to 2048, we're expecting that most of the time
// only a single texture update call will be necessary (i.e. that there are no more
+10 -8
View File
@@ -610,6 +610,14 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
view.prepareUpscaler(scale);
// start froxelization immediately, it has no dependencies
JobSystem::Job* jobFroxelize = nullptr;
if (view.hasDynamicLighting()) {
jobFroxelize = js.runAndRetain(js.createJob(nullptr,
[&engine, &view, &viewMatrix = cameraInfo.view](JobSystem&, JobSystem::Job*) {
view.froxelize(engine, viewMatrix); }));
}
/*
* Allocate command buffer
*/
@@ -887,8 +895,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// We use a framegraph pass to wait for froxelization to finish (so it can be done
// in parallel with .compile()
auto sync = view.getFroxelizerSync();
if (sync) {
if (jobFroxelize) {
auto *sync = jobFroxelize;
js.waitAndRelease(sync);
view.commitFroxels(driver);
}
@@ -980,12 +988,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
auto const depth = ppm.resolveBaseLevel(fg, "Resolved Depth Buffer",
blackboard.get<FrameGraphTexture>("depth"));
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
view.hasShadowing() && view.hasDirectionalLight())) {
input = ppm.debugShadowCascades(fg, input, depth);
}
// TODO: DoF should be applied here, before TAA -- but if we do this it'll result in a lot of
// fireflies due to the instability of the highlights. This can be fixed with a
// dedicated TAA pass for the DoF, as explained in
+1 -1
View File
@@ -94,7 +94,7 @@ FSkybox::FSkybox(FEngine& engine, const Builder& builder) noexcept
FMaterial const* material = engine.getSkyboxMaterial();
mSkyboxMaterialInstance = material->createInstance("Skybox");
TextureSampler const sampler(TextureSampler::MagFilter::LINEAR, TextureSampler::WrapMode::REPEAT);
TextureSampler sampler(TextureSampler::MagFilter::LINEAR, TextureSampler::WrapMode::REPEAT);
auto *pInstance = static_cast<MaterialInstance*>(mSkyboxMaterialInstance);
FTexture const* texture = mSkyboxTexture ? mSkyboxTexture : engine.getDummyCubemap();
pInstance->setParameter("skybox", texture, sampler);
+13 -19
View File
@@ -222,9 +222,6 @@ void FTexture::setImage(FEngine& engine, size_t level,
}
};
// this should have been validated already
assert_invariant(isTextureFormatSupported(engine, mFormat));
ASSERT_PRECONDITION(buffer.type == PixelDataType::COMPRESSED ||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
"The combination of internal format=%u and {format=%u, type=%u} is not supported.",
@@ -298,9 +295,6 @@ void FTexture::setImage(FEngine& engine, size_t level,
}
};
// this should have been validated already
assert_invariant(isTextureFormatSupported(engine, mFormat));
ASSERT_PRECONDITION(buffer.type == PixelDataType::COMPRESSED ||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
"The combination of internal format=%u and {format=%u, type=%u} is not supported.",
@@ -407,8 +401,8 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept {
// Perform a blit for all miplevels down to 1x1.
backend::Handle<backend::HwRenderTarget> dstrth;
do {
uint32_t const dstw = std::max(srcw >> 1u, 1u);
uint32_t const dsth = std::max(srch >> 1u, 1u);
uint32_t dstw = std::max(srcw >> 1u, 1u);
uint32_t dsth = std::max(srch >> 1u, 1u);
proto.level = level++;
dstrth = driver.createRenderTarget(
TargetBufferFlags::COLOR, dstw, dsth, mSampleCount, proto, {}, {});
@@ -506,7 +500,7 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
"reflections texture cannot be compressed");
PrefilterOptions const defaultOptions;
PrefilterOptions defaultOptions;
options = options ? options : &defaultOptions;
JobSystem& js = engine.getJobSystem();
@@ -564,7 +558,7 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
Image temp;
Cubemap cml = CubemapUtils::create(temp, size);
for (size_t j = 0; j < 6; j++) {
Cubemap::Face const face = (Cubemap::Face)j;
Cubemap::Face face = (Cubemap::Face)j;
Image const& image = cml.getImageForFace(face);
for (size_t y = 0; y < size; y++) {
Cubemap::Texel* out = (Cubemap::Texel*)image.getPixelRef(0, y);
@@ -590,10 +584,10 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
for (size_t x = 0; x < size; x++, out++, src++) {
using fp10 = fp<0, 5, 5>;
using fp11 = fp<0, 5, 6>;
fp11 const r{ uint16_t( *src & 0x7FFu) };
fp11 const g{ uint16_t((*src >> 11u) & 0x7FFu) };
fp10 const b{ uint16_t((*src >> 22u) & 0x3FFu) };
Cubemap::Texel const texel{ fp11::tof(r), fp11::tof(g), fp10::tof(b) };
fp11 r{ uint16_t( *src & 0x7FFu) };
fp11 g{ uint16_t((*src >> 11u) & 0x7FFu) };
fp10 b{ uint16_t((*src >> 22u) & 0x3FFu) };
Cubemap::Texel texel{ fp11::tof(r), fp11::tof(g), fp10::tof(b) };
Cubemap::writeAt(out, texel);
}
}
@@ -620,12 +614,12 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
// Finally generate each pre-filtered mipmap level
const size_t baseExp = ctz(size);
size_t const numSamples = options->sampleCount;
size_t numSamples = options->sampleCount;
const size_t numLevels = baseExp + 1;
for (ssize_t i = (ssize_t)baseExp; i >= 0; --i) {
for (ssize_t i = baseExp; i >= 0; --i) {
const size_t dim = 1U << i;
const size_t level = baseExp - i;
const float lod = saturate(float(level) / float(numLevels - 1));
const float lod = saturate(level / (numLevels - 1.0f));
const float linearRoughness = lod * lod;
Image image;
@@ -633,11 +627,11 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
CubemapIBL::roughnessFilter(js, dst, { levels.begin(), uint32_t(levels.size()) },
linearRoughness, numSamples, mirror, true);
Texture::PixelBufferDescriptor const pbd(image.getData(), image.getSize(),
Texture::PixelBufferDescriptor pbd(image.getData(), image.getSize(),
Texture::PixelBufferDescriptor::PixelDataFormat::RGB,
Texture::PixelBufferDescriptor::PixelDataType::FLOAT, 1, 0, 0, image.getStride());
uintptr_t const base = uintptr_t(image.getData());
uintptr_t base = uintptr_t(image.getData());
for (size_t j = 0; j < 6; j++) {
Image const& faceImage = dst.getImageForFace((Cubemap::Face)j);
auto offset = uintptr_t(faceImage.getData()) - base;
+4 -4
View File
@@ -67,7 +67,7 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut
uint32_t byteOffset,
uint8_t byteStride) noexcept {
size_t const attributeSize = Driver::getElementTypeSize(attributeType);
size_t attributeSize = Driver::getElementTypeSize(attributeType);
if (byteStride == 0) {
byteStride = (uint8_t)attributeSize;
}
@@ -143,7 +143,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin());
mDeclaredAttributes = builder->mDeclaredAttributes;
uint8_t const attributeCount = (uint8_t)mDeclaredAttributes.count();
uint8_t attributeCount = (uint8_t) mDeclaredAttributes.count();
AttributeArray attributeArray;
@@ -190,7 +190,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
#pragma nounroll
for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) {
if (bufferSizes[i] > 0) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
@@ -202,7 +202,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
void FVertexBuffer::terminate(FEngine& engine) {
FEngine::DriverApi& driver = engine.getDriverApi();
if (!mBufferObjectsEnabled) {
for (BufferObjectHandle const bo : mBufferObjects) {
for (BufferObjectHandle bo : mBufferObjects) {
driver.destroyBufferObject(bo);
}
}
+35 -59
View File
@@ -32,6 +32,7 @@
#include <filament/TextureSampler.h>
#include <filament/View.h>
#include <private/filament/SibStructs.h>
#include <private/filament/UibStructs.h>
#include <utils/Profiler.h>
@@ -179,8 +180,8 @@ float2 FView::updateScale(FEngine& engine,
const float dt = 1.0f; // we don't really need dt here, setting it to 1, means our parameters are in "frames"
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
float const measured = duration<float, std::milli>{ info.denoisedFrameTime }.count();
float const out = mPidController.update(measured / targetWithHeadroom, 1.0f, dt);
float measured = duration<float, std::milli>{ info.denoisedFrameTime }.count();
float out = mPidController.update(measured / targetWithHeadroom, 1.0f, dt);
// maps pid command to a scale (absolute or relative, see below)
const float command = out < 0.0f ? (1.0f / (1.0f - out)) : (1.0f + out);
@@ -268,7 +269,7 @@ bool FView::isSkyboxVisible() const noexcept {
}
void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, CameraInfo const& cameraInfo) noexcept {
FScene::LightSoa& lightData, CameraInfo const& cameraInfo) noexcept {
SYSTRACE_CALL();
mHasShadowing = false;
@@ -302,7 +303,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
// when we get here all the lights should be visible
assert_invariant(lightData.elementAt<FScene::VISIBILITY>(l));
FLightManager::Instance const li = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
if (UTILS_LIKELY(!li)) {
continue; // invalid instance
@@ -333,8 +334,8 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
CameraInfo const& cameraInfo) noexcept {
void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena,
filament::Viewport const& viewport, CameraInfo const& cameraInfo) noexcept {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
@@ -345,8 +346,15 @@ void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
* Dynamic lights
*/
if (hasDynamicLighting()) {
mHasDynamicLighting = scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT;
if (mHasDynamicLighting) {
scene->prepareDynamicLights(cameraInfo, arena, mLightUbh);
Froxelizer& froxelizer = mFroxelizer;
if (froxelizer.prepare(driver, arena, viewport,
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
// update our uniform buffer if needed
mPerViewUniforms.prepareDynamicLights(mFroxelizer);
}
}
// here the array of visible lights has been shrunk to CONFIG_MAX_LIGHT_COUNT
@@ -357,6 +365,7 @@ void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
*/
const float exposure = Exposure::exposure(cameraInfo.ev100);
mPerViewUniforms.prepareExposure(cameraInfo.ev100);
/*
@@ -374,6 +383,7 @@ void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
FSkybox const* const skybox = scene->getSkybox();
intensity = skybox ? skybox->getIntensity() : FIndirectLight::DEFAULT_INTENSITY;
}
mPerViewUniforms.prepareAmbientLight(engine, *ibl, intensity, exposure);
/*
@@ -383,6 +393,7 @@ void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
FLightManager::Instance const directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
const float3 sceneSpaceDirection = lightData.elementAt<FScene::DIRECTION>(0); // guaranteed normalized
mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight);
mHasDirectionalLight = directionalLight.isValid();
}
CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
@@ -418,7 +429,7 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
}
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
filament::Viewport viewport, CameraInfo cameraInfo,
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
float4 const& userTime, bool needsAlphaChannel) noexcept {
SYSTRACE_CALL();
@@ -461,23 +472,15 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Light culling: runs in parallel with Renderable culling (below)
*/
JobSystem::Job* froxelizeLightsJob = nullptr;
JobSystem::Job* prepareVisibleLightsJob = nullptr;
if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) {
// create and start the prepareVisibleLights job
// note: this job updates LightData (non const)
prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
[&engine, &arena, &viewMatrix = cameraInfo.view, &cullingFrustum,
&lightData = scene->getLightData()]
(JobSystem&, JobSystem::Job*) {
[&cullingFrustum, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) {
FView::prepareVisibleLights(engine.getLightManager(), arena,
viewMatrix, cullingFrustum, lightData);
cameraInfo.view, cullingFrustum, scene->getLightData());
}));
}
// this is used later (in Renderer.cpp) to wait for froxelization to finishes
setFroxelizerSync(froxelizeLightsJob);
Range merged;
FScene::RenderableSoa& renderableData = scene->getRenderableData();
@@ -503,40 +506,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
if (prepareVisibleLightsJob) {
js.waitAndRelease(prepareVisibleLightsJob);
}
// lightData is const from this point on (can only happen after prepareVisibleLightsJob)
auto const& lightData = scene->getLightData();
// now we know if we have dynamic lighting (i.e.: dynamic lights are visible)
mHasDynamicLighting = lightData.size() > FScene::DIRECTIONAL_LIGHTS_COUNT;
// we also know if we have a directional light
FLightManager::Instance const directionalLight =
lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
mHasDirectionalLight = directionalLight.isValid();
// As soon as prepareVisibleLight finishes, we can kick-off the froxelization
if (hasDynamicLighting()) {
auto& froxelizer = mFroxelizer;
if (froxelizer.prepare(driver, arena, viewport,
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
// TODO: might be more consistent to do this in prepareLighting(), but it's not
// strictly necessary
mPerViewUniforms.prepareDynamicLights(mFroxelizer);
}
// We need to pass viewMatrix by value here because it extends the scope of this
// function.
std::function<void(JobSystem&, JobSystem::Job*)> froxelizerWork =
[&froxelizer = mFroxelizer, &engine, viewMatrix = cameraInfo.view, &lightData]
(JobSystem&, JobSystem::Job*) {
froxelizer.froxelizeLights(engine, viewMatrix, lightData);
};
froxelizeLightsJob = js.runAndRetain(js.createJob(nullptr, std::move(froxelizerWork)));
}
setFroxelizerSync(froxelizeLightsJob);
prepareShadowing(engine, renderableData, lightData, cameraInfo);
prepareShadowing(engine, renderableData, scene->getLightData(), cameraInfo);
/*
* Partition the SoA so that renderables are partitioned w.r.t their visibility into the
@@ -633,7 +603,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Relies on FScene::prepare() and prepareVisibleLights()
*/
prepareLighting(engine, arena, cameraInfo);
prepareLighting(engine, driver, arena, viewport, cameraInfo);
/*
* Update driver state
@@ -764,6 +734,12 @@ void FView::cleanupRenderPasses() const noexcept {
mPerViewUniforms.unbindSamplers();
}
void FView::froxelize(FEngine& engine, mat4f const& viewMatrix) const noexcept {
SYSTRACE_CALL();
assert_invariant(mHasDynamicLighting);
mFroxelizer.froxelizeLights(engine, viewMatrix, mScene->getLightData());
}
void FView::commitUniforms(DriverApi& driver) const noexcept {
mPerViewUniforms.commit(driver);
}
@@ -786,7 +762,7 @@ void FView::prepareVisibleRenderables(JobSystem& js,
}
}
void FView::cullRenderables(JobSystem&,
void FView::cullRenderables(JobSystem& js,
FScene::RenderableSoa& renderableData, Frustum const& frustum, size_t bit) noexcept {
SYSTRACE_CALL();
@@ -830,7 +806,7 @@ void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena
size_t visibleLightCount = FScene::DIRECTIONAL_LIGHTS_COUNT;
// skip directional light
for (size_t i = FScene::DIRECTIONAL_LIGHTS_COUNT; i < lightData.size(); i++) {
FLightManager::Instance const li = instanceArray[i];
FLightManager::Instance li = instanceArray[i];
if (visibleArray[i]) {
if (!lcm.isLightCaster(li)) {
visibleArray[i] = 0;
@@ -926,8 +902,8 @@ inline void FView::computeLightCameraDistances(
void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
FScene::RenderableSoa& renderableData, Range visible) noexcept {
FRenderableManager const& rcm = engine.getRenderableManager();
for (uint32_t const index : visible) {
uint8_t const level = 0; // TODO: pick the proper level of detail
for (uint32_t index : visible) {
uint8_t level = 0; // TODO: pick the proper level of detail
auto ri = renderableData.elementAt<FScene::RENDERABLE_INSTANCE>(index);
renderableData.elementAt<FScene::PRIMITIVES>(index) = rcm.getRenderPrimitives(ri, level);
}
@@ -968,10 +944,10 @@ void FView::executePickingQueries(backend::DriverApi& driver,
const uint32_t x = uint32_t(float(pQuery->x) * (scale * mScale.x));
const uint32_t y = uint32_t(float(pQuery->y) * (scale * mScale.y));
driver.readPixels(handle, x, y, 1, 1, {
&pQuery->result.renderable, 4 * 4, // 4*uint
&pQuery->result.renderable, 4*4, // 4*uint
// FIXME: RGBA_INTEGER is guaranteed to work. R_INTEGER must be queried.
backend::PixelDataFormat::RG_INTEGER, backend::PixelDataType::UINT,
pQuery->handler, [](void*, size_t, void* user) {
pQuery->handler, [](void* buffer, size_t size, void* user) {
FPickingQuery* pQuery = static_cast<FPickingQuery*>(user);
pQuery->result.fragCoords = {
pQuery->x, pQuery->y,float(1.0 - pQuery->result.depth) };
+5 -9
View File
@@ -88,10 +88,8 @@ public:
CameraInfo computeCameraInfo(FEngine& engine) const noexcept;
// note: viewport/cameraInfo are passed by value to make it clear that prepare cannot
// keep references on them that would outlive the scope of prepare() (e.g. with JobSystem).
void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena,
filament::Viewport viewport, CameraInfo cameraInfo,
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
math::float4 const& userTime, bool needsAlphaChannel) noexcept;
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept;
@@ -143,8 +141,9 @@ public:
const filament::Viewport& logicalViewport) const noexcept;
void prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, CameraInfo const& cameraInfo) noexcept;
void prepareLighting(FEngine& engine, ArenaScope& arena, CameraInfo const& cameraInfo) noexcept;
FScene::LightSoa& lightData, CameraInfo const& cameraInfo) noexcept;
void prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena,
filament::Viewport const& viewport, CameraInfo const &cameraInfo) noexcept;
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset,
@@ -154,12 +153,10 @@ public:
void prepareShadowMapping(bool highPrecision) const noexcept;
void cleanupRenderPasses() const noexcept;
void froxelize(FEngine& engine, math::mat4f const& viewMatrix) const noexcept;
void commitUniforms(backend::DriverApi& driver) const noexcept;
void commitFroxels(backend::DriverApi& driverApi) const noexcept;
utils::JobSystem::Job* getFroxelizerSync() const noexcept { return mFroxelizerSync; }
void setFroxelizerSync(utils::JobSystem::Job* sync) noexcept { mFroxelizerSync = sync; }
bool hasDirectionalLight() const noexcept { return mHasDirectionalLight; }
bool hasDynamicLighting() const noexcept { return mHasDynamicLighting; }
bool hasShadowing() const noexcept { return mHasShadowing; }
@@ -477,7 +474,6 @@ private:
FCamera* mViewingCamera = nullptr;
mutable Froxelizer mFroxelizer;
utils::JobSystem::Job* mFroxelizerSync = nullptr;
Viewport mViewport;
bool mCulling = true;
+11 -11
View File
@@ -51,7 +51,7 @@ uint32_t FrameGraph::Builder::declareRenderPass(const char* name,
FrameGraphId<FrameGraphTexture> FrameGraph::Builder::declareRenderPass(
FrameGraphId<FrameGraphTexture> color, uint32_t* index) {
color = write(color);
uint32_t const id = declareRenderPass(getName(color),
uint32_t id = declareRenderPass(getName(color),
{ .attachments = { .color = { color }}});
if (index) *index = id;
return color;
@@ -238,10 +238,10 @@ FrameGraphHandle FrameGraph::createNewVersion(FrameGraphHandle handle) noexcept
assert_invariant(handle);
ResourceNode* const node = getActiveResourceNode(handle);
assert_invariant(node);
FrameGraphHandle const parent = node->getParentHandle();
FrameGraphHandle parent = node->getParentHandle();
ResourceSlot& slot = getResourceSlot(handle);
slot.version = ++handle.version; // increase the parent's version
slot.nid = (ResourceSlot::Index)mResourceNodes.size(); // create the new parent node
slot.nid = mResourceNodes.size(); // create the new parent node
ResourceNode* newNode = mArena.make<ResourceNode>(*this, handle, parent);
mResourceNodes.push_back(newNode);
return handle;
@@ -253,7 +253,7 @@ ResourceNode* FrameGraph::createNewVersionForSubresourceIfNeeded(ResourceNode* n
// if we don't already have a new ResourceNode for this resource, create one.
// we keep the old ResourceNode index, so we can direct all the reads to it.
slot.sid = slot.nid; // record the current ResourceNode of the parent
slot.nid = (ResourceSlot::Index)mResourceNodes.size(); // create the new parent node
slot.nid = mResourceNodes.size(); // create the new parent node
node = mArena.make<ResourceNode>(*this, node->resourceHandle, node->getParentHandle());
mResourceNodes.push_back(node);
}
@@ -266,10 +266,10 @@ FrameGraphHandle FrameGraph::addResourceInternal(VirtualResource* resource) noex
FrameGraphHandle FrameGraph::addSubResourceInternal(FrameGraphHandle parent,
VirtualResource* resource) noexcept {
FrameGraphHandle const handle(mResourceSlots.size());
FrameGraphHandle handle(mResourceSlots.size());
ResourceSlot& slot = mResourceSlots.emplace_back();
slot.rid = (ResourceSlot::Index)mResources.size();
slot.nid = (ResourceSlot::Index)mResourceNodes.size();
slot.rid = mResources.size();
slot.nid = mResourceNodes.size();
mResources.push_back(resource);
ResourceNode* pNode = mArena.make<ResourceNode>(*this, handle, parent);
mResourceNodes.push_back(pNode);
@@ -285,7 +285,7 @@ FrameGraphHandle FrameGraph::readInternal(FrameGraphHandle handle, PassNode* pas
ResourceNode* const node = getActiveResourceNode(handle);
// Check preconditions
bool const passAlreadyAWriter = node->hasWriteFrom(passNode);
bool passAlreadyAWriter = node->hasWriteFrom(passNode);
ASSERT_PRECONDITION(!passAlreadyAWriter,
"Pass \"%s\" already writes to \"%s\"",
passNode->getName(), node->getName());
@@ -303,7 +303,7 @@ FrameGraphHandle FrameGraph::readInternal(FrameGraphHandle handle, PassNode* pas
// node to the subresource -- but we may have two parent nodes, one for reads and
// one for writes, so we need to use the one for reads.
auto* parentNode = node->getParentNode();
ResourceSlot const& slot = getResourceSlot(parentNode->resourceHandle);
ResourceSlot& slot = getResourceSlot(parentNode->resourceHandle);
if (slot.sid >= 0) {
// we have a parent's node for reads, use that one
parentNode = mResourceNodes[slot.sid];
@@ -314,7 +314,7 @@ FrameGraphHandle FrameGraph::readInternal(FrameGraphHandle handle, PassNode* pas
// resource is a parent of some subresource, and it might exist as a version for
// writing, in this case we need to add a dependency from its "read" version to
// itself.
ResourceSlot const& slot = getResourceSlot(handle);
ResourceSlot& slot = getResourceSlot(handle);
if (slot.sid >= 0) {
node->setParentReadDependency(mResourceNodes[slot.sid]);
}
@@ -440,7 +440,7 @@ bool FrameGraph::isValid(FrameGraphHandle handle) const {
if (!handle.isInitialized()) {
return false;
}
ResourceSlot const& slot = getResourceSlot(handle);
ResourceSlot slot = getResourceSlot(handle);
if (handle.version != slot.version) {
return false;
}
+9 -9
View File
@@ -76,13 +76,13 @@ void RenderPassNode::execute(FrameGraphResources const& resources, DriverApi& dr
}
}
uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder&,
uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder& builder,
const char* name, FrameGraphRenderPass::Descriptor const& descriptor) {
RenderPassData data;
data.name = name;
data.descriptor = descriptor;
FrameGraphRenderPass::Attachments const& attachments = data.descriptor.attachments;
FrameGraphRenderPass::Attachments& attachments = data.descriptor.attachments;
// retrieve the ResourceNode of the attachments coming to us -- this will be used later
// to compute the discard flags.
@@ -118,7 +118,7 @@ uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder
}
}
uint32_t const id = mRenderTargetData.size();
uint32_t id = mRenderTargetData.size();
mRenderTargetData.push_back(data);
return id;
}
@@ -201,8 +201,8 @@ void RenderPassNode::resolve() noexcept {
// of all attachments size matches there are no ambiguity about the RT size.
// if they don't match however, we select a size that will accommodate all attachments.
uint32_t const width = maxWidth;
uint32_t const height = maxHeight;
uint32_t width = maxWidth;
uint32_t height = maxHeight;
// Update the descriptor if no size was specified (auto mode)
if (!rt.descriptor.viewport.width) {
@@ -277,7 +277,7 @@ void RenderPassNode::RenderPassData::devirtualize(FrameGraph& fg,
}
void RenderPassNode::RenderPassData::destroy(
ResourceAllocatorInterface& resourceAllocator) const noexcept {
ResourceAllocatorInterface& resourceAllocator) noexcept {
if (UTILS_LIKELY(!imported)) {
resourceAllocator.destroyRenderTarget(backend.target);
}
@@ -291,9 +291,9 @@ utils::CString RenderPassNode::graphvizify() const noexcept {
#ifndef NDEBUG
std::string s;
uint32_t const id = getId();
uint32_t id = getId();
const char* const nodeName = getName();
uint32_t const refCount = getRefCount();
uint32_t refCount = getRefCount();
s.append("[label=\"");
s.append(nodeName);
@@ -339,7 +339,7 @@ utils::CString PresentPassNode::graphvizify() const noexcept {
#ifndef NDEBUG
std::string s;
s.reserve(128);
uint32_t const id = getId();
uint32_t id = getId();
s.append("[label=\"Present , id: ");
s.append(std::to_string(id));
s.append("\", style=filled, fillcolor=red3]");

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